Skip to content

Latest commit

 

History

History
1381 lines (1048 loc) · 44.7 KB

File metadata and controls

1381 lines (1048 loc) · 44.7 KB

GEMINI.md - aix Project Context

Project Overview

aix is a unified configuration manager for AI agent editors (e.g., Claude Code, Cursor, Copilot, Zed). It provides a single source of truth via an ai.json file to manage:

  • MCP Servers: Model Context Protocol servers for extending agent capabilities.
  • Skills: Reusable agent behaviors and tools.
  • Rules: System instructions and coding standards.
  • Prompts: Workflow definitions and slash commands.

The project is structured as a TypeScript monorepo using npm workspaces.

Architecture

  • packages/cli: The oclif-based CLI tool (@a1st/aix). Handles user commands, interactive prompts (using Ink/React), and editor installations.
  • packages/core: Core logic for configuration discovery, loading, inheritance (extends), merging, and filesystem operations.
  • packages/schema: Centralized Zod schemas defining the structure of ai.json and internal types.
  • packages/mcp-registry-client: Client for fetching metadata from the official MCP registry.
  • packages/site: (TBD) Documentation or landing site.

Tech Stack

  • Language: TypeScript (ESM)
  • Frameworks: oclif (CLI), Ink (CLI UI), React
  • Validation: Zod
  • Testing: Vitest
  • Linting/Formatting: oxlint, oxfmt
  • Build Tool: tsc (TypeScript Compiler)

Key Commands

Root Development

  • npm run build: Build all packages in the monorepo.
  • npm run test: Run all tests across workspaces.
  • npm run standards: Run lint, format check, and typecheck.
  • npm run lint: Run oxlint for fast linting.
  • npm run format: Format code using oxfmt.
  • npm run typecheck: Run tsc --noEmit to verify types.

CLI Usage (Internal)

  • npm run dev -w @a1st/aix: Run the CLI in development mode using ts-node.

Development Conventions

  • ESM First: All packages use "type": "module".
  • Schema-Driven: All configuration changes must adhere to the schemas in packages/schema.
  • Testing: New features or bug fixes should include tests in the relevant __tests__ directory.
  • Fast Linting: Use oxlint for linting. Configuration is in .oxlintrc.json.
  • Formatting: Use oxfmt. Configuration is in .oxfmtrc.json.
  • Atomic Writes: Core logic should prioritize atomic file writes with backups (handled by packages/core).
  • Releases: Releases are triggered by pushing a signed git tag (e.g., git tag -s vX.Y.Z -m "vX.Y.Z"). Use npm run version:bump to update versions across the monorepo before tagging.

Key Files

  • ai.json: The primary configuration file for a workspace.
  • package.json: Monorepo root configuration and shared scripts.
  • packages/schema/src/config.ts: Defines the main AiJsonConfig schema.
  • packages/cli/src/commands/: Implementation of CLI commands (init, add, install, etc.).
  • packages/core/src/loader.ts: Logic for loading and merging ai.json files.

general

Core Principles

  • Readability and clarity over brevity
  • Follow existing patterns in the codebase before inventing new ones
  • Do deep research to find existing libraries (NPM, GitHub, Cargo, etc.) that solve problems instead of writing code
    • Code writing is a last resort
  • Separate formatting-only changes from functional changes

Working Habits

  • Be critical and thorough. Prefer truth and direct feedback over politeness
  • Look around and use existing patterns and code when possible. Look for:
    • Similar components and use their patterns
    • Library code you can reuse
    • Existing dependencies from package.json or Cargo.toml that you should use
  • Always consider the developer experience:
    • Am I placing a burden on the developer with this change?
    • Is it as easy to use / execute / import / configure as possible?
  • When making any changes:
    • Consider the impact on other parts of the codebase
      • What tests, documentation, etc. needs to be updated?
      • Search for other files that should be changed after what you just did
    • How has the context changed now that I've made this change?
      • Should I refactor the code to introduce an abstraction to make it more maintainable?
      • Should I delete anything that's now unused?
  • Check your work after you finish a task:
    • Did I address everything I was asked to?
    • Run npm run standards (or tsc / eslint / commitlint / markdownlint / cargo lint-clippy && cargo lint-fmt as appropriate)
    • Test significant changes by:
      • Running the tests
      • Running the app and manually testing the changes (Tauri MCP/CLI or Playwright MCP/CLI)

Naming Conventions (General)

  • Use PascalCase for classes
  • Use camelCase for variables, instance functions, and methods
  • Use snake_case for static functions
  • Use kebab-case for file and directory names
  • Use UPPERCASE for environment variables
  • Files exporting classes: PascalCase.js (e.g., User.js)
  • Files exporting functions/objects: kebab-case.js (e.g., my-function.js)
  • Tests: ClassTheyAreTesting.test.js
  • Avoid magic numbers and define constants
  • When it has an acronym or initialism, use all lowercase or all caps, never mixed-case:
    • url or URL, never Url
    • id or ID, never Id. Prefer ID over id when writing docs/sentences unless documenting a third-party entity or when specifically referring to a code object with that exact casing (parameter, variable, etc.)

