compound-agent 1.6.2 → 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -120,7 +120,9 @@ __export(nomic_exports, {
120
120
  embedTexts: () => embedTexts,
121
121
  getEmbedding: () => getEmbedding,
122
122
  isModelAvailable: () => isModelAvailable,
123
- unloadEmbedding: () => unloadEmbedding
123
+ unloadEmbedding: () => unloadEmbedding,
124
+ unloadEmbeddingResources: () => unloadEmbeddingResources,
125
+ withEmbedding: () => withEmbedding
124
126
  });
125
127
  async function getEmbedding() {
126
128
  if (embeddingContext) return embeddingContext;
@@ -147,23 +149,44 @@ async function getEmbedding() {
147
149
  })();
148
150
  return pendingInit;
149
151
  }
150
- function unloadEmbedding() {
151
- if (embeddingContext) {
152
- embeddingContext.dispose().catch(() => {
153
- });
154
- embeddingContext = null;
152
+ async function unloadEmbeddingResources() {
153
+ const pending = pendingInit;
154
+ if (pending) {
155
+ try {
156
+ await pending;
157
+ } catch {
158
+ }
155
159
  }
156
- if (modelInstance) {
157
- modelInstance.dispose().catch(() => {
158
- });
159
- modelInstance = null;
160
+ const context = embeddingContext;
161
+ const model = modelInstance;
162
+ const llama = llamaInstance;
163
+ embeddingContext = null;
164
+ modelInstance = null;
165
+ llamaInstance = null;
166
+ pendingInit = null;
167
+ const disposals = [];
168
+ if (context) {
169
+ disposals.push(context.dispose());
160
170
  }
161
- if (llamaInstance) {
162
- llamaInstance.dispose().catch(() => {
163
- });
164
- llamaInstance = null;
171
+ if (model) {
172
+ disposals.push(model.dispose());
173
+ }
174
+ if (llama) {
175
+ disposals.push(llama.dispose());
176
+ }
177
+ if (disposals.length > 0) {
178
+ await Promise.allSettled(disposals);
179
+ }
180
+ }
181
+ function unloadEmbedding() {
182
+ void unloadEmbeddingResources();
183
+ }
184
+ async function withEmbedding(fn) {
185
+ try {
186
+ return await fn();
187
+ } finally {
188
+ await unloadEmbeddingResources();
165
189
  }
166
- pendingInit = null;
167
190
  }
168
191
  async function embedText(text) {
169
192
  const ctx = await getEmbedding();
@@ -203,7 +226,9 @@ __export(embeddings_exports, {
203
226
  isModelAvailable: () => isModelAvailable,
204
227
  isModelUsable: () => isModelUsable,
205
228
  resolveModel: () => resolveModel,
206
- unloadEmbedding: () => unloadEmbedding
229
+ unloadEmbedding: () => unloadEmbedding,
230
+ unloadEmbeddingResources: () => unloadEmbeddingResources,
231
+ withEmbedding: () => withEmbedding
207
232
  });
208
233
  var init_embeddings = __esm({
209
234
  "src/memory/embeddings/index.ts"() {
@@ -2291,7 +2316,7 @@ async function runBackgroundEmbed(repoRoot) {
2291
2316
  const start = Date.now();
2292
2317
  writeEmbedStatus(repoRoot, { state: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString() });
2293
2318
  try {
2294
- const result = await embedChunks(repoRoot, { onlyMissing: true });
2319
+ const result = await withEmbedding(async () => embedChunks(repoRoot, { onlyMissing: true }));
2295
2320
  writeEmbedStatus(repoRoot, {
2296
2321
  state: "completed",
2297
2322
  chunksEmbedded: result.chunksEmbedded,
@@ -2306,7 +2331,6 @@ async function runBackgroundEmbed(repoRoot) {
2306
2331
  durationMs: Date.now() - start
2307
2332
  });
2308
2333
  } finally {
2309
- unloadEmbedding();
2310
2334
  closeKnowledgeDb();
2311
2335
  lock.release();
2312
2336
  }
@@ -2369,8 +2393,8 @@ function parseBdShowDeps(raw) {
2369
2393
  init_compound();
2370
2394
  init_embeddings();
2371
2395
  init_storage();
2372
- function registerCompoundCommands(program2) {
2373
- program2.command("compound").description("Synthesize cross-cutting patterns from lessons").action(async () => {
2396
+ function registerCompoundCommands(program) {
2397
+ program.command("compound").description("Synthesize cross-cutting patterns from lessons").action(async () => {
2374
2398
  const repoRoot = getRepoRoot();
2375
2399
  const { items } = await readMemoryItems(repoRoot);
2376
2400
  if (items.length === 0) {
@@ -2385,18 +2409,22 @@ function registerCompoundCommands(program2) {
2385
2409
  return;
2386
2410
  }
2387
2411
  openDb(repoRoot);
2388
- const embeddings = [];
2412
+ let embeddings;
2389
2413
  try {
2390
- for (const item of items) {
2391
- const text = `${item.trigger} ${item.insight}`;
2392
- const hash = contentHash(item.trigger, item.insight);
2393
- let vec = getCachedEmbedding(repoRoot, item.id, hash);
2394
- if (!vec) {
2395
- vec = await embedText(text);
2396
- setCachedEmbedding(repoRoot, item.id, vec, hash);
2414
+ embeddings = await withEmbedding(async () => {
2415
+ const vecs = [];
2416
+ for (const item of items) {
2417
+ const text = `${item.trigger} ${item.insight}`;
2418
+ const hash = contentHash(item.trigger, item.insight);
2419
+ let vec = getCachedEmbedding(repoRoot, item.id, hash);
2420
+ if (!vec) {
2421
+ vec = await embedText(text);
2422
+ setCachedEmbedding(repoRoot, item.id, vec, hash);
2423
+ }
2424
+ vecs.push(vec);
2397
2425
  }
2398
- embeddings.push(vec);
2399
- }
2426
+ return vecs;
2427
+ });
2400
2428
  } catch (err) {
2401
2429
  console.error(`Error computing embeddings: ${err instanceof Error ? err.message : String(err)}`);
2402
2430
  console.error("Run: npx ca download-model");
@@ -2707,7 +2735,7 @@ function playBannerAudio() {
2707
2735
  return null;
2708
2736
  }
2709
2737
  proc.unref();
2710
- const cleanup2 = () => {
2738
+ const cleanup = () => {
2711
2739
  try {
2712
2740
  proc.kill();
2713
2741
  } catch {
@@ -2729,7 +2757,7 @@ function playBannerAudio() {
2729
2757
  } catch {
2730
2758
  }
2731
2759
  });
2732
- return { stop: cleanup2 };
2760
+ return { stop: cleanup };
2733
2761
  } catch {
2734
2762
  return null;
2735
2763
  }
@@ -5678,6 +5706,32 @@ $ARGUMENTS
5678
5706
  **MANDATORY FIRST STEP -- NON-NEGOTIABLE**: Use the Read tool to open and read \`.claude/skills/compound/researcher/SKILL.md\` NOW. Do NOT proceed until you have read the complete skill file.
5679
5707
 
5680
5708
  Then: scan docs/compound/research/ for gaps, propose topics via AskUserQuestion, spawn parallel researcher subagents.
5709
+ `,
5710
+ "agentic-audit.md": `---
5711
+ name: compound:agentic-audit
5712
+ description: Audit codebase against the 15-principle agentic manifesto
5713
+ argument-hint: "<scope or focus area>"
5714
+ ---
5715
+ $ARGUMENTS
5716
+
5717
+ # Agentic Audit
5718
+
5719
+ **MANDATORY FIRST STEP -- NON-NEGOTIABLE**: Use the Read tool to open and read \`.claude/skills/compound/agentic/SKILL.md\` NOW. Do NOT proceed until you have read the complete skill file.
5720
+
5721
+ Run in **audit** mode. Score all 15 principles, produce the report, offer beads epic.
5722
+ `,
5723
+ "agentic-setup.md": `---
5724
+ name: compound:agentic-setup
5725
+ description: Set up codebase for agentic AI development (audit-first)
5726
+ argument-hint: "<scope or focus area>"
5727
+ ---
5728
+ $ARGUMENTS
5729
+
5730
+ # Agentic Setup
5731
+
5732
+ **MANDATORY FIRST STEP -- NON-NEGOTIABLE**: Use the Read tool to open and read \`.claude/skills/compound/agentic/SKILL.md\` NOW. Do NOT proceed until you have read the complete skill file.
5733
+
5734
+ Run in **setup** mode. Audit first, then propose and create files to fill gaps.
5681
5735
  `,
5682
5736
  // =========================================================================
5683
5737
  // Utility commands (kept: learn-that, check-that, prime)
@@ -5799,7 +5853,7 @@ npx ca doctor
5799
5853
  settings.json # Claude Code hooks
5800
5854
  plugin.json # Plugin manifest
5801
5855
  agents/compound/ # Subagent definitions
5802
- commands/compound/ # Slash commands (spec-dev, plan, work, review, compound, cook-it)
5856
+ commands/compound/ # Slash commands (spec-dev, plan, work, review, compound, cook-it, agentic-audit, agentic-setup)
5803
5857
  skills/compound/ # Phase skills + agent role skills
5804
5858
  lessons/
5805
5859
  index.jsonl # Memory items (git-tracked source of truth)
@@ -6160,6 +6214,22 @@ Skills are instructions that Claude reads before executing each phase. They live
6160
6214
 
6161
6215
  **What it does**: Analyzes beads epics for knowledge gaps, checks existing docs coverage, proposes research topics for user confirmation, spawns parallel researcher subagents, and stores output at \`docs/compound/research/<topic>/<slug>.md\`.
6162
6216
 
6217
+ ### \`/compound:agentic-audit\`
6218
+
6219
+ **Purpose**: Audit a codebase against the 15-principle Agentic Codebase Manifesto.
6220
+
6221
+ **When invoked**: When evaluating a codebase's readiness for AI agent collaboration.
6222
+
6223
+ **What it does**: Detects the project stack, scores all 15 principles (0-2) with specific evidence across 3 pillars (Codebase Memory, Implementation Feedbacks, Mapping the Context) plus cross-cutting concerns. Produces a scored report (out of 30) with prioritized actions and offers to create a beads epic for improvements.
6224
+
6225
+ ### \`/compound:agentic-setup\`
6226
+
6227
+ **Purpose**: Set up a codebase for agentic AI development (runs audit first).
6228
+
6229
+ **When invoked**: When you want to improve a codebase's agentic readiness by filling gaps.
6230
+
6231
+ **What it does**: Runs the full audit first, then proposes concrete remediation actions for each gap found. Creates real content (AGENTS.md, docs/, ADRs, lint configs) generated from actual codebase analysis. Asks for user approval before each file creation.
6232
+
6163
6233
  ---
6164
6234
 
6165
6235
  ## Skill invocation
@@ -6176,7 +6246,10 @@ Skills are invoked as Claude Code slash commands:
6176
6246
  /compound:research # Spawn research subagent
6177
6247
  /compound:test-clean # Clean test artifacts
6178
6248
  /compound:get-a-phd <focus> # Deep research for agent knowledge
6179
- /compound:learn # Capture a lesson from context
6249
+ /compound:agentic-audit # Audit codebase against agentic manifesto
6250
+ /compound:agentic-setup # Audit then set up agentic infrastructure
6251
+ /compound:learn-that # Conversation-aware lesson capture with confirmation
6252
+ /compound:check-that # Search lessons and apply to current work
6180
6253
  /compound:prime # Prime session with workflow context
6181
6254
  \`\`\`
6182
6255
 
@@ -6330,6 +6403,12 @@ Work is not complete until \`git push\` succeeds.
6330
6403
  };
6331
6404
 
6332
6405
  // src/setup/templates/skills.ts
6406
+ var _bc = "\x1B[96m";
6407
+ var _cn = "\x1B[36m";
6408
+ var _gr = "\x1B[32m";
6409
+ var _mg = "\x1B[35m";
6410
+ var _yl = "\x1B[33m";
6411
+ var _rs = "\x1B[0m";
6333
6412
  var PHASE_SKILLS = {
6334
6413
  "spec-dev": `---
6335
6414
  name: Spec Dev
@@ -6832,17 +6911,25 @@ Before starting EACH phase, you MUST use the Read tool to open its skill file:
6832
6911
  Do NOT proceed from memory. Read the skill, then follow it exactly.
6833
6912
 
6834
6913
  ## Session Start
6835
- When a cooking session begins, IMMEDIATELY print the chef banner below (copy it verbatim):
6836
-
6837
- \`\`\`
6838
-
6839
- \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
6840
- \u2502\u2592\u2592\u2592\u2592\u2592\u2502
6841
- \u2590\u259B\u2588\u2588\u2588\u259C\u258C o
6842
- \u259D\u259C\u2588\u2588\u2588\u2588\u2588\u259B\u2598|
6843
- \u2598\u2598 \u259D\u259D
6844
- Claude the Cooker
6845
- \`\`\`
6914
+ When a cooking session begins, IMMEDIATELY print the brain banner below (copy it verbatim):
6915
+
6916
+ ___ ___
6917
+ .."\`) "\`.." "(\`\`.
6918
+ .'; _..=. ${_bc}::${_rs} \`-'._ ;\`.
6919
+ : ) ;"\`':._. ${_bc}::${_rs}_. ( :.
6920
+ .:-" _. \`"${_cn}##${_rs}"\` "._ \`-:\\
6921
+ /." -"\` ._.${_bc}::${_rs}._. .'" ".:
6922
+ : : ( -: \`" ${_bc}::${_rs} "\` :- ) : )
6923
+ ( .":==._ ' \`'=._${_cn}##${_rs}_.=' ' _.==: .'
6924
+ (: \`, \`"\` \`"${_cn}##${_rs}"\` \`"\` .'\`".)
6925
+ \\\`' \`"--. "- )( -" ..--"\` \`-/
6926
+ (" (_."\` ="""-..."\`...-""= "._) ")
6927
+ "..__..-" )${_gr}%${_rs}\`..'\`${_gr}%${_rs}( "-..__.."
6928
+ (#"...'\\\\${_gr}%%%%${_rs}/\`..."#)
6929
+ \`${_mg}######${_rs}\`--'${_mg}######${_rs}"
6930
+ "${_mg}###${_rs}")${_yl}@@${_rs}(\`${_mg}###${_rs}"
6931
+ \\${_yl}@@${_rs}/
6932
+ Claw'd
6846
6933
 
6847
6934
  Then proceed with the protocol below.
6848
6935
 
@@ -6971,6 +7058,251 @@ Apply the agreed changes:
6971
7058
  - Full test suite passes after changes
6972
7059
  - Coverage not degraded
6973
7060
  - Findings captured in compound-agent memory
7061
+ `,
7062
+ "agentic": `---
7063
+ name: Agentic Codebase
7064
+ description: Audit and set up a codebase for agentic AI development using the 15-principle manifesto
7065
+ ---
7066
+
7067
+ # Agentic Codebase Skill
7068
+
7069
+ ## Overview
7070
+
7071
+ Assess and improve a codebase's readiness for AI agent collaboration. Based on the 15-principle Agentic Codebase Manifesto organized across 3 pillars.
7072
+
7073
+ This skill operates in two modes:
7074
+ - **Mode: audit** -- Score the codebase against all 15 principles, produce a report with evidence and prioritized actions
7075
+ - **Mode: setup** -- Run audit first, then incrementally fill gaps with real content generated from codebase analysis
7076
+
7077
+ Mode is set by the calling command (\\\`/compound:agentic-audit\\\` or \\\`/compound:agentic-setup\\\`). The command wrapper tells you which mode to run -- do not parse \\\`$ARGUMENTS\\\` for mode detection.
7078
+
7079
+ ## Stack Detection
7080
+
7081
+ Before auditing, detect the project stack to adapt checks:
7082
+ 1. Look for package.json (Node/TS), pyproject.toml or setup.py (Python), Cargo.toml (Rust), go.mod (Go), Makefile, CMakeLists.txt (C/C++)
7083
+ 2. Check for framework markers: next.config, django, fastapi, express, etc.
7084
+ 3. Identify build/test/lint commands from config files
7085
+ 4. Store detected stack for use in principle checks and AGENTS.md generation
7086
+
7087
+ ## Audit Methodology
7088
+
7089
+ ### Scoring Rubric
7090
+ Each principle is scored:
7091
+ - **0 (Absent)**: No evidence of this principle in the codebase
7092
+ - **1 (Partial)**: Some evidence but incomplete or inconsistent
7093
+ - **2 (Present)**: Clear, consistent implementation
7094
+
7095
+ Adapt criteria to the detected stack. For example: "strict mode" means TypeScript strict, Python mypy --strict, or Rust default safety. "Linter" means ESLint, pylint/ruff, clippy, golangci-lint, etc. Score based on the ecosystem's equivalent tooling.
7096
+
7097
+ ### The 15 Principles
7098
+
7099
+ #### Pillar I: Codebase Memory (Traceability) -- max 8 points
7100
+
7101
+ **P1. Repository is the only truth**
7102
+ Check: All context an agent needs lives in version control
7103
+ Evidence: Look for docs/ directory, inline documentation, config files
7104
+ Score 0: No docs directory, no README beyond boilerplate
7105
+ Score 1: README exists but key context lives elsewhere
7106
+ Score 2: Comprehensive docs/, config, and context all in-repo
7107
+
7108
+ **P2. Trace decisions, not just outcomes**
7109
+ Check: Architectural decisions have recorded rationale
7110
+ Evidence: Look for docs/adr/, docs/decisions/, ADR files
7111
+ Score 0: No decision records
7112
+ Score 1: Some decisions documented but inconsistent format
7113
+ Score 2: ADR directory with structured records
7114
+
7115
+ **P3. Never answer the same question twice**
7116
+ Check: Solutions/fixes are documented to prevent rediscovery
7117
+ Evidence: Solutions docs, post-mortems, troubleshooting guides, or a memory system
7118
+ Score 0: No solutions documentation
7119
+ Score 1: Scattered notes but no systematic approach
7120
+ Score 2: Structured solutions docs or integrated memory system
7121
+
7122
+ **P4. Knowledge is infrastructure**
7123
+ Check: Documentation is versioned alongside code
7124
+ Evidence: Specs, research, standards co-located in repo
7125
+ Score 0: Documentation lives outside version control
7126
+ Score 1: Some docs in repo but key knowledge is external
7127
+ Score 2: All project knowledge versioned in docs/
7128
+
7129
+ #### Pillar II: Implementation Feedbacks (Mechanical Verification) -- max 8 points
7130
+
7131
+ **P5. Test is specification**
7132
+ Check: Tests define behavior before or alongside implementation
7133
+ Evidence: Test files, coverage tooling, test-first patterns
7134
+ Score 0: No tests or minimal coverage
7135
+ Score 1: Tests exist but post-hoc or inconsistent
7136
+ Score 2: Comprehensive test suite with test-driven patterns
7137
+
7138
+ **P6. Constraints are multipliers**
7139
+ Check: Linters, type checkers, architectural rules configured and enforced
7140
+ Evidence: ESLint/pylint/clippy config, TypeScript strict mode, CI enforcement
7141
+ Score 0: No linting or type checking
7142
+ Score 1: Linter exists but not enforced in CI
7143
+ Score 2: Strict linting + type checking enforced in CI
7144
+
7145
+ **P7. Write feedback for machines**
7146
+ Check: Error messages, logs, and output are structured for agent consumption
7147
+ Evidence: Structured logging, clear error messages with context
7148
+ Score 0: Unstructured logs, generic error messages
7149
+ Score 1: Some structured logging but inconsistent
7150
+ Score 2: Structured logging throughout, remediation hints in errors
7151
+
7152
+ **P8. Fight entropy continuously**
7153
+ Check: Active maintenance processes prevent drift
7154
+ Evidence: Automated formatting, dependency updates, quality monitoring
7155
+ Score 0: No automated maintenance
7156
+ Score 1: Basic formatting but no proactive monitoring
7157
+ Score 2: Automated formatting + dependency updates + quality tracking
7158
+
7159
+ #### Pillar III: Mapping the Context (Navigable Structure) -- max 8 points
7160
+
7161
+ **P9. Map, not manual**
7162
+ Check: Entry point document provides a navigable map, not an encyclopedia
7163
+ Evidence: AGENTS.md, CLAUDE.md, or similar
7164
+ Score 0: No agent-facing entry point document
7165
+ Score 1: README exists but not optimized for agents
7166
+ Score 2: Dedicated AGENTS.md or CLAUDE.md with commands, structure, conventions
7167
+
7168
+ **P10. Explicit over implicit, always**
7169
+ Check: Types, naming, patterns are explicit
7170
+ Evidence: Type annotations, consistent naming, documented conventions
7171
+ Score 0: No type annotations, inconsistent naming
7172
+ Score 1: Some types but gaps, or undocumented conventions
7173
+ Score 2: Full type coverage, documented naming conventions
7174
+
7175
+ **P11. Modularity is non-negotiable**
7176
+ Check: Single responsibility per file, clear boundaries
7177
+ Evidence: File sizes, module organization, dependency structure
7178
+ Score 0: Monolithic files (>500 LOC common), unclear boundaries
7179
+ Score 1: Some modular structure but large files remain
7180
+ Score 2: Consistent small files, clear APIs, enforced boundaries
7181
+
7182
+ **P12. Structure in layers, govern by inheritance**
7183
+ Check: Layered architecture with explicit dependency rules
7184
+ Evidence: Layer separation, import rules, dependency graph
7185
+ Score 0: No discernible layering
7186
+ Score 1: Informal layers but no enforcement
7187
+ Score 2: Explicit layers with enforced dependency directions
7188
+
7189
+ #### Cross-Cutting -- max 6 points
7190
+
7191
+ **P13. Simplicity compounds**
7192
+ Check: Prefer boring technologies, minimal abstractions
7193
+ Evidence: Dependency count, abstraction depth
7194
+ Score 0: Over-engineered with many abstractions
7195
+ Score 1: Moderate complexity, some unnecessary abstractions
7196
+ Score 2: Minimal dependencies, straightforward patterns
7197
+
7198
+ **P14. Human designs the system, not the output**
7199
+ Check: Human effort in system design (tests, docs, constraints)
7200
+ Evidence: Quality of test harnesses, documentation, CI/CD
7201
+ Score 0: No investment in development infrastructure
7202
+ Score 1: Some tooling but gaps in key areas
7203
+ Score 2: Strong CI, testing framework, documentation system
7204
+
7205
+ **P15. Parallelize by decomposition**
7206
+ Check: Work can be split into independent units
7207
+ Evidence: Module independence, clear interfaces, minimal coupling
7208
+ Score 0: Tightly coupled, hard to work on independently
7209
+ Score 1: Some independent modules but shared state
7210
+ Score 2: Well-decomposed with clear interfaces
7211
+
7212
+ ### Audit Execution Steps
7213
+
7214
+ 1. Run \\\`npx ca search "agentic codebase"\\\` for relevant lessons
7215
+ 2. Detect project stack (see Stack Detection above)
7216
+ 3. Use Glob and Grep to check for evidence of each principle:
7217
+ - Glob for: docs/**, *.test.*, .eslintrc*, AGENTS.md, CLAUDE.md
7218
+ - Grep for: type annotations, structured logging, ADR format
7219
+ - Read key files: README, config files, sample source files
7220
+ 4. Score each principle (0-2) with specific evidence
7221
+ 5. Aggregate scores by pillar and compute total out of 30
7222
+ 6. Generate prioritized actions (score-0 first, then score-1)
7223
+ 7. Present report to user
7224
+
7225
+ ### Report Format
7226
+
7227
+ Present as markdown tables per pillar:
7228
+
7229
+ Pillar I: Codebase Memory -- X/8
7230
+ | # | Principle | Score | Evidence |
7231
+ |---|-----------|-------|----------|
7232
+ | P1 | Repository is the only truth | 0/1/2 | finding |
7233
+ ...repeat for all pillars with separator rows...
7234
+
7235
+ **Overall Score: X/30**
7236
+
7237
+ ### Priority Actions
7238
+ 1. [Score-0 items first, most impactful]
7239
+ 2. ...
7240
+
7241
+ After presenting, use \\\`AskUserQuestion\\\`: "Create a beads epic with issues for improvements?"
7242
+ If yes, create epic via bd create and individual issues.
7243
+
7244
+ ## Setup Methodology
7245
+
7246
+ ### Prerequisites
7247
+ Run the full audit first. Setup only addresses gaps found by the audit.
7248
+
7249
+ ### Setup Execution Steps
7250
+
7251
+ 1. Present audit findings summary
7252
+ 2. For each principle scored 0 or 1, propose a concrete action:
7253
+
7254
+ **P1/P4 gaps**: Create docs/ skeleton (INDEX.md, adr/, standards/) with real content from analysis
7255
+ **P2 gaps**: Create ADR template and first ADR from actual architecture analysis
7256
+ **P3 gaps**: Suggest solutions documentation structure
7257
+ **P5 gaps**: Suggest test framework setup based on detected stack
7258
+ **P6 gaps**: Suggest linter/type checker configuration for detected stack
7259
+ **P7 gaps**: Suggest structured logging patterns for detected stack
7260
+ **P9 gaps**: Generate AGENTS.md by analyzing actual codebase (build commands, structure, conventions)
7261
+ **P10 gaps**: Suggest type annotation and strict mode settings
7262
+ **P11 gaps**: Identify files >500 LOC, suggest refactoring targets
7263
+ **P12 gaps**: Document layer structure and suggest import lint rules (e.g., eslint-plugin-import boundaries, Rust mod visibility)
7264
+ **P13 gaps**: Flag over-abstraction (deep inheritance, excessive wrappers), suggest simplification targets
7265
+ **P14 gaps**: Suggest CI pipeline improvements, test harness setup, or pre-commit hooks for detected stack
7266
+ **P15 gaps**: Identify tightly coupled modules, suggest interface extraction for parallel workability
7267
+ **P8 gaps**: Suggest automated formatting (prettier/black/rustfmt), dependency update tooling (renovate/dependabot), and quality monitoring
7268
+
7269
+ 3. Before each action, use \\\`AskUserQuestion\\\`: "Create [file]? Preview: [content]"
7270
+ 4. Only create/modify files the user approves
7271
+ 5. Never overwrite existing files without explicit approval
7272
+
7273
+ ### Setup Completion Gate
7274
+ After all approved actions are applied, verify:
7275
+ - List all files created/modified during setup
7276
+ - Run quality gates if available (\\\`pnpm test\\\`, \\\`pnpm lint\\\`, or stack equivalent)
7277
+ - Confirm no existing files were overwritten without approval
7278
+ - Present summary: principles addressed, files created, remaining gaps
7279
+
7280
+ ## Memory Integration
7281
+
7282
+ - Before analysis: \\\`npx ca search "agentic codebase"\\\` for relevant lessons
7283
+ - After completing: offer \\\`npx ca learn\\\` to capture insights
7284
+
7285
+ ## Common Pitfalls
7286
+
7287
+ - Scoring too generously without specific evidence for score 2
7288
+ - Generating template content instead of analyzing the actual codebase
7289
+ - Overwriting existing files without asking
7290
+ - Not detecting the project stack before generating content
7291
+ - Creating too many files at once instead of prioritizing
7292
+ - Forgetting to offer beads epic creation after audit
7293
+
7294
+ ## Quality Criteria
7295
+
7296
+ - All 15 principles assessed with specific evidence
7297
+ - Scores justified with findings
7298
+ - Pillar totals and overall score calculated correctly
7299
+ - Actions prioritized (score-0 before score-1)
7300
+ - Stack detected and checks adapted accordingly
7301
+ - User consulted via AskUserQuestion at key decisions
7302
+ - Memory searched before analysis
7303
+ - Setup mode ran audit first
7304
+ - No files overwritten without approval
7305
+ - Generated content based on actual codebase analysis
6974
7306
  `
6975
7307
  };
6976
7308
  var PHASE_SKILL_REFERENCES = {
@@ -7596,12 +7928,12 @@ function registerPhaseSubcommands(phaseCheck, getDryRun, repoRoot) {
7596
7928
  console.log("Phase state cleaned.");
7597
7929
  });
7598
7930
  }
7599
- function registerPhaseCheckCommand(program2) {
7600
- const phaseCheck = program2.command("phase-check").description("Manage cook-it phase state").option("--dry-run", "Show what would be done without making changes");
7931
+ function registerPhaseCheckCommand(program) {
7932
+ const phaseCheck = program.command("phase-check").description("Manage cook-it phase state").option("--dry-run", "Show what would be done without making changes");
7601
7933
  const getDryRun = () => phaseCheck.opts().dryRun ?? false;
7602
7934
  const repoRoot = () => getRepoRoot();
7603
7935
  registerPhaseSubcommands(phaseCheck, getDryRun, repoRoot);
7604
- program2.command("phase-clean").description("Remove phase state file (alias for `phase-check clean`)").action(() => {
7936
+ program.command("phase-clean").description("Remove phase state file (alias for `phase-check clean`)").action(() => {
7605
7937
  cleanPhaseState(repoRoot());
7606
7938
  console.log("Phase state cleaned.");
7607
7939
  });
@@ -7884,8 +8216,8 @@ async function runStopAuditHook() {
7884
8216
  console.log(JSON.stringify({}));
7885
8217
  }
7886
8218
  }
7887
- function registerHooksCommand(program2) {
7888
- const hooksCommand = program2.command("hooks").description("Git hooks management");
8219
+ function registerHooksCommand(program) {
8220
+ const hooksCommand = program.command("hooks").description("Git hooks management");
7889
8221
  hooksCommand.command("run <hook>").description("Run a hook script (called by git/Claude hooks)").option("--json", "Output as JSON").action(async (hook, options) => {
7890
8222
  if (hook === "pre-commit") {
7891
8223
  if (options.json) {
@@ -8810,8 +9142,8 @@ function registerClaudeSubcommand(setupCommand) {
8810
9142
  });
8811
9143
  }
8812
9144
  init_embeddings();
8813
- function registerDownloadModelCommand(program2) {
8814
- program2.command("download-model").description("Download the embedding model for semantic search").option("--json", "Output as JSON").action(async (options) => {
9145
+ function registerDownloadModelCommand(program) {
9146
+ program.command("download-model").description("Download the embedding model for semantic search").option("--json", "Output as JSON").action(async (options) => {
8815
9147
  const alreadyExisted = isModelAvailable();
8816
9148
  if (alreadyExisted) {
8817
9149
  const modelPath2 = join(homedir(), ".node-llama-cpp", "models", MODEL_FILENAME);
@@ -8881,8 +9213,9 @@ async function handleModelAndEmbed(repoRoot, opts) {
8881
9213
  } catch {
8882
9214
  }
8883
9215
  try {
9216
+ const { withEmbedding: withEmbedding2 } = await Promise.resolve().then(() => (init_embeddings(), embeddings_exports));
8884
9217
  const { preWarmLessonEmbeddings: preWarmLessonEmbeddings2 } = await Promise.resolve().then(() => (init_prewarm(), prewarm_exports));
8885
- await preWarmLessonEmbeddings2(repoRoot);
9218
+ await withEmbedding2(async () => preWarmLessonEmbeddings2(repoRoot));
8886
9219
  } catch {
8887
9220
  }
8888
9221
  }
@@ -9015,8 +9348,8 @@ function printPnpmConfigStatus2(result) {
9015
9348
  console.log(` pnpm config: Added onlyBuiltDependencies [${result.added.join(", ")}]`);
9016
9349
  }
9017
9350
  }
9018
- function registerInitCommand(program2) {
9019
- program2.command("init").description("Initialize compound-agent in this repository").option("--skip-agents", "Skip AGENTS.md modification").option("--skip-hooks", "Skip git hooks installation").option("--skip-claude", "Skip Claude Code hooks installation").option("--skip-model", "Skip embedding model download").option("--json", "Output result as JSON").option("--update", "Run upgrade logic on existing install").action(async function(options) {
9351
+ function registerInitCommand(program) {
9352
+ program.command("init").description("Initialize compound-agent in this repository").option("--skip-agents", "Skip AGENTS.md modification").option("--skip-hooks", "Skip git hooks installation").option("--skip-claude", "Skip Claude Code hooks installation").option("--skip-model", "Skip embedding model download").option("--json", "Output result as JSON").option("--update", "Run upgrade logic on existing install").action(async function(options) {
9020
9353
  await initAction(this, options);
9021
9354
  });
9022
9355
  }
@@ -9243,14 +9576,14 @@ async function deleteAction(ids, options) {
9243
9576
  }
9244
9577
  }
9245
9578
  }
9246
- function registerCrudCommands(program2) {
9247
- program2.command("show <id>").description("Show details of a specific lesson").option("--json", "Output as JSON").action(async (id, options) => {
9579
+ function registerCrudCommands(program) {
9580
+ program.command("show <id>").description("Show details of a specific lesson").option("--json", "Output as JSON").action(async (id, options) => {
9248
9581
  await showAction(id, options);
9249
9582
  });
9250
- program2.command("update <id>").description("Update a lesson").option("--insight <text>", "Update insight").option("--trigger <text>", "Update trigger").option("--evidence <text>", "Update evidence").option("--severity <level>", "Update severity (low/medium/high)").option("--tags <tags>", "Update tags (comma-separated)").option("--confirmed <bool>", "Update confirmed status (true/false)").option("--json", "Output as JSON").action(async (id, options) => {
9583
+ program.command("update <id>").description("Update a lesson").option("--insight <text>", "Update insight").option("--trigger <text>", "Update trigger").option("--evidence <text>", "Update evidence").option("--severity <level>", "Update severity (low/medium/high)").option("--tags <tags>", "Update tags (comma-separated)").option("--confirmed <bool>", "Update confirmed status (true/false)").option("--json", "Output as JSON").action(async (id, options) => {
9251
9584
  await updateAction(id, options);
9252
9585
  });
9253
- program2.command("delete <ids...>").description("Soft delete lessons (creates tombstone)").option("--json", "Output as JSON").action(async (ids, options) => {
9586
+ program.command("delete <ids...>").description("Soft delete lessons (creates tombstone)").option("--json", "Output as JSON").action(async (ids, options) => {
9254
9587
  await deleteAction(ids, options);
9255
9588
  });
9256
9589
  }
@@ -9356,8 +9689,8 @@ var STATUS_ICONS = {
9356
9689
  fail: "FAIL",
9357
9690
  warn: "WARN"
9358
9691
  };
9359
- function registerDoctorCommand(program2) {
9360
- program2.command("doctor").description("Verify external dependencies and project health").action(async () => {
9692
+ function registerDoctorCommand(program) {
9693
+ program.command("doctor").description("Verify external dependencies and project health").action(async () => {
9361
9694
  const repoRoot = getRepoRoot();
9362
9695
  const checks = await runDoctor(repoRoot);
9363
9696
  console.log("Compound Agent Health Check:\n");
@@ -9383,8 +9716,8 @@ function registerDoctorCommand(program2) {
9383
9716
 
9384
9717
  // src/commands/management-invalidation.ts
9385
9718
  init_storage();
9386
- function registerInvalidationCommands(program2) {
9387
- program2.command("wrong <id>").description("Mark a lesson as invalid/wrong").option("-r, --reason <text>", "Reason for invalidation").action(async function(id, options) {
9719
+ function registerInvalidationCommands(program) {
9720
+ program.command("wrong <id>").description("Mark a lesson as invalid/wrong").option("-r, --reason <text>", "Reason for invalidation").action(async function(id, options) {
9388
9721
  const repoRoot = getRepoRoot();
9389
9722
  const { items } = await readMemoryItems(repoRoot);
9390
9723
  const lesson = items.find((l) => l.id === id);
@@ -9408,7 +9741,7 @@ function registerInvalidationCommands(program2) {
9408
9741
  console.log(` Reason: ${options.reason}`);
9409
9742
  }
9410
9743
  });
9411
- program2.command("validate <id>").description("Re-enable a previously invalidated lesson").action(async function(id) {
9744
+ program.command("validate <id>").description("Re-enable a previously invalidated lesson").action(async function(id) {
9412
9745
  const repoRoot = getRepoRoot();
9413
9746
  const { items } = await readMemoryItems(repoRoot);
9414
9747
  const lesson = items.find((l) => l.id === id);
@@ -9505,11 +9838,11 @@ async function importAction(file) {
9505
9838
  console.log(`Imported ${imported} ${lessonWord}`);
9506
9839
  }
9507
9840
  }
9508
- function registerIOCommands(program2) {
9509
- program2.command("export").description("Export lessons as JSON to stdout").option("--since <date>", "Only include lessons created after this date (ISO8601)").option("--tags <tags>", "Filter by tags (comma-separated, OR logic)").action(async (options) => {
9841
+ function registerIOCommands(program) {
9842
+ program.command("export").description("Export lessons as JSON to stdout").option("--since <date>", "Only include lessons created after this date (ISO8601)").option("--tags <tags>", "Filter by tags (comma-separated, OR logic)").action(async (options) => {
9510
9843
  await exportAction(options);
9511
9844
  });
9512
- program2.command("import <file>").description("Import lessons from a JSONL file").action(async (file) => {
9845
+ program.command("import <file>").description("Import lessons from a JSONL file").action(async (file) => {
9513
9846
  await importAction(file);
9514
9847
  });
9515
9848
  }
@@ -9609,14 +9942,14 @@ async function statsAction() {
9609
9942
  console.log(`Retrievals: ${totalRetrievals} total, ${avgRetrievals} avg per lesson`);
9610
9943
  console.log(`Storage: ${formatBytes(totalSize)} (index: ${formatBytes(indexSize)}, data: ${formatBytes(dataSize)})`);
9611
9944
  }
9612
- function registerMaintenanceCommands(program2) {
9613
- program2.command("compact").description("Compact lessons: remove tombstones and invalid records").option("-f, --force", "Run compaction even if below threshold").option("--dry-run", "Show what would be done without making changes").action(async (options) => {
9945
+ function registerMaintenanceCommands(program) {
9946
+ program.command("compact").description("Compact lessons: remove tombstones and invalid records").option("-f, --force", "Run compaction even if below threshold").option("--dry-run", "Show what would be done without making changes").action(async (options) => {
9614
9947
  await compactAction(options);
9615
9948
  });
9616
- program2.command("rebuild").description("Rebuild SQLite index from JSONL").option("-f, --force", "Force rebuild even if unchanged").action(async (options) => {
9949
+ program.command("rebuild").description("Rebuild SQLite index from JSONL").option("-f, --force", "Force rebuild even if unchanged").action(async (options) => {
9617
9950
  await rebuildAction(options);
9618
9951
  });
9619
- program2.command("stats").description("Show database health and statistics").action(async () => {
9952
+ program.command("stats").description("Show database health and statistics").action(async () => {
9620
9953
  await statsAction();
9621
9954
  });
9622
9955
  }
@@ -9736,8 +10069,8 @@ ${formattedLessons}
9736
10069
  }
9737
10070
  return output;
9738
10071
  }
9739
- function registerPrimeCommand(program2) {
9740
- program2.command("prime").description("Output context for Claude Code (guidelines + top lessons)").action(async () => {
10072
+ function registerPrimeCommand(program) {
10073
+ program.command("prime").description("Output context for Claude Code (guidelines + top lessons)").action(async () => {
9741
10074
  const output = await getPrimeContext();
9742
10075
  console.log(output);
9743
10076
  });
@@ -9748,8 +10081,8 @@ function formatFinding(finding) {
9748
10081
  const filePart = finding.file ? ` ${finding.file}` : "";
9749
10082
  return `${label} [${finding.source}]${filePart} -- ${finding.issue}`;
9750
10083
  }
9751
- function registerAuditCommands(program2) {
9752
- program2.command("audit").description("Run audit checks against the codebase").option("--json", "Output as JSON").option("--no-rules", "Skip rule checks").option("--no-patterns", "Skip pattern checks").option("--no-lessons", "Skip lesson checks").action(async function() {
10084
+ function registerAuditCommands(program) {
10085
+ program.command("audit").description("Run audit checks against the codebase").option("--json", "Output as JSON").option("--no-rules", "Skip rule checks").option("--no-patterns", "Skip pattern checks").option("--no-lessons", "Skip lesson checks").action(async function() {
9753
10086
  const repoRoot = getRepoRoot();
9754
10087
  const { quiet } = getGlobalOpts(this);
9755
10088
  const opts = this.opts();
@@ -9845,8 +10178,8 @@ async function disableReviewer(repoRoot, name) {
9845
10178
  }
9846
10179
 
9847
10180
  // src/commands/reviewer.ts
9848
- function registerReviewerCommand(program2) {
9849
- const reviewer = program2.command("reviewer").description("Manage external code reviewers (gemini, codex)");
10181
+ function registerReviewerCommand(program) {
10182
+ const reviewer = program.command("reviewer").description("Manage external code reviewers (gemini, codex)");
9850
10183
  reviewer.command("enable <name>").description(`Enable an external reviewer (${VALID_REVIEWERS.join(", ")})`).action(async (name) => {
9851
10184
  const repoRoot = getRepoRoot();
9852
10185
  try {
@@ -9895,8 +10228,8 @@ function registerReviewerCommand(program2) {
9895
10228
  }
9896
10229
  });
9897
10230
  }
9898
- function registerRulesCommands(program2) {
9899
- const rulesCmd = program2.command("rules").description("Run repository-defined rule checks");
10231
+ function registerRulesCommands(program) {
10232
+ const rulesCmd = program.command("rules").description("Run repository-defined rule checks");
9900
10233
  rulesCmd.command("check").description("Check codebase against rules in .claude/rules.json").action(function() {
9901
10234
  const repoRoot = getRepoRoot();
9902
10235
  const { quiet } = getGlobalOpts(this);
@@ -10007,8 +10340,8 @@ function formatTestSummary(summary, logPath) {
10007
10340
  lines.push(`LOG: Full output at ${logPath}`);
10008
10341
  return lines.join("\n");
10009
10342
  }
10010
- function registerTestSummaryCommand(program2) {
10011
- program2.command("test-summary").description("Run tests and output a compact summary").option("--fast", "Run pnpm test:fast instead of pnpm test").option("--cmd <command>", "Custom test command to run").action((options) => {
10343
+ function registerTestSummaryCommand(program) {
10344
+ program.command("test-summary").description("Run tests and output a compact summary").option("--fast", "Run pnpm test:fast instead of pnpm test").option("--cmd <command>", "Custom test command to run").action((options) => {
10012
10345
  const repoRoot = getRepoRoot();
10013
10346
  let testCmd = "pnpm test";
10014
10347
  if (options.cmd) {
@@ -10103,8 +10436,8 @@ var STATUS_LABEL = {
10103
10436
  pass: "PASS",
10104
10437
  fail: "FAIL"
10105
10438
  };
10106
- function registerVerifyGatesCommand(program2) {
10107
- program2.command("verify-gates <epic-id>").description("Verify workflow gates are satisfied before epic closure").action(async (epicId) => {
10439
+ function registerVerifyGatesCommand(program) {
10440
+ program.command("verify-gates <epic-id>").description("Verify workflow gates are satisfied before epic closure").action(async (epicId) => {
10108
10441
  try {
10109
10442
  const checks = await runVerifyGates(epicId, { repoRoot: getRepoRoot() });
10110
10443
  console.log(`Gate checks for epic ${epicId}:
@@ -10134,100 +10467,38 @@ function registerVerifyGatesCommand(program2) {
10134
10467
  }
10135
10468
 
10136
10469
  // src/changelog-data.ts
10137
- var CHANGELOG_RECENT = `## [1.6.2] - 2026-03-05
10470
+ var CHANGELOG_RECENT = `## [1.6.5] - 2026-03-07
10138
10471
 
10139
10472
  ### Fixed
10140
10473
 
10141
- - **Cook-it session banner**: The cook-it skill now instructs Claude to print the "Claude the Cooker" ASCII chef banner at the very start of every cooking session.
10474
+ - **Loop script bash 3.2 syntax error**: macOS ships bash 3.2 which misparses \`case\` pattern \`)\` inside \`$(...)\` as closing the subshell. Added \`(\` prefix to case patterns for POSIX compliance. Added \`/bin/bash -n\` regression test.
10475
+ - **Loop script \`--verbose\` flag**: \`--output-format=stream-json\` with \`-p\` requires \`--verbose\`, not \`--include-partial-messages\`.
10142
10476
 
10143
- ## [1.6.1] - 2026-03-05
10144
-
10145
- ### Changed
10146
-
10147
- - **Renamed brainstorm phase to spec-dev**: The \`/compound:brainstorm\` slash command is now \`/compound:spec-dev\`. The phase focuses on structured specification development using EARS notation, Mermaid diagrams, and Socratic dialogue rather than open-ended brainstorming. Old \`brainstorm.md\` command files are auto-cleaned during upgrade.
10148
- - **Integration test stability**: Reduced integration test parallelism (\`maxForks: 1\`) and increased timeouts to 60s to eliminate non-deterministic ETIMEDOUT failures under load.
10149
-
10150
- ### Added
10151
-
10152
- - **Spec reference file**: \`.claude/skills/compound/spec-dev/references/spec-guide.md\` provides quick-reference material for EARS patterns, Mermaid diagram selection, NL ambiguity detection, and trade-off documentation frameworks. Installed automatically during \`ca setup\`.
10153
- - **Hook error visibility**: Hook runners now log errors to stderr when \`CA_DEBUG\` environment variable is set, instead of silently swallowing all failures.
10154
- - **check-plan stdin safety**: \`ca check-plan\` now enforces a 30-second timeout and 1MB size limit when reading from stdin, preventing hangs in CI/CD environments.
10155
- - **Embed lock expiry**: Embedding lock files now expire after 1 hour as a safety valve against zombie processes holding locks indefinitely.
10156
- - **Phase-state backward compatibility**: Legacy \`lfg_active\` field in phase state files is automatically migrated to \`cookit_active\` on read.
10157
- - **clean-lessons scope messaging**: \`ca clean-lessons\` now reports when non-lesson items are excluded from analysis.
10477
+ ## [1.6.4] - 2026-03-07
10158
10478
 
10159
10479
  ### Fixed
10160
10480
 
10161
- - **Missing spec-guide.md**: The reference file was declared in skill templates and CHANGELOG but never generated during setup. Now installed alongside phase skills.
10162
- - **Upgrade cleanup for lfg.md**: Added \`lfg.md\` to deprecated commands list so \`ca setup --update\` removes stale lfg command files from upgraded repos.
10163
- - **Docs template terminology**: WORKFLOW.md template now uses "Spec Dev" instead of "Brainstorm" for Phase 1.
10164
- - **Test file naming**: Renamed \`brainstorm-phase.test.ts\` to \`spec-dev-phase.test.ts\` to match the refactored phase name.
10165
- - **Library bundle cleanup**: Moved CLI-only re-exports (\`registerWatchCommand\`, \`registerLoopCommands\`) out of the library barrel to eliminate unused import warnings in \`dist/index.js\`.
10166
- - **plan.test.ts embedding guard**: Added \`skipIf(skipEmbedding)\` to unguarded test that calls \`retrieveForPlan\` without mocking.
10167
- - **Agent template test count**: Updated setup.test.ts to expect 9 agent templates (was 8), matching the actual AGENT_TEMPLATES count after \`lessons-reviewer.md\` was added.
10168
-
10169
- ## [1.6.0] - 2026-03-02
10170
-
10171
- ### Added
10172
-
10173
- - **\`ca watch\` command**: Live pretty-printer for infinity loop trace files. Tails \`agent_logs/trace_*.jsonl\` and formats stream-json events (tool calls, text deltas, token usage, epic markers) with colored output. Supports \`--epic <id>\` to watch a specific epic and \`--no-follow\` for one-shot reads.
10174
- - **Stream-json micro logging**: Generated infinity loop scripts now use \`--output-format stream-json --include-partial-messages\` to capture structured JSONL event traces alongside the existing macro text log. Trace files written to \`agent_logs/trace_<epic>-<ts>.jsonl\` with \`.latest\` symlink for easy discovery.
10175
- - **\`/compound:learn-that\` slash command**: Conversation-aware lesson capture with user confirmation before saving
10176
- - **\`/compound:check-that\` slash command**: Search lessons and proactively apply them to current work
10177
- - **Eager knowledge embedding**: Knowledge chunks from \`docs/\` are now embedded for semantic search when the model is available
10178
- - \`ca index-docs --embed\` embeds chunks after indexing
10179
- - \`ca init\` now downloads the embedding model (with \`--skip-model\` opt-out) and installs the post-commit hook
10180
- - Background embedding spawns after \`ca init\`/\`ca setup\` so users can start working immediately
10181
- - PID-based lock file prevents concurrent embedding processes
10182
- - Status file (\`embed-status.json\`) tracks background embedding progress
10183
- - **New modules**: \`embed-chunks.ts\`, \`embed-lock.ts\`, \`embed-status.ts\`, \`embed-background.ts\` with full test coverage
10184
-
10185
- ### Removed
10186
-
10187
- - **\`/compound:learn\` slash command**: Replaced by \`/compound:learn-that\` with conversation-aware capture and user confirmation
10188
- - **\`ca worktree\` command family**: All five subcommands (\`create\`, \`merge\`, \`list\`, \`cleanup\`, \`wire-deps\`) removed. Claude Code now provides native \`EnterWorktree\` support. Running \`ca worktree\` prints a deprecation notice.
10189
- - **\`/compound:set-worktree\` slash command**: Use Claude Code's native worktree workflow instead.
10190
- - **Conditional Merge gate in \`verify-gates\`**: Only Review and Compound gates remain.
10191
- - **\`shortId\` utility**: Dead code after worktree removal, cleaned up.
10192
-
10193
- ### Changed
10481
+ - **Embedding memory leak**: Every \`npx ca search\` spawned a process that loaded the ~150MB native embedding model. Without explicit cleanup, native threads kept processes alive as zombies. During heavy usage (Claude Code subagents), 32+ processes accumulated = 4.5GB leaked.
10482
+ - \`withEmbedding(fn)\` scoped wrapper guarantees cleanup via try/finally. All 9 command-layer consumers migrated from manual try/finally to this single API.
10483
+ - ESLint rule \`require-embedding-cleanup\` catches any file importing \`embedText\`/\`getEmbedding\` without a cleanup function. Scoped to \`src/commands/\` and \`src/setup/\`.
10484
+ - \`cli-app.ts\` backstop: top-level finally in \`runProgram()\` catches anything the above two miss.
10485
+ - **\`embedText\` probe inside \`withEmbedding\` scope**: \`clean-lessons\` command was calling \`embedText\` outside of \`withEmbedding\`, leaking the model on every invocation.
10486
+ - **Agentic skill report format**: Markdown table was missing \`|---|\` separator row, rendering as plain text in some renderers.
10487
+ - **Agentic skill missing setup remediation**: 5 of 15 principles (P8, P12-P15) had no setup actions. Added concrete remediation guidance for each.
10488
+ - **Agentic skill missing completion gate**: Other skills have phase gates; the agentic skill was missing one. Added Setup Completion Gate with verification steps.
10489
+ - **Agentic skill stack-biased scoring**: Rubric was TypeScript-heavy. Added language-neutral scoring guidance (mypy, clippy, ruff equivalents).
10490
+ - **Agentic skill \`$ARGUMENTS\` dead code**: Mode is set by the calling command (\`/compound:agentic-audit\` or \`/compound:agentic-setup\`), not parsed from \`$ARGUMENTS\`.
10491
+ - **Docs template missing agentic commands**: \`SKILLS.md\` template now lists \`agentic-audit\` and \`agentic-setup\` in the command inventory.
10194
10492
 
10195
- - **Loop script uses piped stream splitting**: Claude invocation changed from \`&>\` capture to a \`tee | extract_text\` pipeline. Raw JSONL streams to trace file while extracted text feeds the macro log for marker detection. Backwards compatible \u2014 all existing markers (EPIC_COMPLETE, EPIC_FAILED, HUMAN_REQUIRED) still work.
10196
- - **\`ca setup --update\` now cleans deprecated paths**: Automatically removes stale worktree skill/command files from \`.claude/\` and \`.gemini/\` directories.
10197
- - **\`ca setup\` also cleans deprecated paths**: Fresh setup runs now remove stale files from prior versions.
10198
- - **SKILLS.md template**: Command inventory now lists all 11 slash commands (was 7).
10493
+ ## [1.6.3] - 2026-03-05
10199
10494
 
10200
- ### Fixed
10495
+ ### Changed
10201
10496
 
10202
- - **Eager embedding hardening** (production readiness fixes from triple review):
10203
- - **P0**: Background worker spawn now resolves \`dist/cli.js\` deterministically instead of relying on \`npx ca\` (which failed silently in dev/built contexts)
10204
- - **P0**: \`embed-worker\` command hidden from \`ca --help\` output
10205
- - **P1**: Stale lock recovery uses atomic delete-then-\`wx\` to prevent two processes both reclaiming
10206
- - **P1**: DB connection opened after lock acquisition to prevent leak on contention
10207
- - **P1**: \`--embed\` now throws when model unavailable (was silently returning 0)
10208
- - **P2**: Batch embedding (16 chunks per call) with per-batch SQLite transactions (was 1 fsync per row)
10209
- - **P2**: \`EmbedStatus\` rewritten as discriminated union; removed dead \`chunksTotal\` field
10210
- - **P2**: \`readLock\` validates JSON shape instead of blind \`as\` cast
10211
- - **P2**: Vector batch length assertion guards against short responses from embedding backend
10212
- - **P3**: Extracted \`indexAndSpawnEmbed()\` shared helper \u2014 \`init.ts\` and \`all.ts\` no longer duplicate logic
10213
- - **P3**: \`ca setup\` now prints feedback when background embedding spawns
10214
- - **P3**: \`filesErrored\` count shown in \`ca index-docs\` output
10215
- - **P3**: Barrel re-exports consolidated through \`./memory/knowledge/index.js\`
10216
- - **\`EPIC_ID_PATTERN\` duplication**: \`loop.ts\` now uses distinctly named \`LOOP_EPIC_ID_PATTERN\` to avoid confusion with the canonical pattern in \`cli-utils.ts\`.
10217
- - **Stale worktree lesson invalidated**: Memory item \`Ld204372e\` marked invalid to prevent irrelevant context injection.
10218
-
10219
- ### Performance
10220
-
10221
- - **Eliminate double model initialization**: \`ca search\` now uses \`isModelAvailable()\` (fs.existsSync, zero cost) instead of \`isModelUsable()\` which loaded the 278MB native model just to probe availability, then loaded it again for actual embedding
10222
- - **Bulk-read cached embeddings**: \`getCachedEmbeddingsBulk()\` replaces N individual \`getCachedEmbedding()\` SQLite queries with a single bulk read
10223
- - **Eliminate redundant JSONL parsing**: \`searchVector()\` and \`findSimilarLessons()\` now use \`readAllFromSqlite()\` after \`syncIfNeeded()\` instead of re-parsing the JSONL file
10224
- - **Float32Array consistency**: Lesson embedding path now keeps \`Float32Array\` from node-llama-cpp instead of converting via \`Array.from()\` (4x memory savings per vector)
10225
- - **Pre-warm lesson embedding cache**: \`ca init\` now pre-computes embeddings for all lessons with missing or stale cache entries, eliminating cold-start latency on first search
10226
- - **Graceful embedding fallback**: \`ca search\` falls back to keyword-only search on runtime embedding failures instead of crashing`;
10497
+ - **Cook-it session banner**: Replaced "Claude the Cooker" chef ASCII art with rscr's detailed front-view brain ASCII art ("Claw'd"). Brain features ANSI-colored augmentation zones: cyan neural interface (\`##\`), bright cyan signal crosslinks (\`::\`), green bio-circuits (\`%\`), magenta data highways (\`######\`), and yellow power nodes (\`@@\`).`;
10227
10498
 
10228
10499
  // src/commands/about.ts
10229
- function registerAboutCommand(program2) {
10230
- program2.command("about").description("Show version, animation, and recent changelog").action(async () => {
10500
+ function registerAboutCommand(program) {
10501
+ program.command("about").description("Show version, animation, and recent changelog").action(async () => {
10231
10502
  if (process.stdout.isTTY) {
10232
10503
  await playInstallBanner();
10233
10504
  } else {
@@ -10239,22 +10510,23 @@ function registerAboutCommand(program2) {
10239
10510
  }
10240
10511
 
10241
10512
  // src/commands/knowledge.ts
10513
+ init_embeddings();
10242
10514
  init_sqlite_knowledge();
10243
10515
  var MAX_DISPLAY_TEXT = 200;
10244
- function registerKnowledgeCommand(program2) {
10245
- program2.command("knowledge <query>").description("Search docs knowledge base").option("-n, --limit <number>", "Maximum results", "6").action(async function(query, opts) {
10516
+ function registerKnowledgeCommand(program) {
10517
+ program.command("knowledge <query>").description("Search docs knowledge base").option("-n, --limit <number>", "Maximum results", "6").action(async function(query, opts) {
10246
10518
  const globalOpts = getGlobalOpts(this);
10519
+ let limit;
10520
+ try {
10521
+ limit = parseLimit(opts.limit, "limit");
10522
+ } catch (err) {
10523
+ const message = err instanceof Error ? err.message : "Invalid limit";
10524
+ console.error(formatError("knowledge", "INVALID_LIMIT", message, "Use -n with a positive integer"));
10525
+ process.exitCode = 1;
10526
+ return;
10527
+ }
10528
+ const repoRoot = getRepoRoot();
10247
10529
  try {
10248
- let limit;
10249
- try {
10250
- limit = parseLimit(opts.limit, "limit");
10251
- } catch (err) {
10252
- const message = err instanceof Error ? err.message : "Invalid limit";
10253
- console.error(formatError("knowledge", "INVALID_LIMIT", message, "Use -n with a positive integer"));
10254
- process.exitCode = 1;
10255
- return;
10256
- }
10257
- const repoRoot = getRepoRoot();
10258
10530
  openKnowledgeDb(repoRoot);
10259
10531
  if (getChunkCount(repoRoot) === 0) {
10260
10532
  try {
@@ -10270,7 +10542,7 @@ function registerKnowledgeCommand(program2) {
10270
10542
  out.info(`Auto-index failed (${msg}). Run manually: npx ca index-docs`);
10271
10543
  }
10272
10544
  }
10273
- const results = await searchKnowledge(repoRoot, query, { limit });
10545
+ const results = await withEmbedding(async () => searchKnowledge(repoRoot, query, { limit }));
10274
10546
  if (results.length === 0) {
10275
10547
  out.info("No matching results found.");
10276
10548
  return;
@@ -10298,15 +10570,15 @@ function registerKnowledgeCommand(program2) {
10298
10570
  // src/commands/knowledge-index.ts
10299
10571
  init_embeddings();
10300
10572
  init_sqlite_knowledge();
10301
- function registerKnowledgeIndexCommand(program2) {
10302
- program2.command("index-docs").description("Index docs/ directory into knowledge base").option("--force", "Re-index all files (ignore cache)").option("--embed", "Embed chunks for semantic search").action(async function(options) {
10573
+ function registerKnowledgeIndexCommand(program) {
10574
+ program.command("index-docs").description("Index docs/ directory into knowledge base").option("--force", "Re-index all files (ignore cache)").option("--embed", "Embed chunks for semantic search").action(async function(options) {
10303
10575
  const repoRoot = getRepoRoot();
10304
10576
  out.info("Indexing docs/ directory...");
10305
10577
  try {
10306
- const result = await indexDocs(repoRoot, {
10578
+ const result = await withEmbedding(async () => indexDocs(repoRoot, {
10307
10579
  force: options.force,
10308
10580
  embed: options.embed
10309
- });
10581
+ }));
10310
10582
  const skippedPart = result.filesSkipped > 0 ? ` (${result.filesSkipped} skipped)` : "";
10311
10583
  const deletedPart = result.chunksDeleted > 0 ? `, ${result.chunksDeleted} deleted` : "";
10312
10584
  const duration = (result.durationMs / 1e3).toFixed(1);
@@ -10321,11 +10593,10 @@ function registerKnowledgeIndexCommand(program2) {
10321
10593
  }
10322
10594
  out.info(`Duration: ${duration}s`);
10323
10595
  } finally {
10324
- unloadEmbedding();
10325
10596
  closeKnowledgeDb();
10326
10597
  }
10327
10598
  });
10328
- program2.command("embed-worker <repoRoot>", { hidden: true }).description("Internal: background embedding worker").action(async (repoRoot) => {
10599
+ program.command("embed-worker <repoRoot>", { hidden: true }).description("Internal: background embedding worker").action(async (repoRoot) => {
10329
10600
  const { runBackgroundEmbed: runBackgroundEmbed2 } = await Promise.resolve().then(() => (init_embed_background(), embed_background_exports));
10330
10601
  await runBackgroundEmbed2(repoRoot);
10331
10602
  });
@@ -10392,22 +10663,21 @@ async function cleanLessonsAction() {
10392
10663
  process.exitCode = 1;
10393
10664
  return;
10394
10665
  }
10395
- try {
10396
- await embedText("test");
10397
- } catch (e) {
10398
- console.error(
10399
- formatError(
10400
- "clean-lessons",
10401
- "MODEL_UNUSABLE",
10402
- `Embedding model failed to initialize: ${e instanceof Error ? e.message : String(e)}`,
10403
- "Check model compatibility"
10404
- )
10405
- );
10406
- process.exitCode = 1;
10407
- unloadEmbedding();
10408
- return;
10409
- }
10410
- try {
10666
+ await withEmbedding(async () => {
10667
+ try {
10668
+ await embedText("test");
10669
+ } catch (e) {
10670
+ console.error(
10671
+ formatError(
10672
+ "clean-lessons",
10673
+ "MODEL_UNUSABLE",
10674
+ `Embedding model failed to initialize: ${e instanceof Error ? e.message : String(e)}`,
10675
+ "Check model compatibility"
10676
+ )
10677
+ );
10678
+ process.exitCode = 1;
10679
+ return;
10680
+ }
10411
10681
  await syncIfNeeded(repoRoot);
10412
10682
  const { items } = await readMemoryItems(repoRoot);
10413
10683
  const activeItems = items.filter((item) => !item.invalidatedAt && item.type === "lesson");
@@ -10420,12 +10690,10 @@ async function cleanLessonsAction() {
10420
10690
  return;
10421
10691
  }
10422
10692
  printReport(pairs);
10423
- } finally {
10424
- unloadEmbedding();
10425
- }
10693
+ });
10426
10694
  }
10427
- function registerCleanLessonsCommand(program2) {
10428
- program2.command("clean-lessons").description("Analyze lessons for semantic duplicates and contradictions").action(cleanLessonsAction);
10695
+ function registerCleanLessonsCommand(program) {
10696
+ program.command("clean-lessons").description("Analyze lessons for semantic duplicates and contradictions").action(cleanLessonsAction);
10429
10697
  }
10430
10698
 
10431
10699
  // src/commands/capture.ts
@@ -10552,9 +10820,9 @@ async function checkSimilarityPostCapture(repoRoot, item) {
10552
10820
  if (!isModelAvailable2()) return;
10553
10821
  const { syncIfNeeded: syncIfNeeded2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
10554
10822
  const { findSimilarLessons: findSimilarLessons2 } = await Promise.resolve().then(() => (init_vector(), vector_exports));
10555
- const { embedText: embedText2, unloadEmbedding: unloadEmbedding2 } = await Promise.resolve().then(() => (init_nomic(), nomic_exports));
10823
+ const { embedText: embedText2, withEmbedding: withEmbedding2 } = await Promise.resolve().then(() => (init_nomic(), nomic_exports));
10556
10824
  const chalk6 = await import('chalk');
10557
- try {
10825
+ await withEmbedding2(async () => {
10558
10826
  await embedText2("test");
10559
10827
  await syncIfNeeded2(repoRoot);
10560
10828
  const similar = await findSimilarLessons2(repoRoot, item.insight, { excludeId: item.id });
@@ -10567,9 +10835,7 @@ async function checkSimilarityPostCapture(repoRoot, item) {
10567
10835
  console.log("");
10568
10836
  console.log(`Run ${chalk6.default.bold("'npx ca clean-lessons'")} to review and resolve.`);
10569
10837
  }
10570
- } finally {
10571
- unloadEmbedding2();
10572
- }
10838
+ });
10573
10839
  } catch (err) {
10574
10840
  if (process.env["CA_DEBUG"]) {
10575
10841
  process.stderr.write(`[CA_DEBUG] checkSimilarityPostCapture catch: ${err instanceof Error ? err.message : String(err)}
@@ -10697,17 +10963,18 @@ async function handleCapture(cmd, options) {
10697
10963
  outputCapturePreview(lesson);
10698
10964
  }
10699
10965
  }
10700
- function registerCaptureCommands(program2) {
10701
- program2.command("learn <insight>").description("Capture a new memory item (lesson, solution, pattern, or preference)").option("-t, --trigger <text>", "What triggered this lesson").option("--tags <tags>", "Comma-separated tags", "").option("-s, --severity <level>", "Lesson severity: high, medium, low").option("-y, --yes", "Skip confirmation").option("--citation <file:line>", "Source file (optionally with :line number)").option("--citation-commit <hash>", "Git commit hash for citation").option("--type <type>", "Memory item type: lesson, solution, pattern, preference", "lesson").option("--pattern-bad <code>", "Bad pattern example (required when --type pattern)").option("--pattern-good <code>", "Good pattern example (required when --type pattern)").action(async function(insight, options) {
10966
+ function registerCaptureCommands(program) {
10967
+ program.command("learn <insight>").description("Capture a new memory item (lesson, solution, pattern, or preference)").option("-t, --trigger <text>", "What triggered this lesson").option("--tags <tags>", "Comma-separated tags", "").option("-s, --severity <level>", "Lesson severity: high, medium, low").option("-y, --yes", "Skip confirmation").option("--citation <file:line>", "Source file (optionally with :line number)").option("--citation-commit <hash>", "Git commit hash for citation").option("--type <type>", "Memory item type: lesson, solution, pattern, preference", "lesson").option("--pattern-bad <code>", "Bad pattern example (required when --type pattern)").option("--pattern-good <code>", "Good pattern example (required when --type pattern)").action(async function(insight, options) {
10702
10968
  await handleLearn(this, insight, options);
10703
10969
  });
10704
- program2.command("detect").description("Detect learning triggers from input").requiredOption("--input <file>", "Path to JSON input file").option("--save", "Save proposed lesson (requires --yes)").option("-y, --yes", "Confirm save (required with --save)").option("--json", "Output result as JSON").action(async (options) => {
10970
+ program.command("detect").description("Detect learning triggers from input").requiredOption("--input <file>", "Path to JSON input file").option("--save", "Save proposed lesson (requires --yes)").option("-y, --yes", "Confirm save (required with --save)").option("--json", "Output result as JSON").action(async (options) => {
10705
10971
  await handleDetect(options);
10706
10972
  });
10707
- program2.command("capture").description("Capture a lesson from trigger/insight or input file").option("-t, --trigger <text>", "What triggered this lesson").option("-i, --insight <text>", "The insight or lesson learned").option("--input <file>", "Path to JSON input file (alternative to trigger/insight)").option("--json", "Output result as JSON").option("-y, --yes", "Skip confirmation and save immediately").action(async function(options) {
10973
+ program.command("capture").description("Capture a lesson from trigger/insight or input file").option("-t, --trigger <text>", "What triggered this lesson").option("-i, --insight <text>", "The insight or lesson learned").option("--input <file>", "Path to JSON input file (alternative to trigger/insight)").option("--json", "Output result as JSON").option("-y, --yes", "Skip confirmation and save immediately").action(async function(options) {
10708
10974
  await handleCapture(this, options);
10709
10975
  });
10710
10976
  }
10977
+ init_embeddings();
10711
10978
  init_storage();
10712
10979
  init_search2();
10713
10980
  function parseLimitOrNull(rawLimit, optionName, commandName) {
@@ -10803,23 +11070,23 @@ async function searchAction(cmd, query, options) {
10803
11070
  }
10804
11071
  const { verbose, quiet } = getGlobalOpts(cmd);
10805
11072
  await syncIfNeeded(repoRoot);
10806
- let results;
10807
- if (isModelAvailable()) {
10808
- try {
10809
- const candidateLimit = limit * CANDIDATE_MULTIPLIER;
10810
- const [vectorResults, keywordResults] = await Promise.all([
10811
- searchVector(repoRoot, query, { limit: candidateLimit }),
10812
- searchKeywordScored(repoRoot, query, candidateLimit)
10813
- ]);
10814
- const merged = mergeHybridResults(vectorResults, keywordResults, { minScore: MIN_HYBRID_SCORE });
10815
- const ranked = rankLessons(merged);
10816
- results = ranked.slice(0, limit).map((r) => r.lesson);
10817
- } catch {
10818
- results = await searchKeyword(repoRoot, query, limit);
11073
+ const results = await withEmbedding(async () => {
11074
+ if (isModelAvailable()) {
11075
+ try {
11076
+ const candidateLimit = limit * CANDIDATE_MULTIPLIER;
11077
+ const [vectorResults, keywordResults] = await Promise.all([
11078
+ searchVector(repoRoot, query, { limit: candidateLimit }),
11079
+ searchKeywordScored(repoRoot, query, candidateLimit)
11080
+ ]);
11081
+ const merged = mergeHybridResults(vectorResults, keywordResults, { minScore: MIN_HYBRID_SCORE });
11082
+ const ranked = rankLessons(merged);
11083
+ return ranked.slice(0, limit).map((r) => r.lesson);
11084
+ } catch {
11085
+ return await searchKeyword(repoRoot, query, limit);
11086
+ }
10819
11087
  }
10820
- } else {
10821
- results = await searchKeyword(repoRoot, query, limit);
10822
- }
11088
+ return await searchKeyword(repoRoot, query, limit);
11089
+ });
10823
11090
  if (results.length > 0) {
10824
11091
  incrementRetrievalCount(repoRoot, results.map((lesson) => lesson.id));
10825
11092
  }
@@ -10953,7 +11220,7 @@ async function checkPlanAction(cmd, options) {
10953
11220
  return;
10954
11221
  }
10955
11222
  try {
10956
- const result = await retrieveForPlan(repoRoot, planText, limit);
11223
+ const result = await withEmbedding(async () => retrieveForPlan(repoRoot, planText, limit));
10957
11224
  if (options.json) {
10958
11225
  outputCheckPlanJson(result.lessons);
10959
11226
  return;
@@ -10977,48 +11244,48 @@ async function checkPlanAction(cmd, options) {
10977
11244
  process.exitCode = 1;
10978
11245
  }
10979
11246
  }
10980
- function registerRetrievalCommands(program2) {
10981
- program2.command("search <query>").description("Search lessons").option("-n, --limit <number>", "Maximum results", DEFAULT_SEARCH_LIMIT).action(async function(query, options) {
11247
+ function registerRetrievalCommands(program) {
11248
+ program.command("search <query>").description("Search lessons").option("-n, --limit <number>", "Maximum results", DEFAULT_SEARCH_LIMIT).action(async function(query, options) {
10982
11249
  await searchAction(this, query, options);
10983
11250
  });
10984
- program2.command("list").description("List all lessons").option("-n, --limit <number>", "Maximum results", DEFAULT_LIST_LIMIT).option("--invalidated", "Show only invalidated lessons").action(async function(options) {
11251
+ program.command("list").description("List all lessons").option("-n, --limit <number>", "Maximum results", DEFAULT_LIST_LIMIT).option("--invalidated", "Show only invalidated lessons").action(async function(options) {
10985
11252
  await listAction(this, options);
10986
11253
  });
10987
- program2.command("load-session").description("Load high-severity lessons for session context").option("--json", "Output as JSON").action(async function(options) {
11254
+ program.command("load-session").description("Load high-severity lessons for session context").option("--json", "Output as JSON").action(async function(options) {
10988
11255
  await loadSessionAction(this, options);
10989
11256
  });
10990
- program2.command("check-plan").description("Check plan against relevant lessons").option("--plan <text>", "Plan text to check").option("--json", "Output as JSON").option("-n, --limit <number>", "Maximum results", DEFAULT_CHECK_PLAN_LIMIT).action(async function(options) {
11257
+ program.command("check-plan").description("Check plan against relevant lessons").option("--plan <text>", "Plan text to check").option("--json", "Output as JSON").option("-n, --limit <number>", "Maximum results", DEFAULT_CHECK_PLAN_LIMIT).action(async function(options) {
10991
11258
  await checkPlanAction(this, options);
10992
11259
  });
10993
11260
  }
10994
11261
 
10995
11262
  // src/commands/index.ts
10996
- function registerSetupCommands(program2) {
10997
- registerInitCommand(program2);
10998
- registerHooksCommand(program2);
10999
- const setupCommand = program2.command("setup");
11263
+ function registerSetupCommands(program) {
11264
+ registerInitCommand(program);
11265
+ registerHooksCommand(program);
11266
+ const setupCommand = program.command("setup");
11000
11267
  registerSetupAllCommand(setupCommand);
11001
11268
  registerClaudeSubcommand(setupCommand);
11002
11269
  registerGeminiSubcommand(setupCommand);
11003
- registerDownloadModelCommand(program2);
11004
- }
11005
- function registerManagementCommands(program2) {
11006
- registerInvalidationCommands(program2);
11007
- registerMaintenanceCommands(program2);
11008
- registerIOCommands(program2);
11009
- registerPrimeCommand(program2);
11010
- registerCrudCommands(program2);
11011
- registerAuditCommands(program2);
11012
- registerDoctorCommand(program2);
11013
- registerReviewerCommand(program2);
11014
- registerRulesCommands(program2);
11015
- registerTestSummaryCommand(program2);
11016
- registerVerifyGatesCommand(program2);
11017
- registerAboutCommand(program2);
11018
- registerKnowledgeCommand(program2);
11019
- registerKnowledgeIndexCommand(program2);
11020
- registerCleanLessonsCommand(program2);
11021
- program2.command("worktree").description("(removed) Use Claude Code native worktree support").action(() => {
11270
+ registerDownloadModelCommand(program);
11271
+ }
11272
+ function registerManagementCommands(program) {
11273
+ registerInvalidationCommands(program);
11274
+ registerMaintenanceCommands(program);
11275
+ registerIOCommands(program);
11276
+ registerPrimeCommand(program);
11277
+ registerCrudCommands(program);
11278
+ registerAuditCommands(program);
11279
+ registerDoctorCommand(program);
11280
+ registerReviewerCommand(program);
11281
+ registerRulesCommands(program);
11282
+ registerTestSummaryCommand(program);
11283
+ registerVerifyGatesCommand(program);
11284
+ registerAboutCommand(program);
11285
+ registerKnowledgeCommand(program);
11286
+ registerKnowledgeIndexCommand(program);
11287
+ registerCleanLessonsCommand(program);
11288
+ program.command("worktree").description("(removed) Use Claude Code native worktree support").action(() => {
11022
11289
  console.error("ca worktree has been removed. Use Claude Code's native EnterWorktree support instead.");
11023
11290
  process.exitCode = 1;
11024
11291
  });
@@ -11094,7 +11361,7 @@ get_next_epic() {
11094
11361
  if [ -n "$EPIC_IDS" ]; then
11095
11362
  # From explicit list, find first still-open epic not yet processed
11096
11363
  for epic_id in $EPIC_IDS; do
11097
- case " $PROCESSED " in *" $epic_id "*) continue ;; esac
11364
+ case " $PROCESSED " in (*" $epic_id "*) continue ;; esac
11098
11365
  local status
11099
11366
  status=$(bd show "$epic_id" --json 2>/dev/null | parse_json '.status' 2>/dev/null || echo "")
11100
11367
  if [ "$status" = "open" ]; then
@@ -11108,7 +11375,7 @@ get_next_epic() {
11108
11375
  local epic_id
11109
11376
  if [ "$HAS_JQ" = true ]; then
11110
11377
  epic_id=$(bd list --type=epic --ready --json --limit=10 2>/dev/null | jq -r '.[].id' 2>/dev/null | while read -r id; do
11111
- case " $PROCESSED " in *" $id "*) continue ;; esac
11378
+ case " $PROCESSED " in (*" $id "*) continue ;; esac
11112
11379
  echo "$id"
11113
11380
  break
11114
11381
  done)
@@ -11265,7 +11532,7 @@ while true; do
11265
11532
  claude --dangerously-skip-permissions \\
11266
11533
  --model "$MODEL" \\
11267
11534
  --output-format stream-json \\
11268
- --include-partial-messages \\
11535
+ --verbose \\
11269
11536
  -p "$PROMPT" \\
11270
11537
  2>"$LOGFILE.stderr" | tee "$TRACEFILE" | extract_text > "$LOGFILE" || true
11271
11538
 
@@ -11367,8 +11634,8 @@ async function handleLoop(cmd, options) {
11367
11634
  out.info("Run it with: " + outputPath);
11368
11635
  out.info("Preview with: LOOP_DRY_RUN=1 " + outputPath);
11369
11636
  }
11370
- function registerLoopCommands(program2) {
11371
- program2.command("loop").description("Generate infinity loop script for epic tasks").option("--epics <ids...>", "Specific epic IDs to process").option("-o, --output <path>", "Output script path", "./infinity-loop.sh").option("--max-retries <n>", "Max retries per epic on failure", "1").option("--model <model>", "Claude model to use", "claude-opus-4-6").option("--force", "Overwrite existing script").action(async function(options) {
11637
+ function registerLoopCommands(program) {
11638
+ program.command("loop").description("Generate infinity loop script for epic tasks").option("--epics <ids...>", "Specific epic IDs to process").option("-o, --output <path>", "Output script path", "./infinity-loop.sh").option("--max-retries <n>", "Max retries per epic on failure", "1").option("--model <model>", "Claude model to use", "claude-opus-4-6").option("--force", "Overwrite existing script").action(async function(options) {
11372
11639
  await handleLoop(this, options);
11373
11640
  });
11374
11641
  }
@@ -11475,15 +11742,15 @@ async function tailFile(filePath, follow) {
11475
11742
  const child = spawn("tail", ["-f", "-n", "+1", filePath], { stdio: ["ignore", "pipe", "ignore"] });
11476
11743
  const rl2 = createInterface({ input: child.stdout });
11477
11744
  rl2.on("line", processLine);
11478
- const cleanup2 = () => {
11745
+ const cleanup = () => {
11479
11746
  child.kill("SIGTERM");
11480
11747
  };
11481
- process.on("SIGINT", cleanup2);
11482
- process.on("SIGTERM", cleanup2);
11748
+ process.on("SIGINT", cleanup);
11749
+ process.on("SIGTERM", cleanup);
11483
11750
  return new Promise((done) => {
11484
11751
  child.on("close", () => {
11485
- process.off("SIGINT", cleanup2);
11486
- process.off("SIGTERM", cleanup2);
11752
+ process.off("SIGINT", cleanup);
11753
+ process.off("SIGTERM", cleanup);
11487
11754
  done();
11488
11755
  });
11489
11756
  });
@@ -11538,8 +11805,8 @@ async function handleWatch(cmd, options) {
11538
11805
  out.info(`Watching: ${traceFile}`);
11539
11806
  await tailFile(traceFile, follow);
11540
11807
  }
11541
- function registerWatchCommand(program2) {
11542
- program2.command("watch").description("Tail and pretty-print live trace from infinity loop sessions").option("--epic <id>", "Watch a specific epic trace").option("--no-follow", "Print existing trace and exit (no live tail)").action(async function(options) {
11808
+ function registerWatchCommand(program) {
11809
+ program.command("watch").description("Tail and pretty-print live trace from infinity loop sessions").option("--epic <id>", "Watch a specific epic trace").option("--no-follow", "Print existing trace and exit (no live tail)").action(async function(options) {
11543
11810
  await handleWatch(this, options);
11544
11811
  });
11545
11812
  }
@@ -11587,7 +11854,8 @@ function commandNeedsSqlite(cmd) {
11587
11854
  return false;
11588
11855
  }
11589
11856
 
11590
- // src/cli.ts
11857
+ // src/cli-app.ts
11858
+ init_embeddings();
11591
11859
  init_storage();
11592
11860
  function detectPackageManager(cwd) {
11593
11861
  if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
@@ -11651,46 +11919,62 @@ function printBuildToolsHint() {
11651
11919
  }
11652
11920
  }
11653
11921
 
11654
- // src/cli.ts
11655
- function cleanup() {
11922
+ // src/cli-app.ts
11923
+ async function cleanupCliResources() {
11924
+ try {
11925
+ await unloadEmbeddingResources();
11926
+ } catch {
11927
+ }
11656
11928
  try {
11657
11929
  closeDb();
11658
11930
  } catch {
11659
11931
  }
11660
11932
  }
11661
- process.on("SIGINT", () => {
11662
- cleanup();
11663
- process.exit(0);
11664
- });
11665
- process.on("SIGTERM", () => {
11666
- cleanup();
11667
- process.exit(0);
11668
- });
11669
- var program = new Command();
11670
- program.option("-v, --verbose", "Show detailed output").option("-q, --quiet", "Suppress non-essential output");
11671
- program.name("ca").description("Semantically-intelligent workflow plugin for Claude Code").version(VERSION);
11672
- registerCaptureCommands(program);
11673
- registerRetrievalCommands(program);
11674
- registerManagementCommands(program);
11675
- registerSetupCommands(program);
11676
- registerCompoundCommands(program);
11677
- registerLoopCommands(program);
11678
- registerWatchCommand(program);
11679
- registerPhaseCheckCommand(program);
11680
- program.hook("preAction", (_thisCommand, actionCommand) => {
11681
- if (!commandNeedsSqlite(actionCommand)) return;
11682
- try {
11683
- ensureSqliteAvailable();
11684
- } catch (err) {
11685
- let root;
11933
+ function attachSignalHandlers() {
11934
+ const handleSignal = (exitCode) => {
11935
+ void cleanupCliResources().finally(() => process.exit(exitCode));
11936
+ };
11937
+ process.on("SIGINT", () => handleSignal(0));
11938
+ process.on("SIGTERM", () => handleSignal(0));
11939
+ }
11940
+ function createProgram() {
11941
+ const program = new Command();
11942
+ program.option("-v, --verbose", "Show detailed output").option("-q, --quiet", "Suppress non-essential output");
11943
+ program.name("ca").description("Semantically-intelligent workflow plugin for Claude Code").version(VERSION);
11944
+ registerCaptureCommands(program);
11945
+ registerRetrievalCommands(program);
11946
+ registerManagementCommands(program);
11947
+ registerSetupCommands(program);
11948
+ registerCompoundCommands(program);
11949
+ registerLoopCommands(program);
11950
+ registerWatchCommand(program);
11951
+ registerPhaseCheckCommand(program);
11952
+ program.hook("preAction", (_thisCommand, actionCommand) => {
11953
+ if (!commandNeedsSqlite(actionCommand)) return;
11686
11954
  try {
11687
- root = getRepoRoot();
11688
- } catch {
11955
+ ensureSqliteAvailable();
11956
+ } catch (err) {
11957
+ let root;
11958
+ try {
11959
+ root = getRepoRoot();
11960
+ } catch {
11961
+ }
11962
+ printNativeBuildDiagnostic(err, root);
11963
+ process.exit(1);
11689
11964
  }
11690
- printNativeBuildDiagnostic(err, root);
11691
- process.exit(1);
11965
+ });
11966
+ return program;
11967
+ }
11968
+ async function runProgram(program, argv = process.argv) {
11969
+ try {
11970
+ await program.parseAsync(argv);
11971
+ } finally {
11972
+ await cleanupCliResources();
11692
11973
  }
11693
- });
11694
- program.parse();
11974
+ }
11975
+
11976
+ // src/cli.ts
11977
+ attachSignalHandlers();
11978
+ await runProgram(createProgram());
11695
11979
  //# sourceMappingURL=cli.js.map
11696
11980
  //# sourceMappingURL=cli.js.map