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.
packages/cli: Theoclif-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 ofai.jsonand internal types.packages/mcp-registry-client: Client for fetching metadata from the official MCP registry.packages/site: (TBD) Documentation or landing site.
- Language: TypeScript (ESM)
- Frameworks:
oclif(CLI),Ink(CLI UI),React - Validation:
Zod - Testing:
Vitest - Linting/Formatting:
oxlint,oxfmt - Build Tool:
tsc(TypeScript Compiler)
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: Runoxlintfor fast linting.npm run format: Format code usingoxfmt.npm run typecheck: Runtsc --noEmitto verify types.
npm run dev -w @a1st/aix: Run the CLI in development mode usingts-node.
- 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
oxlintfor 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"). Usenpm run version:bumpto update versions across the monorepo before tagging.
ai.json: The primary configuration file for a workspace.package.json: Monorepo root configuration and shared scripts.packages/schema/src/config.ts: Defines the mainAiJsonConfigschema.packages/cli/src/commands/: Implementation of CLI commands (init, add, install, etc.).packages/core/src/loader.ts: Logic for loading and mergingai.jsonfiles.
- 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
- 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?
- Consider the impact on other parts of the codebase
- Check your work after you finish a task:
- Did I address everything I was asked to?
- Run
npm run standards(ortsc/eslint/commitlint/markdownlint/cargo lint-clippy && cargo lint-fmtas appropriate) - Test significant changes by:
- Running the tests
- Running the app and manually testing the changes (Tauri MCP/CLI or Playwright MCP/CLI)
- 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:
urlorURL, neverUrlidorID, neverId. PreferIDoveridwhen writing docs/sentences unless documenting a third-party entity or when specifically referring to a code object with that exact casing (parameter, variable, etc.)
- 3 spaces (never tabs)
- Wrapped lines: indent one level from first line
- Chained functions: indent one level from chain start
- 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
- Space after control structures:
if (condition) - No space between function name and parenthesis:
myFunction() - No space between
catchand 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
- Avoid deep nesting (low cyclomatic complexity)
- Most common case in
if(notelse) - 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
- 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)
- 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 */tocreateFoo()) - Add JSDoc comments to enum values when the name alone doesn't convey the domain-specific meaning
- 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
- End with newline character (not blank line)
- No Windows line endings
- No commented-out code without reason
- Ternary operator only for simple conditions
- 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
- Use existing libraries to the fullest extent possible
- Always verify function signatures before using
- 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, notmargin-left;text-align: start, nottext-align: left - Use CSS variables for theme-able values
- DO NOT use
@extend - DO NOT override
line-heightto values other than1unless you have a very good reason
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.
Vue components have two optional <style> blocks:
- A non-scoped block for CSS custom property (theme variable) definitions
- A scoped block for all component-specific styling
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
When supporting light/dark mode with explicit theme classes, define variables in three places for full coverage:
:root, .theme--light { ... }— light mode (explicit opt-in and default).theme--dark { ... }— dark mode (explicit opt-in)@media (prefers-color-scheme: dark) { :root:not(.theme--light) { ... } }— system-preference dark mode
- 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 */ }
}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.
.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;
}Use :deep() to style slotted or child-component content from a scoped parent:
:deep(.card-title) { font-weight: 400; }
:deep(> .icon) { flex-shrink: 0; }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);
}- 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: reduceto disable animations where appropriate - Consider whether a component's text should be user-selectable. Use
user-select: nonefor button-like interactive elements
- Use
@useand@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
- 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
@deprecatedon 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
Refer to this when running git commands:
- https://raw.githubusercontent.com/silvermine/silvermine-info/refs/heads/master/commit-history.md
- https://raw.githubusercontent.com/silvermine/standardization/refs/heads/master/commitlint.js
- ABSOLUTELY DO NOT
git pushwithout express permission - ABSOLUTELY DO NOT include
Co-Authored-Bystatements in git commit messages
Commits must follow the Conventional Commits specification:
<type>(<scope>): <subject> (#issue)
[optional body]
[optional footer]
feat:— New featurefix:— Bug fixdocs:— Documentation changesstyle:— Code style changes (formatting, white-space, etc.)refactor:— Code refactoring without behavior changesperf:— Performance improvementstest:— Adding or updating testsbuild:— Changes to build system or dependenciesci:— CI/CD configuration changeschore:— Maintenance tasksconfig:— Configuration changesrevert:— Revert a previous commit
These are the ONLY two kinds fo scoped commits allowed
sub(feat):— Sub-commit of a larger featuresub(fix):— Sub-commit of a larger fix
- 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
- 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
- Blank line before footer
- Breaking changes: start with
BREAKING CHANGE:(case-sensitive) - Extra ticket references:
Closes #123,Fixes #456
- 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
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.
- 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
-
Always use
npminstead ofpnpmoryarn -
Always use the
--save-exactflag when installing a dependency -
Use the
-yflag withnpxwhen running a command -
Use project-defined
npmscripts instead of equivalent CLI commands -
ABSOLUTELY DO NOT introduce new dependencies without verifying the license is admissible (see dependency-licenses.md)
- 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.
- 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
- Do NOT use
.to.be.trueor.to.be.false. Use.to.strictlyEqual(true)or.to.strictlyEqual(false)instead - Use
.to.eql()for deep equality comparisons
Follow these standards:
- https://github.com/silvermine/silvermine-info/raw/refs/heads/master/coding-standards/typescript.md
- https://raw.githubusercontent.com/silvermine/silvermine-info/refs/heads/master/coding-standards.md
- Use
letandconstinstead ofvar - Prefer
constfor immutable variables - Group consecutive declarations in one statement; avoid separate
constfor simple, related assignments - Avoid multi-line
constdeclarations (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/awaitover 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 use
anyin TypeScript code - ABSOLUTELY DO NOT use
astypecasts to fix type narrowing issues; use or implement proper type guard functions instead - ABSOLUTELY DO NOT use
do-whileloops,for-inloops, nested ternary operators, or multiline template literals
BAD: DO NOT DO THIS
const a = 1;
const b = 2;
const c = 3;GOOD:
const a = 1,
b = 2,
c = 3;- 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
memoas 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
- Good:
- 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)
- Return single value:
- 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); }))
- 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)
- No implicit
anytypes; never useany - 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
ascasts to fix type narrowing issues; use proper type guards
- Rest parameters: yes
- Spread operator: yes
- Default parameters: yes
- Iterators and generators: use with caution
- Parameter properties: yes (private only)
- Avoid excessive use of primitive types; encapsulate data in composite types
- Prefer immutability:
- Use
readonlyfor immutable properties - Use
as constfor literals that never change
- Use
- Prefer
instanceoffor error checking - Be cautious with string-based error checks
- If a
process.envvalue must be set before an import, place the assignment at the top of the file, before imports - When
process.envvalues come from CLI args: import arg parser first, parse args, assignprocess.env, then import everything else
Assume Vue 3.5+ with the Composition API and Single-File Components (SFCs) unless
package.json indicates otherwise.
- ABSOLUTELY DO NOT use
v-htmlin Vue templates; usev-html-contentdirective instead (sanitizes via DOMPurify)
Every component follows this block order:
<template><script lang="ts">— type/interface exports and constants (nosetup)<script setup lang="ts">— component logic<style lang="scss">— global (non-scoped) CSS variables for theme support<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';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>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;For v-model bindings, use defineModel (Vue 3.4+):
const modelValue = defineModel<boolean>({ default: false });In the template:
<SwitchRoot v-model="modelValue" />Unlike props and slots interfaces, emits may be defined inline:
defineEmits<{
'update:selected': [value: boolean];
select: [event: Event];
}>();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>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>Use useSlots() or $slots to conditionally render wrapper elements:
const slots = useSlots();<div v-if="$slots.actions" class="card-actions">
<slot name="actions" />
</div>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().
Use useTemplateRef (Vue 3.5+):
const thumbnail = useTemplateRef<typeof Thumbnail>('thumbnail');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.
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>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';Export all public interfaces, type aliases, and constants from the non-setup <script>
block so consumers can import them alongside the component.
- All interactive components must have appropriate ARIA attributes (
role,aria-label,aria-pressed,aria-disabled,tabindex, etc.) - Icon-only buttons require an
ariaLabelprop (enforce at type level with discriminated unions) - Use
<label for>associations for form controls, oraria-labelledbywhen the label lives outside the component - Screen-reader-only content uses a
.sr-onlyutility class - Support keyboard interaction (
@keydown.enter,@keydown.space) alongside click handlers
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 behindonMountedortypeof window !== 'undefined' - Prefer VueUse composables (SSR-safe by default)
- All custom directives must implement the
getSSRPropshook 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;<ComponentName :prop-1="value" :prop-2="value" :long-prop-name="value" @event="handler">
</ComponentName>- Run
npx -y browserslistto see the supported browser list if it's not documented
- 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.
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
- 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.
- 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
- 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
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
- 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
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
- Always indent lists by 3 spaces
- Always use
*for unordered lists instead of- - Include a newline before and after the list
-
Always include a tag to specify the language, even if it's
txt -
Run
npm run standardsif available to lint withmarkdownlint
- Readability and clarity through vertical alignment
- UPPERCASE keywords and functions
- Explicit column names (never
SELECT *) - Consistent formatting and indentation
- 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
;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- 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, notuserId) - Aliases: Short but clear; one/two letters for simple cases (e.g.,
u,p), descriptive abbreviations for complex queries
- List columns in logical order: IDs first, descriptive fields, then dates
- Use explicit column names (never
SELECT *) - Group related columns together
- Use explicit JOIN syntax (INNER JOIN, LEFT JOIN, etc.)
- Align ON conditions with JOIN keyword
- Use parentheses for complex join conditions
- Each condition on its own line for complex queries
- Use parentheses to clarify order of operations
- Group related conditions together
- Create indexes for columns in WHERE and JOIN clauses
- Create composite indexes for multi-column queries
- Consider maintenance cost on frequently-updated tables
- 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 PLANto verify index usage
- 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
);- 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
These standards supplement the general Silvermine coding standards. Where Swift community conventions conflict with the general standard, the Swift-specific rule takes precedence.
- 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
- 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
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
letwith camelCase (notUPPER_SNAKE_CASE) - Verbs for functions that perform actions
- Noun phrases for functions returning values without side effects
- Omit
getandlistprefixes for accessor methods - Boolean properties:
is,has,can,shouldprefixes - Enum case names: lowerCamelCase
- Protocols: nouns for what something is;
-able/-ible/-ingfor capabilities
// 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]- 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)
- Structs: simple data values, properties are themselves value types
- Classes: identity semantics or inheritance needed
- Actors: thread-safe shared mutable state
- 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
- Use
throw/catchfor recoverable errors - Use
guardfor early returns; theelseblock must exit scope - Provide meaningful error types with enums
- Use Swift Concurrency (
async/await, actors) - Leverage Swift 6 strict concurrency checking and
@Sendableannotations - Use
async letfor concurrent tasks - Use actors for thread-safe shared mutable state
- 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
- Avoid strong reference cycles with
weakandunowned - 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)
}
}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
}- Prefer Swift Testing framework (
@Test,#expect) over XCTest for new tests - Test all public interfaces
- Follow Arrange-Act-Assert pattern
- 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"),
]