Formatting Rules

Indentation

  • 3 spaces (never tabs)
  • Wrapped lines: indent one level from first line
  • Chained functions: indent one level from chain start

Braces & Structure

  • Opening brace at end of line (K&R style)
  • Always use braces for conditionals/loops (even single line)
  • One blank line between unrelated statements
  • One-two blank lines between functions

Spacing & Operators

  • Space after control structures: if (condition)
  • No space between function name and parenthesis: myFunction()
  • No space between catch and parentheses: catch(error) {
  • Space around operators (except unary: !, ++, --)
  • Space after commas in arrays/arguments
  • Spaces inside array brackets: [ 'item' ] and object braces: { key: 'value' }
  • Empty arrays/objects: no spaces ([], {})
  • Multi-line arrays/objects: always trailing comma

Control Structures

  • Avoid deep nesting (low cyclomatic complexity)
  • Most common case in if (not else)
  • Use positive logic over negative
  • Break complex conditions into variables/functions
  • Check error conditions early with early returns
  • Do not add defensive empty checks before operations that naturally handle empty inputs

Variable Best Practices

  • Declare in lowest possible scope
  • Declare at top of scope before statements
  • Initialized variables before uninitialized
  • Avoid modifying input parameters (except immediate sanitization)
  • Always sanitize user input
  • Prefer immutability (readonly, as const, const)

Function Documentation

  • Document the function purpose in JSDoc format
  • Document types or parameters if they are not obvious from the code
  • Omit JSDoc entirely when the function name already conveys its purpose (e.g., do not add /** Creates the foo */ to createFoo())
  • Add JSDoc comments to enum values when the name alone doesn't convey the domain-specific meaning

Comments

  • Only add a comment if:
    • The code's rationale is not obvious from naming/context
    • The comment answers "why," NOT "what" or "how"
    • The surrounding code uses comments in a similar way
  • Do not comment on types, parameters, or usage that are clear from code or naming
  • Use ASCII in comments, never unicode symbols

File Standards

  • End with newline character (not blank line)
  • No Windows line endings
  • No commented-out code without reason
  • Ternary operator only for simple conditions

Front-End Development

  • Pay attention to the current version of the component, and use a similar pattern as set by existing elements
  • Consider accessibility / a11y
  • Create reusable components rather than ad-hoc solutions

Library Usage

  • Use existing libraries to the fullest extent possible
  • Always verify function signatures before using

css-scss

  • Always use SCSS, not CSS
  • If using a component library, use the component's existing props or built-in options over custom styling. Reuse appropriate CSS classes
  • If none are available, prefer pre-existing utility classes over custom styling
  • Avoid ad-hoc CSS unless absolutely necessary
  • Consider adding a custom utility class to the global SCSS if a pattern is used in multiple places (e.g. text truncation, screen reader text, grid patterns)
  • Use CSS logical properties: margin-inline-start, not margin-left; text-align: start, not text-align: left
  • Use CSS variables for theme-able values

ABSOLUTELY DO NOT

  • DO NOT use @extend
  • DO NOT override line-height to values other than 1 unless you have a very good reason

SCSS API

If the project auto-injects SCSS namespaces via its build config (e.g. Vite's css.preprocessorOptions), these are generally libraries and you should prefer these mixins/vars/functions over hard-coded values.

Check the project's vite.config.ts or equivalent to see what is available.

Style Block Structure

Vue components have two optional <style> blocks:

  1. A non-scoped block for CSS custom property (theme variable) definitions
  2. A scoped block for all component-specific styling

Theming

CSS custom property naming

Follow a consistent naming pattern for theme variables:

--<namespace>-<component>-<property>
--<namespace>-<component>-<subComponentOrVariant>-<property>--<state>

Examples: --button-fill-color, --button-fill-bgColor--hover, --toggle-bgColor--active, --chip-borderColor--selected

If the project uses a namespace prefix (e.g. app-), include it: --app-button-fill-color, --app-toggle-bgColor--active

Theme layers

When supporting light/dark mode with explicit theme classes, define variables in three places for full coverage:

  1. :root, .theme--light { ... } — light mode (explicit opt-in and default)
  2. .theme--dark { ... } — dark mode (explicit opt-in)
  3. @media (prefers-color-scheme: dark) { :root:not(.theme--light) { ... } } — system-preference dark mode

Naming Conventions (BEM-like with SCSS)

  • Block: the component root class, matching the component name in camelCase (e.g. .card, .progressBar, .toggle). Projects may use a namespace prefix (e.g. .app-card)
  • Element: hyphen-separated from the block (e.g. .card-body, .card-title, .button-label)
  • Modifier: double-hyphen suffix (e.g. .button--disabled, .card--layout-horizontal, .chip--selected)

Use SCSS & nesting to compose these:

.card {
   &-body {
      &-primaryArea { /* .card-body-primaryArea */ }
   }
   &--disabled { /* .card--disabled */ }
}

Exported CSS classes

When a component exposes slots that consumers may fill with custom content, export a frozen object of CSS class names so consumers can apply the same styling:

export const cardCSSClasses = Object.freeze({
   cardPrimaryArea: 'card-body-primaryArea',
   cardTitle: 'card-title',
});

Pass these to slots via v-bind:

<slot name="title" v-bind="{ title, className: cardCSSClasses.cardTitle }" />

Limit exported class names to those required for styling slotted content or test assertions.

Property Ordering

.className {
   // Positioning
   position: absolute;
   z-index: 1;
   top: 0;
   inset-inline-end: 0;

   // Display / box model
   display: inline-block;
   overflow: hidden;
   width: 100px;
   padding: 10px;
   border-style: solid;
   margin: 10px;

   // Color
   background: #000000;
   color: #ffffff;

   // Text
   font-family: sans-serif;
   font-size: 16px;
   line-height: 1.4;
   text-align: right;

   // Everything else
   cursor: pointer;
}

Scoped Style Patterns

:deep()

Use :deep() to style slotted or child-component content from a scoped parent:

:deep(.card-title) { font-weight: 400; }
:deep(> .icon) { flex-shrink: 0; }

v-bind() in CSS

Vue's v-bind() can reference reactive values from <script setup> in <style scoped> blocks:

const aspectRatio = ref('16 / 9');
.thumbnail {
   aspect-ratio: v-bind(aspectRatio);
}

Responsive Design

  • No fixed pixel breakpoints. Components should rely on Flexbox, CSS Grid, and container-relative sizing
  • Use @media (hover: hover) or a project-provided hover mixin when giving a component hover styles that should not show on touch-only devices
  • Use prefers-reduced-motion: reduce to disable animations where appropriate
  • Consider whether a component's text should be user-selectable. Use user-select: none for button-like interactive elements

SCSS Syntax

  • Use @use and @forward. Do not use @import
  • Use sass:map, sass:list, etc. via @use 'sass:map' as map;
  • Prefer the modern Dart Sass compiler API (api: 'modern-compiler' in Vite config)
  • CSS Modules (.module.scss) should only be used for demo/story styling, not in library components. Use :global() within a module file to target third-party data attributes

do-not

  • ABSOLUTELY DO NOT create ad-hoc test scripts. If you absolutely must, clean up those files when you're done
  • ABSOLUTELY DO NOT ignore "pre-existing" TypeScript or linting errors. If you see them, fix them before proceeding
  • ABSOLUTELY DO NOT ignore "pre-existing" tests that fail. If you see them, fix them before proceeding
  • ABSOLUTELY DO NOT ignore "pre-existing" documentation that is out of date. If you see it, fix it before proceeding
  • ABSOLUTELY DO NOT use @deprecated on anything unless you are explicitly asked to. Always fully refactor and delete old code as-needed instead of deprecating it
  • ABSOLUTELY DO NOT implement functionality that already exists in a library or package, especially if that package is already installed in the project. Examples: parsing, validation, formatting
  • ABSOLUTELY DO NOT disable linting rules (ESLint, oxlint, clippy, etc.) in the config to get around linting errors. Fix the underlying issues
  • ABSOLUTELY DO NOT instruct me to do things like "run the dev server and test it out," "run the tests," "install this module", or anything else that you can do yourself as part of the task

git

Refer to this when running git commands:

ABSOLUTELY DO NOT

  • ABSOLUTELY DO NOT git push without express permission
  • ABSOLUTELY DO NOT include Co-Authored-By statements in git commit messages

Commit Message Format

Commits must follow the Conventional Commits specification:

<type>(<scope>): <subject> (#issue)

[optional body]

[optional footer]

Type (Required)

  • feat: — New feature
  • fix: — Bug fix
  • docs: — Documentation changes
  • style: — Code style changes (formatting, white-space, etc.)
  • refactor: — Code refactoring without behavior changes
  • perf: — Performance improvements
  • test: — Adding or updating tests
  • build: — Changes to build system or dependencies
  • ci: — CI/CD configuration changes
  • chore: — Maintenance tasks
  • config: — Configuration changes
  • revert: — Revert a previous commit

Scoped Commits

These are the ONLY two kinds fo scoped commits allowed

  • sub(feat): — Sub-commit of a larger feature
  • sub(fix): — Sub-commit of a larger fix

Subject Line Requirements

  • 72 characters maximum for the full header (type + scope + subject + issue)
  • Use imperative mood ("add feature" not "added feature" or "adds feature")
  • Lowercase start (except proper nouns)
  • Do not end with a period
  • Include issue number in format (#xxxxx) at the end
  • If multiple issues, include additional numbers in the body/footer instead
  • Describe what the commit does, not what was wrong

Body Requirements

  • Separate from subject with a blank line
  • 90 characters maximum per line
  • Explain why — but only if the subject doesn't fully explain the commit on its own
  • Bodies are discouraged for simple commits. Only add a body when you have a reason to explain why the change was made
  • Describe the outcome or behavior change, not the implementation mechanism
  • Prefer plain language over technical jargon
  • Use markdown formatting for lists

Footer

  • Blank line before footer
  • Breaking changes: start with BREAKING CHANGE: (case-sensitive)
  • Extra ticket references: Closes #123, Fixes #456

Commit Content

  • Each commit is a cohesive unit of work — independently reviewable
  • No "fix review stuff" commits — squash/rebase into the original commits
  • Renames/moves in separate commits from content changes
  • Import updates can be included with file moves

Examples

Subject-only (preferred):

fix: handle expired tokens in auth refresh flow (#12345)

With body (only when needed):

refactor: replace API request queue with streaming pipeline

The queue-based approach caused memory pressure under high concurrency
because it buffered entire responses before forwarding.

mcps

  • Always use context7 when you need code generation, setup or configuration steps, or library/API documentation. Automatically use the Context7 MCP tools to resolve library ID and get library docs without being explicitly asked
  • If you make UI changes, use MCP tools to test them in a real environment unless project-specific rules say not to
    • Use the Tauri MCP when working within a Tauri app
    • Use Playwright for other projects

npm

  • Always use npm instead of pnpm or yarn

  • Always use the --save-exact flag when installing a dependency

  • Use the -y flag with npx when running a command

  • Use project-defined npm scripts instead of equivalent CLI commands

  • ABSOLUTELY DO NOT introduce new dependencies without verifying the license is admissible (see dependency-licenses.md)

tauri

  • Assume Rust code must compile and run on Android, iOS, and Windows in addition to macOS/Linux. This applies to both Tauri projects and pure Rust crates.
  • Account for platform differences, especially:
    • C library bindings (for example SQLite ABI, pointer sizes, and calling conventions)
    • Conditional compilation (#[cfg(target_os = ...)]) when platform-specific behavior is unavoidable
    • File path handling (use std::path::Path, not string manipulation)
    • Platform-specific dependencies and feature flags in Cargo.toml
  • When introducing or modifying FFI/unsafe code, verify that types and assumptions hold across all target platforms, especially 32-bit Android ARM versus 64-bit desktop.

testing

  • When writing tests, prefer practical e2e tests over unit tests, but add unit tests for critical functionality or complex logic
  • If you write tests, always run them
  • Use descriptive test names that explain the expected behavior
  • Group related tests with describe() blocks
  • Add blank line before expect / assertion statements
  • Follow the Arrange-Act-Assert pattern

Assertion Rules

  • Do NOT use .to.be.true or .to.be.false. Use .to.strictlyEqual(true) or .to.strictlyEqual(false) instead
  • Use .to.eql() for deep equality comparisons

typescript-javascript

Follow these standards:

Core Principles

  • Use let and const instead of var
  • Prefer const for immutable variables
  • Group consecutive declarations in one statement; avoid separate const for simple, related assignments
  • Avoid multi-line const declarations (complex objects get their own block)
  • Use template literals when they improve readability; avoid multi-line template literals
  • Use String() for explicit string conversion
  • Prefer async/await over Promise chaining
  • Use Promise.all() for concurrent async operations
  • Use English for all code and documentation
  • Always wrap comments to utilize the full line length
  • Code must be 140 characters or less; comments must be 91 characters or less
  • Use early returns to reduce indentation

ABSOLUTELY DO NOT

  • ABSOLUTELY DO NOT use any in TypeScript code
  • ABSOLUTELY DO NOT use as typecasts to fix type narrowing issues; use or implement proper type guard functions instead
  • ABSOLUTELY DO NOT use do-while loops, for-in loops, nested ternary operators, or multiline template literals

Style

BAD: DO NOT DO THIS

const a = 1;

const b = 2;

const c = 3;

GOOD:

const a = 1,
      b = 2,
      c = 3;

Naming Conventions

  • Use PascalCase for classes
  • Use camelCase for variables, functions, and methods
  • Use snake_case for static functions
  • Use kebab-case for file and directory names
  • Use UPPERCASE for environment variables
  • Avoid magic numbers and define constants
  • Use memo as the accumulator in reduce functions
  • Acronyms: ALL_CAPS except when first letter of camelCase property
    • Good: id, assetID, lank, contentLANK, getAWSRole
    • Bad: assetId, contentLank, getAwsRole
  • Function naming:
    • Return single value: get + column name
    • Return single record: get + table name
    • Return multiple rows: list + table name
    • No return: strong verb + noun
    • Boolean: start with "is" (e.g., isPublished)

Functions & Logic

  • Avoid deeply nested blocks by using early returns and extracting logic
  • Use higher-order functions (map, filter, reduce) to simplify logic
  • Arrow function parameters must have parentheses and explicit returns (good: (a) => { return value; }, bad: a => a.value)
  • No implicit returns in arrow functions; no arrow functions as class properties
  • Use arrow functions for simple cases (<3 instructions), named functions otherwise
  • Use default parameter values instead of null/undefined checks
  • Use RO-RO (Receive Object, Return Object) for passing and returning multiple parameters
  • Use rest parameters instead of arguments
  • Point-free style: when a callback is simply a direct function reference, pass it directly (good: rules.map(lankFromRule), bad: rules.map((rule) => { return lankFromRule(rule); }))

Destructuring

  • Basic object/array destructuring: yes
  • Array destructuring with rest: yes
  • Object destructuring with rest: avoid (risky)
  • Array destructuring with ignores: use with caution
  • Renaming while destructuring: yes
  • Deep data destructuring: avoid (not readable)

Types (TypeScript)

  • No implicit any types; never use any
  • Explicit types for exported variables
  • Explicit return types for functions
  • Use primitive types (string, number, boolean)
  • No space between variable and colon; one space between colon and type
  • Reuse existing type definitions (e.g., Zod-inferred types) rather than duplicating
  • Never use as casts to fix type narrowing issues; use proper type guards

TypeScript Features

  • Rest parameters: yes
  • Spread operator: yes
  • Default parameters: yes
  • Iterators and generators: use with caution
  • Parameter properties: yes (private only)

Data Handling

  • Avoid excessive use of primitive types; encapsulate data in composite types
  • Prefer immutability:
    • Use readonly for immutable properties
    • Use as const for literals that never change

Error Handling

  • Prefer instanceof for error checking
  • Be cautious with string-based error checks

Imports

  • If a process.env value must be set before an import, place the assignment at the top of the file, before imports
  • When process.env values come from CLI args: import arg parser first, parse args, assign process.env, then import everything else

vue

Assume Vue 3.5+ with the Composition API and Single-File Components (SFCs) unless package.json indicates otherwise.

ABSOLUTELY DO NOT

  • ABSOLUTELY DO NOT use v-html in Vue templates; use v-html-content directive instead (sanitizes via DOMPurify)

SFC Structure

Every component follows this block order:

  1. <template>
  2. <script lang="ts"> — type/interface exports and constants (no setup)
  3. <script setup lang="ts"> — component logic
  4. <style lang="scss"> — global (non-scoped) CSS variables for theme support
  5. <style lang="scss" scoped> — scoped component styles

The dual <script> block pattern ensures TypeScript interfaces and exported constants are available to consumers without requiring <script setup>.

<template>
   <div class="myComponent">
      <slot />
   </div>
</template>

<script lang="ts">
export interface MyComponentProps {
   label: string;
}
</script>

<script setup lang="ts">
const props = defineProps<MyComponentProps>();
</script>

<style lang="scss">
/* theme variables (non-scoped so theme switching works globally) */
</style>

<style lang="scss" scoped>
/* component styles */
</style>

Usage:

import { MyComponentProps }, MyComponent from './MyComponent.vue';

Props

TypeScript interface props (preferred)

Define a TypeScript interface in the non-setup <script> block and reference it with defineProps<T>():

<script lang="ts">
export interface ButtonProps {
   disabled?: boolean;
   appearance?: 'subdued' | 'link' | 'outline' | 'fill';
}
</script>

<script setup lang="ts">
const props = withDefaults(defineProps<ButtonProps>(), {
   disabled: false,
   appearance: 'subdued',
});
</script>

Discriminated union props

When a component has mutually-exclusive prop combinations, define separate interfaces and combine them with a union type. Use never to exclude invalid combinations:

export interface IconOnlyButtonProps extends BaseButtonProps {
   icon: IconName;
   label?: never;
   ariaLabel: string; // Required when no visible label
}

export interface LabelOnlyButtonProps extends BaseButtonProps {
   icon?: never;
   label: string;
   ariaLabel?: string;
}

export type ButtonProps = IconOnlyButtonProps | LabelOnlyButtonProps;

defineModel

For v-model bindings, use defineModel (Vue 3.4+):

const modelValue = defineModel<boolean>({ default: false });

In the template:

<SwitchRoot v-model="modelValue" />

Emits

Unlike props and slots interfaces, emits may be defined inline:

defineEmits<{
   'update:selected': [value: boolean];
   select: [event: Event];
}>();

Slots

Define slot types in the non-setup <script> block and register them with defineSlots:

<script lang="ts">
export interface CardSlots {
   default?: never;
   actions?: () => unknown;
   title?: (props: { title: string; className: string }) => unknown;
}
</script>

<script setup lang="ts">
defineSlots<CardSlots>();
</script>

Slot props

Pass CSS classes and internal state to slot consumers via v-bind:

<slot v-bind="{ cssClasses: buttonCSSClasses }">
   <!-- default content -->
</slot>

<slot name="title" v-bind="{ title, className: cardCSSClasses.cardTitle, id: titleID }">
   <h3 :id="titleID" :class="cardCSSClasses.cardTitle">{{ title }}</h3>
</slot>

Checking slot existence

Use useSlots() or $slots to conditionally render wrapper elements:

const slots = useSlots();
<div v-if="$slots.actions" class="card-actions">
   <slot name="actions" />
</div>

VueUse

Use @vueuse/core (and @vueuse/components if installed) instead of raw browser APIs. VueUse composables handle lifecycle cleanup automatically and are SSR-safe.

Instead of Use
addEventListener / removeEventListener useEventListener()
setTimeout / clearTimeout useTimeoutFn()
setInterval / clearInterval useIntervalFn()
new ResizeObserver() useResizeObserver()
new IntersectionObserver() useIntersectionObserver()
window.matchMedia() useMediaQuery()
Manual scroll position tracking useScroll()
lodash.debounce / hand-rolled debounce useDebounceFn()
getComputedStyle / manual CSS var reads useCssVar()

Other commonly used composables: onClickOutside(), useElementSize(), useFocusTrap(), useVModel().

Template Refs

Use useTemplateRef (Vue 3.5+):

const thumbnail = useTemplateRef<typeof Thumbnail>('thumbnail');

Component Composition

Dynamic element rendering

Use <component :is="..."> to switch between element types based on props:

const elementType = computed(() => {
   return props.href ? 'a' : 'button';
});
<component :is="elementType" v-bind="attrs">
   <slot />
</component>

If using a headless component library (e.g. Reka UI, Radix Vue), consider its <Primitive> component for root-level elements — it provides asChild for composability without extra wrapper elements in the DOM.

inheritAttrs

When a component needs manual control over where $attrs are applied, disable automatic attribute inheritance and spread attrs explicitly:

defineOptions({ inheritAttrs: false });
<template>
   <div>
      <button v-bind="{ ...$attrs, onClick: undefined }" />
   </div>
</template>

Exports

Component index

Every component should be exported from the component index with both a named default export and a wildcard re-export (for types):

export * from './Button.vue';
export { default as Button } from './Button.vue';

Type exports

Export all public interfaces, type aliases, and constants from the non-setup <script> block so consumers can import them alongside the component.

Accessibility

  • All interactive components must have appropriate ARIA attributes (role, aria-label, aria-pressed, aria-disabled, tabindex, etc.)
  • Icon-only buttons require an ariaLabel prop (enforce at type level with discriminated unions)
  • Use <label for> associations for form controls, or aria-labelledby when the label lives outside the component
  • Screen-reader-only content uses a .sr-only utility class
  • Support keyboard interaction (@keydown.enter, @keydown.space) alongside click handlers

Server-Side Rendering (SSR)

If the component library must render in both browser and Node.js environments:

  • Do not access window, document, navigator, or other browser globals at the top level of <script setup>. Guard behind onMounted or typeof window !== 'undefined'
  • Prefer VueUse composables (SSR-safe by default)
  • All custom directives must implement the getSSRProps hook so Vue's SSR renderer can produce correct attributes without mounting the directive in a DOM:
export const vMyDirective = {
   beforeMount(el, binding) {
      el.setAttribute('data-x', binding.value);
   },
   getSSRProps(binding) {
      return { 'data-x': binding.value };
   },
} satisfies ObjectDirective;

Template Format

<ComponentName :prop-1="value" :prop-2="value" :long-prop-name="value" @event="handler">
</ComponentName>

Browser Support

  • Run npx -y browserslist to see the supported browser list if it's not documented

writing

Content

  • Be specific, not generic. Replace vague claims of importance with concrete facts. "Inventor of the first train-coupling device" instead of "a revolutionary titan of industry."
  • Skip the significance speeches. Do not add sentences about how something "marks a pivotal moment," "represents a broader trend," "highlights the enduring legacy," or "underscores the importance." Let facts speak for themselves.
  • Avoid superficial analysis. Do not attach "-ing" phrases that editorialize: "creating a lively community," "showcasing the brand's dedication," "demonstrating ongoing relevance."
  • No promotional language. Cut puffery like "nestled within," "vibrant," "rich tapestry," "seamlessly connecting," "gateway to," "breathtaking." Write neutrally.
  • Attribute specifically or not at all. Do not use weasel phrases like "has been described as," "is considered," "researchers note," "scholars argue" without naming who. If you cannot name a source, remove the claim.
  • Do not exaggerate consensus. Do not present one or two sources as "several publications" or "multiple scholars." Do not imply lists are non-exhaustive when sources give no indication other examples exist.
  • Skip "Challenges and Future Prospects" formulas. Do not write "Despite its [positive words], [subject] faces challenges..." followed by vague optimism about future initiatives.
  • No hedging preambles. Do not acknowledge something is "relatively minor" before explaining its importance anyway.

Word Choice

Avoid overused AI vocabulary:

  • High-frequency offenders: delve, tapestry, landscape, multifaceted, intricate, nuanced, pivotal, comprehensive, innovative, cutting-edge, groundbreaking, transformative, paradigm, foster, leverage, spearhead, underscore, highlight, crucial, vital, robust, seamless, holistic, synergy, realm, beacon, testament, embark, unveil, unravel, commendable, meticulous, intrinsic, Moreover, Furthermore, Notably, Importantly, Indeed, Thus, Hence, Therefore, Consequently
  • One or two may be coincidental; clusters are evidence of overuse

Grammar & Structure

  • Use simple verbs. Prefer "is" and "are" over "serves as," "stands as," "marks," "represents," "constitutes," "features," "offers." Write "She is chairman" not "She serves as chairman."
  • Avoid negative parallelisms. Do not write "not only ... but also," "it's not just about ... it's ...," "not ... but rather." These constructions try to appear balanced but often add nothing.
  • Break the rule of three. Do not default to "adjective, adjective, and adjective" or "phrase, phrase, and phrase" patterns. Vary list lengths.
  • Avoid elegant variation. If you name something, use that name again. Do not cycle through synonyms like "the protagonist," "the key player," "the eponymous character" to avoid repetition.
  • No false ranges. "From X to Y" requires a real scale. "From the Big Bang to the cosmic web" or "from problem-solving to artistic expression" are meaningless ranges that sound impressive but say nothing.

Formatting & Style

  • Use sentence case for headings. Do not capitalize every word in section titles.
  • Avoid excessive boldface. Do not bold every key term or create "key takeaways" lists with bolded headers.
  • No emojis unless explicitly requested

Communication

  • No meta-commentary. Do not include phrases like "In this section, we will discuss..." or "Below is a detailed overview based on available information."
  • No disclaimers about knowledge gaps. Do not write "While specific details are not extensively documented..." or "As of my last knowledge update..."
  • No placeholder text. Do not leave brackets like [describe the specific section] or dates like 2025-XX-XX.
  • No collaborative language in output. Do not write "Would you like me to..." or "Here's a template you can customize" in content meant for publication.

Source: https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing

Dependency License Policy

Dependencies must be licensed under one of the following licenses, or a license that is equally permissive:

  • 0BSD
  • AFL-2.1
  • Apache-2.0
  • BSD-2-Clause
  • BSD-3-Clause
  • ISC
  • MIT
  • Python-2.0
  • Unlicense
  • WTFPL

Rules

  • Do not recommend or add any dependency whose license is not on the admissible list above (or equally permissive)
  • Verify the license of a dependency before recommending or installing it
  • If an existing dependency is discovered to be licensed under a non-admissible license, immediately flag it to the developer before proceeding with any other work
  • Copyleft licenses (e.g., GPL, LGPL, MPL, AGPL, EUPL, SSPL, BSL) and non-commercial or source-available licenses are not admissible
  • When in doubt about whether a license is "equally permissive," flag it to the developer for a decision rather than assuming it is acceptable

libraries

Use these by default:

  • Vue / Vue Router / Pinia
  • Vite 8+
  • Vitest
    • Vitest Browser + Playwright if applicable to project
  • TypeScript 6+
  • oxlint / oxfmt
  • NPM
  • https://github.com/guidepup/guidepup for screenreader testing

markdown

Lists

  • Always indent lists by 3 spaces
  • Always use * for unordered lists instead of -
  • Include a newline before and after the list

Code Blocks

  • Always include a tag to specify the language, even if it's txt

  • Run npm run standards if available to lint with markdownlint

sql

Core Principles

  • Readability and clarity through vertical alignment
  • UPPERCASE keywords and functions
  • Explicit column names (never SELECT *)
  • Consistent formatting and indentation

Formatting Rules

Indentation and Alignment

  • Vertically align sibling elements of queries
  • Indent new lines in statements
  • Start new lines with commas when splitting comma-delimited text
  • Break lines at keywords (JOIN, WHERE, AND, OR)
  • Terminate statements with semicolon on new line
SELECT u.userID
     , u.firstName
     , u.lastName
     , p.profileImageUrl
  FROM Users u
  JOIN UserProfiles p
    ON u.userID = p.userID
 WHERE u.isActive = 1
   AND u.createdDate >= '2023-01-01'
ORDER BY u.lastName
        , u.firstName
;

Alignment Pattern Quick Reference

SELECT column1          -- SELECT keyword left-aligned
     , column2          -- Columns aligned with comma-first
     , column3
  FROM TableName t      -- FROM indented 2 spaces
  JOIN OtherTable o     -- JOIN aligned with FROM
    ON t.id = o.id      -- ON conditions indented from JOIN
   AND t.active = 1     -- Additional conditions aligned with ON
 WHERE t.status = 1     -- WHERE aligned with FROM/JOIN
   AND t.date > '2023'  -- Additional conditions aligned
ORDER BY column1        -- ORDER BY aligned with FROM/JOIN/WHERE
        , column2       -- Order columns aligned
;                       -- Semicolon on new line

Naming Conventions

  • UPPERCASE: SQL keywords, functions (SELECT, FROM, WHERE, COUNT)
  • PascalCase: Table and view names, plural form (e.g., Users, UserProfiles)
  • camelCase: Column names (e.g., userID, firstName, emailAddress)
  • Capitalize "ID" suffix (e.g., userID, not userId)
  • Aliases: Short but clear; one/two letters for simple cases (e.g., u, p), descriptive abbreviations for complex queries

Query Structure

SELECT Statements

  • List columns in logical order: IDs first, descriptive fields, then dates
  • Use explicit column names (never SELECT *)
  • Group related columns together

JOIN Clauses

  • Use explicit JOIN syntax (INNER JOIN, LEFT JOIN, etc.)
  • Align ON conditions with JOIN keyword
  • Use parentheses for complex join conditions

WHERE Clauses

  • Each condition on its own line for complex queries
  • Use parentheses to clarify order of operations
  • Group related conditions together

Performance Guidelines

Indexing

  • Create indexes for columns in WHERE and JOIN clauses
  • Create composite indexes for multi-column queries
  • Consider maintenance cost on frequently-updated tables

Query Optimization

  • Use EXISTS instead of IN for subqueries checking existence
  • Limit result sets with appropriate WHERE conditions
  • Use LIMIT when only a subset is needed
  • Use EXPLAIN QUERY PLAN to verify index usage

DDL

Table Creation

  • Define primary keys explicitly
  • Use appropriate data types
  • Include NOT NULL constraints where appropriate
  • Add meaningful default values
  • Define foreign key relationships explicitly
CREATE TABLE Users (
   userID INTEGER PRIMARY KEY AUTOINCREMENT
 , firstName TEXT NOT NULL
 , lastName TEXT NOT NULL
 , emailAddress TEXT NOT NULL UNIQUE
 , isActive INTEGER NOT NULL DEFAULT 1
 , createdDate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
 , lastModifiedDate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Best Practices

  • Use parameterized queries to prevent SQL injection
  • Avoid dynamic SQL with string concatenation
  • Consider NULL values in logic; use COALESCE for defaults
  • Keep queries focused on single purpose
  • Break complex queries into views or CTEs when appropriate
  • SQLite supports both ordinary and recursive CTEs
  • Comment complex business logic and non-obvious performance considerations

swift

These standards supplement the general Silvermine coding standards. Where Swift community conventions conflict with the general standard, the Swift-specific rule takes precedence.

Core Principles

  • Prefer value types (structs) over reference types (classes) unless you need identity semantics or inheritance
  • Leverage Swift's type system for safety (optionals, enums with associated values, protocols)
  • Use Swift Concurrency (async/await, actors) over callback patterns
  • Follow Swift API Design Guidelines for naming

Formatting

  • One blank line between method definitions
  • No blank line after opening brace or before closing brace
  • One space after colon in type declarations, no space before
  • Break long lines at logical points; indent continuation lines by 3 spaces

Naming Conventions

Overrides from general standard: Swift uses camelCase for all functions and properties, including static members. Do not use snake_case for static functions, UPPER_SNAKE_CASE for constants, or underscore prefixes for private members.

  • camelCase for all variables, functions, and properties (including static)
  • PascalCase for types (classes, structs, enums, protocols)
  • Constants use let with camelCase (not UPPER_SNAKE_CASE)
  • Verbs for functions that perform actions
  • Noun phrases for functions returning values without side effects
  • Omit get and list prefixes for accessor methods
  • Boolean properties: is, has, can, should prefixes
  • Enum case names: lowerCamelCase
  • Protocols: nouns for what something is; -able/-ible/-ing for capabilities

Function Examples

// Good
func remove(at index: Int)
func user(id: String) -> User?
func activeUsers() -> [User]
func fetchUser(id: String) async throws -> User

// Bad
func removeItem(atIndex index: Int)
func getUser(id: String) -> User?
func listUsers() -> [User]

Code Organization

  • Use the most restrictive access control possible; default to private
  • One type per file unless types are closely related
  • Ordering: properties, initializers, computed properties, public methods, private methods
  • Use extensions to organize code by functionality (one protocol per extension)

Value Types vs Reference Types

  • Structs: simple data values, properties are themselves value types
  • Classes: identity semantics or inheritance needed
  • Actors: thread-safe shared mutable state

Swift Features

Optionals

  • Never force unwrap (!) except in tests or when truly impossible to be nil
  • Use optional binding (if let, guard let) for safe unwrapping
  • Use nil coalescing (??) for default values

Error Handling

  • Use throw/catch for recoverable errors
  • Use guard for early returns; the else block must exit scope
  • Provide meaningful error types with enums

Concurrency

  • Use Swift Concurrency (async/await, actors)
  • Leverage Swift 6 strict concurrency checking and @Sendable annotations
  • Use async let for concurrent tasks
  • Use actors for thread-safe shared mutable state

Closures

  • Use trailing closure syntax when the closure is the last argument
  • Prefer named parameters over shorthand ($0, $1) for closures longer than a single expression
  • Be explicit about capture lists

Memory Management

  • Avoid strong reference cycles with weak and unowned
  • Use value types (structs) when appropriate to avoid reference cycle concerns
  • Be explicit about capture lists in closures
func updateProfile() {
   networkService.fetch { [weak self] result in
      guard let self else { return }
      self.updateUI(with: result)
   }
}

Documentation

Swift uses /// documentation comments with - Parameters: and - Returns: markup instead of JSDoc:

/// Calculates the total price including tax
///
/// - Parameters:
///   - items: The items to calculate the price for.
///   - taxRate: The tax rate to apply.
/// - Returns: The total price with tax applied
func calculateTotal(for items: [Item], taxRate: Double) -> Double {
   // Implementation
}

Testing

  • Prefer Swift Testing framework (@Test, #expect) over XCTest for new tests
  • Test all public interfaces
  • Follow Arrange-Act-Assert pattern

Dependency Management (Swift Package Manager)

  • Always specify exact versions for dependencies
  • Avoid version ranges or branch dependencies in production code
// Good
dependencies: [
    .package(url: "https://github.com/apple/swift-log.git", exact: "1.4.2"),
]

// Bad
dependencies: [
    .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
]