-
When delegating to a sub-agent, tell it to read CLAUDE.md before starting work.
-
After any fix, trace ALL scenarios through the code before calling it done.
-
ALWAYS read .ai-docs/DOCUMENTATION_MAP.md before working on any area of the codebase. It indexes verified documentation for every major system.
-
NEVER run ANY git commands that modify the staging area or working tree — no
git add,git reset,git stash,git checkout,git restore,git clean. The user curates their staging area intentionally. This applies to sub-agents too — explicitly forbid git commands when delegating.
This file provides behavioral rules and conventions. For codebase reference documentation, see .ai-docs/.
| Directory | Purpose |
|---|---|
/home/vince/dev/cli |
CLI tool (this repo) - entry point for all operations |
/home/vince/dev/skills |
Plugin marketplace - skills, agents, stacks |
/home/vince/dev/cv-launch |
Test project - install targets for testing |
- NEVER run ANY git command that modifies the staging area or working tree (
git add,git reset,git stash,git checkout,git restore,git clean) - NEVER use git worktrees (
isolation: "worktree") - NEVER use
git checkout,git restore, or any command that discards working tree changes — ask the user how to proceed - NEVER introduce new workflow patterns (tools, flags, strategies) that the user hasn't explicitly requested
- NEVER put machine-specific absolute paths in any file tracked by git
- NEVER use
as SkillIdoras SkillSlugcasts on valid union members — the literal string IS the type. Only cast at parse boundaries (YAML, JSON, CLI args) or for deliberately invalid error-path test data (testing that bad IDs produce errors). Fabricated test IDs that aren't in the union should usestringtype, not casts. - NEVER use
as unknown as Tdouble casts — fix the upstream type instead - NEVER use
{} as Record<K, V>— useconst x: Partial<Record<K, V>> = {}with a type annotation - NEVER use
matrix.skills[id]!non-null assertions — usegetSkillById(id)frommatrix-provider.ts
- NEVER use optional chaining (
?.) or null coalescing (?? "",|| []) on data that must exist — use asserting lookups. Silent fallbacks hide bugs. - NEVER build multi-tier resolution fallbacks (try exact → try alias → try directory name). Data matches on the first lookup or it's an error.
- NEVER fall back to
path.basename(dir)as a skill ID — usefrontmatter.namefromparseFrontmatter() - NEVER derive
slugfrom skill ID or directory path —slugis a required field in metadata, always pass it explicitly - NEVER add backward-compatibility shims or legacy fallbacks — the project is pre-1.0. Remove old code cleanly.
- NEVER hardcode
projectDirfor skill/agent paths when a skill has ascopefield — useos.homedir()for"global"scope,projectDirfor"project"scope - NEVER use
path.join(projectDir, LOCAL_SKILLS_PATH)without checking scope — global-scoped local skills live at~/.claude/skills/, not<project>/.claude/skills/ - NEVER load global local skills as a fallback only when project has none — always merge both project and global local skills (project takes precedence on conflict)
- NEVER pass a uniform scope to
claudePluginInstall/claudePluginUninstallfor multiple skills — each skill has its own scope in itsSkillConfig - NEVER let a marketplace
primarySourceoverride a user's savedsourcein config — saved source ("local"or marketplace name) always takes priority over computed defaults - NEVER use conditional fallbacks like
if (x.length === 0) { use fallback }when both primary and fallback data should always be merged
- NEVER construct test data inline — use factories from
__tests__/helpers.tsand fixtures fromcreate-test-source.ts. If a factory doesn't exist, create one. - NEVER create custom mock skills when a canonical
SKILLS.*entry fromtest-fixtures.tswould work - NEVER call
createMockMatrix(SKILLS.react)inline when a pre-built constant exists inmock-matrices.ts - NEVER pass the entire
SKILLSregistry tocreateMockMatrix— spread individual entries - NEVER construct
ProjectConfig,ProjectSourceConfig, orAgentScopeConfig[]inline — usebuildProjectConfig(),buildSourceConfig(),buildAgentConfigs() - NEVER write inline SKILL.md frontmatter or agent YAML template strings — use
renderSkillMd(),renderAgentYaml()fromcontent-generators.ts - NEVER repeat agent metadata strings inline — use
AGENT_DEFSfrommock-agents.ts - NEVER put TODO/task IDs in test
describe()blocks - NEVER define path/timeout/text constants locally in E2E test files — use
DIRS,FILES,TIMEOUTS,SOURCE_PATHS,STEP_TEXT,EXIT_CODESfrome2e/pages/constants.ts - NEVER write a helper function in an E2E test file without first grepping
e2e/helpers/test-utils.tsande2e/fixtures/for an existing one
- NEVER create redundant type aliases — use
Pick<>,Partial<>, or&. Checktypes/first. - NEVER add unnecessary comments — only when unintuitive, complex, or for edge cases
- NEVER reassign constants to other constants — use the original directly
- NEVER build intermediate data structures imperatively — use
.map(),.flatMap(), or literal arrays - NEVER export constants only used within the same file — run grep before adding
export
- ALWAYS delegate implementation and test code to sub-agents. Tell them to read CLAUDE.md. Tell them: "Do NOT run any git commands."
- ALWAYS trace ALL scenarios through the code after any fix
- ALWAYS grep for the old value when changing test data or renaming anything
- ALWAYS search for all call sites when removing a workaround
- When a task is deferred, ALWAYS move it to
TODO-deferred.md— never delete - ALWAYS write a finding to
.ai-docs/agent-findings/when a sub-agent fixes an anti-pattern, discovers a missing standard, or notices convention drift — use the template from.ai-docs/agent-findings/TEMPLATE.md - ALWAYS tell sub-agents: "If you fix an anti-pattern or discover a missing standard, write a finding to
.ai-docs/agent-findings/using the template in.ai-docs/agent-findings/TEMPLATE.md"
- ALWAYS use
resolveInstallPaths(projectDir, scope)with the explicit scope parameter when resolving skill/agent directories - ALWAYS split skill lists by scope (
filter(s => s.scope === "global")/filter(s => s.scope !== "global")) before any path-dependent operation (copy, delete, install, uninstall) - ALWAYS load both project AND global local skills and merge them — see
source-loader.tsandcompile.tsfor the correct pattern - ALWAYS preserve saved
sourcefrom config over computedprimarySourcewhen restoring wizard state —saved?.source ?? primarySource ?? DEFAULT_PUBLIC_SOURCE_NAME
- ALWAYS use type guards (
isCategory(),isDomain(),isAgentName()fromutils/type-guards.ts) instead ofascasts for runtime narrowing - ALWAYS use
getSkillById(id)orgetSkillBySlug(slug)frommatrix-provider.tsfor skill lookups where the skill must exist. Only usematrix.skills[id]when genuinely optional. - ALWAYS use
parseFrontmatter()fromlib/loading/loader.tsfor SKILL.md parsing - ALWAYS type factory function parameters with the narrowest union type (
SkillId, notstring). Error-path tests cast at the call site. - ALWAYS use
typedEntries()/typedKeys()fromutils/typed-object.ts(not rawObject.entries())
- ALWAYS prefer
SKILLS.*fromtest-fixtures.tsovercreateMockSkill()for standard domain skills - ALWAYS use
createMockMatrixspread syntax:createMockMatrix(SKILLS.react, SKILLS.hono) - ALWAYS use spread isolation
{ ...SKILLS.react }when passing to functions that mutate objects in-place - ALWAYS use pre-built matrix constants from
mock-matrices.tsinstead of inlinecreateMockMatrix(SKILLS.*)calls - ALWAYS use config factories:
buildProjectConfig(),buildSourceConfig(),buildAgentConfigs(),buildSkillConfigs() - ALWAYS use
AGENT_DEFSfrommock-agents.tsfor agent metadata - When fixing test data, ALWAYS evaluate the construction pattern too, not just the values
- ALWAYS read
.ai-docs/standards/e2e/README.mdbefore writing or modifying E2E tests - ALWAYS use
toStrictEqual(nottoEqual) for object and array comparisons in assertions - ALWAYS verify config AND filesystem after any operation that changes either. If a test completes a wizard flow or runs a command that creates, modifies, or removes files or config entries, assert the resulting state of both. If it should NOT change something, snapshot before and assert identical after. Never check only one side.
- NEVER broaden an assertion to make a failing test pass — investigate why it fails. If it's a fixture limitation, keep the strict assertion as a commented-out
// KNOWN GAP:with an explanation. If it's a product bug, mark the testit.fails. - NEVER add a key-press method to an E2E step page object without calling
waitForStableRender()first — React effects may not have fired yet, causing handlers to silently no-op
Use factories from __tests__/helpers.ts and constants from __tests__/mock-data/. Grep for createMock*, build*, SKILLS.*, AGENT_DEFS.*, render* to find what's available. Never inline test data — if a factory doesn't exist, create one.
Is it a complete skill/agent/category object?
├─ YES → Use factory from helpers.ts (createMockSkill, createMockAgent, createMockCategory)
└─ NO → Is it a full project directory structure?
├─ YES → Use createTestSource() from fixtures/create-test-source.ts
└─ NO → Does it create a config, matrix, or stack?
├─ YES → Use a factory (createMockMatrix, buildWizardResult, etc.) — NEVER inline
└─ NO → Is it a partial object for one test case?
├─ YES → Inline is fine
└─ NO → Use factory with overrides parameter
- File naming: kebab-case for ALL files and directories
- Exports: Named exports only (no default exports). Use
.jsextensions on relative imports in new files. - Constants: No magic numbers or hardcoded strings — use
STANDARD_FILES.*,STANDARD_DIRS.*,EXIT_CODES.*,UI_SYMBOLS.*,CLI_COLORS.*fromconsts.ts - Error handling:
getErrorMessage(error)for unknown errors,this.handleError(error)in commands,EXIT_CODES.*constants, no silent catch blocks - Logging:
warn()for user issues,verbose()for diagnostics,log()for always-visible - TypeScript: Zero
anywithout justification, no@ts-ignorewithout comment, Zod schemas at parse boundaries, all remaining casts must have comments explaining why
Items not already covered by NEVER/ALWAYS rules above:
- Tests written and passing (
npm test) - Type check passes (
tsc --noEmit) - No ESLint errors
- No
console.logleft in code - No commented-out code
- Use
createTempDir()/cleanupTempDir()in tests (not rawmkdtemp) - Type definitions updated if public API changed
| Document | Purpose |
|---|---|
| .ai-docs/DOCUMENTATION_MAP.md | Codebase documentation index |
| TODO.md | Active tasks and blockers |