peaks-loop 4.0.31 → 4.0.33

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README-en.md +3 -4
  3. package/README.md +20 -28
  4. package/dist/cli/commands/_register.js +4 -3
  5. package/dist/cli/commands/audit-commands.js +12 -39
  6. package/dist/cli/commands/capability-commands.d.ts +6 -0
  7. package/dist/cli/commands/capability-commands.js +20 -2
  8. package/dist/cli/commands/ecc-commands.js +3 -2
  9. package/dist/cli/commands/project-commands.js +1 -1
  10. package/dist/cli/commands/shadcn-commands.d.ts +32 -0
  11. package/dist/cli/commands/shadcn-commands.js +120 -0
  12. package/dist/cli/commands/slice-commands.js +1 -4
  13. package/dist/services/audit/static-service.d.ts +1 -1
  14. package/dist/services/audit/static-service.js +1 -1
  15. package/dist/services/code-review/ecc-bridge.d.ts +94 -15
  16. package/dist/services/code-review/ecc-bridge.js +133 -13
  17. package/dist/services/dashboard/project-dashboard-service.d.ts +0 -7
  18. package/dist/services/dashboard/project-dashboard-service.js +1 -9
  19. package/dist/services/preferences/preferences-types.d.ts +16 -7
  20. package/dist/services/recommendations/capability-seed-items.js +0 -1
  21. package/dist/services/recommendations/installed-capability-detector.d.ts +33 -0
  22. package/dist/services/recommendations/installed-capability-detector.js +76 -0
  23. package/dist/services/scan/orphan-service.js +1 -1
  24. package/dist/services/slice/multi-pass-orchestrator.js +0 -13
  25. package/dist/services/slice/slice-decompose-import-edges.d.ts +2 -2
  26. package/dist/services/slice/slice-decompose-import-edges.js +1 -29
  27. package/dist/services/slice/slice-decompose-runners.d.ts +2 -4
  28. package/dist/services/slice/slice-decompose-runners.js +1 -23
  29. package/dist/services/slice/slice-decompose-service.d.ts +3 -4
  30. package/dist/services/slice/slice-decompose-service.js +10 -29
  31. package/dist/services/slice/slice-decompose-types.d.ts +6 -47
  32. package/dist/services/slice/slice-decompose-types.js +3 -2
  33. package/dist/services/slice/slice-topology-types.d.ts +2 -3
  34. package/package.json +5 -5
  35. package/schemas/decomposition-v1.json +0 -12
  36. package/schemas/decomposition-v2.json +1 -15
  37. package/skills/bee/peaks-prd/SKILL.md +0 -1
  38. package/skills/bee/peaks-rd/SKILL.md +1 -1
  39. package/skills/bee/peaks-rd/references/external-references.md +1 -3
  40. package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -2
  41. package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +22 -8
  42. package/skills/bee/peaks-rd/references/rd-runbook.md +0 -2
  43. package/skills/bee/peaks-txt/SKILL.md +0 -12
  44. package/skills/peaks-doctor/SKILL.md +1 -1
  45. package/skills/peaks-slice-decompose/SKILL.md +0 -1
  46. package/skills/peaks-slice-decompose/references/v2-schema.md +1 -12
  47. package/dist/cli/commands/understand-commands.d.ts +0 -3
  48. package/dist/cli/commands/understand-commands.js +0 -148
  49. package/dist/services/understand/hybrid-types.d.ts +0 -32
  50. package/dist/services/understand/hybrid-types.js +0 -1
  51. package/dist/services/understand/understand-hybrid-service.d.ts +0 -10
  52. package/dist/services/understand/understand-hybrid-service.js +0 -100
  53. package/dist/services/understand/understand-scan-service.d.ts +0 -28
  54. package/dist/services/understand/understand-scan-service.js +0 -170
  55. package/dist/services/understand/understand-types.d.ts +0 -50
  56. package/dist/services/understand/understand-types.js +0 -1
@@ -31,7 +31,7 @@
31
31
  * === Source: peaks-rd/references/parallel-review-fanout.md ===
32
32
  *
33
33
  * Sub-agent 1 contract (Tier 7 v2.11.0): the parent RD loop calls
34
- * `Agent({ subagent_type: 'everything-claude-code:code-review', ... })`
34
+ * `Agent({ subagent_type: 'ecc:code-reviewer', ... })`
35
35
  * with the diff + handoff in scope, receives the structured envelope,
36
36
  * then runs `adaptEccEnvelopeToRdCodeReview(env, { rid, generatedAt })`
37
37
  * and writes the resulting body to
@@ -51,18 +51,43 @@ export interface EccEnvelope {
51
51
  readonly gateAction: EccGateAction;
52
52
  }
53
53
  /**
54
- * 5-state ECC detection result:
54
+ * The real upstream ECC agent name for the code-review role:
55
+ * `~/.peaks/agents/ecc/code-reviewer.md`. Upstream ships `<lang>-reviewer`
56
+ * agents and has NO `code-review.md`, so this is the default the
57
+ * `ready-via-cache` nextActions name when the caller does not pass the
58
+ * output of `resolveMaterializedAgentName(['code-reviewer', 'code-review'])`.
59
+ */
60
+ export declare const DEFAULT_CACHE_ECC_AGENT_NAME = "code-reviewer";
61
+ /**
62
+ * The native (plugin) ECC agent id dispatched through the Agent tool.
63
+ * Verified upstream: the plugin is named `ecc` (`.claude-plugin/plugin.json`,
64
+ * `marketplace.json`) and the agent's frontmatter `name` is `code-reviewer`
65
+ * (`~/.peaks/agents/ecc/code-reviewer.md`), so the composed id is
66
+ * `ecc:code-reviewer`. The previous id composed the OLD repo name
67
+ * (`everything-claude-code`) with a non-existent `code-review` agent, so it
68
+ * matched no registry entry and the native path silently fell through to
69
+ * inline review.
70
+ */
71
+ export declare const DEFAULT_NATIVE_ECC_AGENT_ID = "ecc:code-reviewer";
72
+ /**
73
+ * 6-state ECC detection result:
55
74
  * - `ready` — ECC plugin is installed and reachable
56
- * - `plugin-missing` — `everything-claude-code` not in skill list
57
- * - `agent-missing` — plugin present but `code-review` agent absent
75
+ * - `ready-via-cache` — plugin/agent absent, but the materialized ECC
76
+ * review agent (real name `code-reviewer`, NOT
77
+ * `code-review`) exists under `~/.peaks/agents/ecc/`;
78
+ * dispatch a GENERIC sub-agent with that body
79
+ * - `plugin-missing` — `everything-claude-code` not in skill list and no materialized cache
80
+ * - `agent-missing` — plugin present but `code-review` agent absent and no materialized cache
58
81
  * - `dispatch-failed` — Agent tool call threw before returning envelope
59
82
  * - `envelope-malformed` — returned value failed `isEccEnvelope` validation
60
83
  *
61
- * The caller (peaks-rd's sub-agent 1 contract) inspects `state` and
62
- * either proceeds with the envelope or records a degradation note in
63
- * the request artifact body (`code-review-ecc-degraded-to-inline`).
84
+ * Fallback order (RD fan-out, 2026-09-09-ecc-dynamic): native plugin →
85
+ * cache-backed generic agent → inline. The caller (peaks-rd's sub-agent 1
86
+ * contract) inspects `state` and either proceeds with the envelope or records
87
+ * a degradation note in the request artifact body
88
+ * (`code-review-ecc-degraded-to-inline`).
64
89
  */
65
- export type EccDetectState = 'ready' | 'plugin-missing' | 'agent-missing' | 'dispatch-failed' | 'envelope-malformed';
90
+ export type EccDetectState = 'ready' | 'ready-via-cache' | 'plugin-missing' | 'agent-missing' | 'dispatch-failed' | 'envelope-malformed';
66
91
  export interface EccDetectResult {
67
92
  readonly state: EccDetectState;
68
93
  readonly pluginInstalled: boolean;
@@ -105,39 +130,93 @@ export declare function adaptEccEnvelopeToRdCodeReview(env: EccEnvelope, opts: {
105
130
  generatedAt: string;
106
131
  }): RdCodeReviewDoc;
107
132
  /**
108
- * 5-state detector — mirrors `detectOcr`'s shape (state enum + warnings
133
+ * 6-state detector — mirrors `detectOcr`'s shape (state enum + warnings
109
134
  * + nextActions). The caller passes in the result of its own probe:
110
135
  *
111
136
  * - `pluginInstalled`: did the skill list scan find `everything-claude-code`?
112
137
  * - `agentAvailable`: did the Agent tool say `code-review` is registered?
138
+ * - `cacheAgentAvailable`: does the resolved materialized agent exist
139
+ * (`~/.peaks/agents/ecc/<cacheAgentName>.md`, default `code-reviewer`)?
113
140
  * - `dispatchError`: optional — the Agent tool threw before returning.
114
141
  * - `envelope`: optional — what came back (if anything).
115
142
  *
116
- * The detector never blocks: when state !== `ready`, the parent RD
117
- * loop records a `code-review-ecc-degraded-to-inline` note and falls
118
- * back to inline review. This matches `detectOcr`'s soft-fail policy.
143
+ * The detector never blocks: when the state is not dispatchable
144
+ * (`ready` / `ready-via-cache`), the parent RD loop records a
145
+ * `code-review-ecc-degraded-to-inline` note and falls back to inline
146
+ * review. This matches `detectOcr`'s soft-fail policy.
147
+ *
148
+ * Backward compat: the native id is `DEFAULT_NATIVE_ECC_AGENT_ID`
149
+ * (`ecc:code-reviewer` — plugin `ecc`, agent `code-reviewer`). A registry
150
+ * that still exposes the OLD `everything-claude-code` plugin id will not
151
+ * match it, so the operator should pin the id explicitly. peaks-loop
152
+ * cannot enumerate the user's plugin registry: detection stays
153
+ * caller-supplied (`pluginInstalled` / `agentAvailable`), never probed here.
119
154
  */
120
155
  export declare function detectEcc(input: {
121
156
  readonly pluginInstalled: boolean;
122
157
  readonly agentAvailable: boolean;
158
+ /**
159
+ * Did the caller find the materialized review agent (written by
160
+ * `peaks ecc install` / `materializeEccAgents`) under `~/.peaks/agents/ecc/`?
161
+ * When true and the native plugin path is unavailable, `state` becomes
162
+ * `ready-via-cache` so the RD loop can dispatch a generic sub-agent with
163
+ * the materialized body.
164
+ */
165
+ readonly cacheAgentAvailable?: boolean;
166
+ /**
167
+ * Resolved materialized agent name — pass
168
+ * `resolveMaterializedAgentName(['code-reviewer', 'code-review'])` from
169
+ * `peaks-loop-mut`. Defaults to `code-reviewer` (the real upstream name);
170
+ * upstream has no `code-review.md`.
171
+ */
172
+ readonly cacheAgentName?: string;
123
173
  readonly dispatchError?: unknown;
124
174
  readonly envelope?: unknown;
125
175
  }): EccDetectResult;
126
176
  /**
127
177
  * Convenience: run `detectEcc` and `adaptEccEnvelopeToRdCodeReview`
128
178
  * in one call. The parent RD loop calls this after the Agent tool
129
- * returns. When `detectEcc` returns state !== 'ready', the caller
130
- * should NOT call `adaptEccEnvelopeToRdCodeReview` — fall back to
131
- * inline review instead.
179
+ * returns. When `detectEcc` returns a non-dispatchable state
180
+ * (`plugin-missing` / `agent-missing` / `dispatch-failed` /
181
+ * `envelope-malformed`), the caller should NOT call
182
+ * `adaptEccEnvelopeToRdCodeReview` — fall back to inline review instead.
183
+ *
184
+ * Both `ready` (native plugin) and `ready-via-cache` (materialized generic
185
+ * agent) adapt the envelope through the SAME bridge.
132
186
  */
133
187
  export declare function runEccCodeReview(input: {
134
188
  readonly rid: string;
135
189
  readonly generatedAt: string;
136
190
  readonly pluginInstalled: boolean;
137
191
  readonly agentAvailable: boolean;
192
+ readonly cacheAgentAvailable?: boolean;
193
+ /** Resolved materialized agent name (see `detectEcc`); defaults to `code-reviewer`. */
194
+ readonly cacheAgentName?: string;
138
195
  readonly dispatchError?: unknown;
139
196
  readonly envelope?: unknown;
140
197
  }): {
141
198
  detect: EccDetectResult;
142
199
  doc: RdCodeReviewDoc | null;
143
200
  };
201
+ /**
202
+ * The exact JSON shape the generic (cache-backed) sub-agent MUST return.
203
+ * Kept as a const so the RD reference docs and the prompt builder cannot
204
+ * drift: `isEccEnvelope` is the authoritative validator.
205
+ */
206
+ export declare const ECC_OUTPUT_CONTRACT: string;
207
+ /**
208
+ * Compose the plugin-free dispatch prompt for sub-agent 1.
209
+ *
210
+ * `instructions` is the body of the materialized review agent
211
+ * (`~/.peaks/agents/ecc/code-reviewer.md` — resolve the real name with
212
+ * `resolveMaterializedAgentName`, never hardcode `code-review.md`);
213
+ * `diff` is the slice diff. The
214
+ * composed prompt is handed to a GENERIC sub-agent (no ECC plugin needed)
215
+ * and its reply is validated with `isEccEnvelope`, then rendered by
216
+ * `adaptEccEnvelopeToRdCodeReview` — the same bridge the native path uses.
217
+ */
218
+ export declare function buildCacheBackedEccPrompt(input: {
219
+ readonly rid: string;
220
+ readonly instructions: string;
221
+ readonly diff: string;
222
+ }): string;
@@ -1,3 +1,27 @@
1
+ /**
2
+ * The real upstream ECC agent name for the code-review role:
3
+ * `~/.peaks/agents/ecc/code-reviewer.md`. Upstream ships `<lang>-reviewer`
4
+ * agents and has NO `code-review.md`, so this is the default the
5
+ * `ready-via-cache` nextActions name when the caller does not pass the
6
+ * output of `resolveMaterializedAgentName(['code-reviewer', 'code-review'])`.
7
+ */
8
+ export const DEFAULT_CACHE_ECC_AGENT_NAME = 'code-reviewer';
9
+ /**
10
+ * The native (plugin) ECC agent id dispatched through the Agent tool.
11
+ * Verified upstream: the plugin is named `ecc` (`.claude-plugin/plugin.json`,
12
+ * `marketplace.json`) and the agent's frontmatter `name` is `code-reviewer`
13
+ * (`~/.peaks/agents/ecc/code-reviewer.md`), so the composed id is
14
+ * `ecc:code-reviewer`. The previous id composed the OLD repo name
15
+ * (`everything-claude-code`) with a non-existent `code-review` agent, so it
16
+ * matched no registry entry and the native path silently fell through to
17
+ * inline review.
18
+ */
19
+ export const DEFAULT_NATIVE_ECC_AGENT_ID = 'ecc:code-reviewer';
20
+ /** Materialized path hint for the cache-backed agent, e.g. `~/.peaks/agents/ecc/code-reviewer.md`. */
21
+ function materializedAgentPath(name) {
22
+ const resolved = name !== undefined && name.length > 0 ? name : DEFAULT_CACHE_ECC_AGENT_NAME;
23
+ return `~/.peaks/agents/ecc/${resolved}.md`;
24
+ }
1
25
  /**
2
26
  * Validate a raw value as an EccEnvelope. Accepts the strict shape
3
27
  * only — extra fields are tolerated (forward-compat for ECC adding
@@ -82,7 +106,7 @@ export function adaptEccEnvelopeToRdCodeReview(env, opts) {
82
106
  `## Summary`,
83
107
  '',
84
108
  `- generatedAt: ${opts.generatedAt}`,
85
- `- source: everything-claude-code:code-review (Tier 7 bridge)`,
109
+ `- source: ${DEFAULT_NATIVE_ECC_AGENT_ID} (Tier 7 bridge)`,
86
110
  `- gateAction: ${env.gateAction}`,
87
111
  `- passed: ${env.passed}`,
88
112
  `- violations: ${env.violations.length}`,
@@ -146,43 +170,87 @@ function renderFindings(violations) {
146
170
  return ['## Findings', '', ...bullets, ''].join('\n');
147
171
  }
148
172
  /**
149
- * 5-state detector — mirrors `detectOcr`'s shape (state enum + warnings
173
+ * 6-state detector — mirrors `detectOcr`'s shape (state enum + warnings
150
174
  * + nextActions). The caller passes in the result of its own probe:
151
175
  *
152
176
  * - `pluginInstalled`: did the skill list scan find `everything-claude-code`?
153
177
  * - `agentAvailable`: did the Agent tool say `code-review` is registered?
178
+ * - `cacheAgentAvailable`: does the resolved materialized agent exist
179
+ * (`~/.peaks/agents/ecc/<cacheAgentName>.md`, default `code-reviewer`)?
154
180
  * - `dispatchError`: optional — the Agent tool threw before returning.
155
181
  * - `envelope`: optional — what came back (if anything).
156
182
  *
157
- * The detector never blocks: when state !== `ready`, the parent RD
158
- * loop records a `code-review-ecc-degraded-to-inline` note and falls
159
- * back to inline review. This matches `detectOcr`'s soft-fail policy.
183
+ * The detector never blocks: when the state is not dispatchable
184
+ * (`ready` / `ready-via-cache`), the parent RD loop records a
185
+ * `code-review-ecc-degraded-to-inline` note and falls back to inline
186
+ * review. This matches `detectOcr`'s soft-fail policy.
187
+ *
188
+ * Backward compat: the native id is `DEFAULT_NATIVE_ECC_AGENT_ID`
189
+ * (`ecc:code-reviewer` — plugin `ecc`, agent `code-reviewer`). A registry
190
+ * that still exposes the OLD `everything-claude-code` plugin id will not
191
+ * match it, so the operator should pin the id explicitly. peaks-loop
192
+ * cannot enumerate the user's plugin registry: detection stays
193
+ * caller-supplied (`pluginInstalled` / `agentAvailable`), never probed here.
160
194
  */
161
195
  export function detectEcc(input) {
162
196
  const warnings = [];
163
197
  const nextActions = [];
198
+ const cacheAgentAvailable = input.cacheAgentAvailable === true;
199
+ const cacheAgentPath = materializedAgentPath(input.cacheAgentName);
164
200
  if (!input.pluginInstalled) {
201
+ if (cacheAgentAvailable) {
202
+ return {
203
+ state: 'ready-via-cache',
204
+ pluginInstalled: false,
205
+ agentAvailable: false,
206
+ warnings: [
207
+ 'ECC Claude Code plugin is not installed; using the cache-backed generic agent instead.'
208
+ ],
209
+ nextActions: [
210
+ `Dispatch a generic sub-agent with the body of \`${cacheAgentPath}\` (see \`buildCacheBackedEccPrompt\`).`,
211
+ 'Refresh the materialized copy with `peaks ecc install`.',
212
+ 'Install the `ecc` plugin for the native path (optional; native plugin → cache-backed → inline).'
213
+ ]
214
+ };
215
+ }
165
216
  return {
166
217
  state: 'plugin-missing',
167
218
  pluginInstalled: false,
168
219
  agentAvailable: false,
169
220
  warnings,
170
221
  nextActions: [
171
- 'Install the `everything-claude-code` plugin (Claude Code marketplace).',
172
- 'Until installed, peaks-rd falls back to inline code review (degradation note: code-review-ecc-degraded-to-inline).'
222
+ 'Install the `ecc` plugin (Claude Code marketplace).',
223
+ 'Or run `peaks ecc install` to populate the cache-backed generic agent path.',
224
+ 'Until one of the two is available, peaks-rd falls back to inline code review (degradation note: code-review-ecc-degraded-to-inline).'
173
225
  ]
174
226
  };
175
227
  }
176
228
  if (!input.agentAvailable) {
229
+ if (cacheAgentAvailable) {
230
+ return {
231
+ state: 'ready-via-cache',
232
+ pluginInstalled: true,
233
+ agentAvailable: false,
234
+ warnings: [
235
+ '`ecc` is installed but its `code-reviewer` agent is not registered; using the cache-backed generic agent instead.'
236
+ ],
237
+ nextActions: [
238
+ `Dispatch a generic sub-agent with the body of \`${cacheAgentPath}\` (see \`buildCacheBackedEccPrompt\`).`,
239
+ 'Verify the plugin is enabled in your Claude Code settings (MCP servers / agent registry) to restore the native path.'
240
+ ]
241
+ };
242
+ }
177
243
  return {
178
244
  state: 'agent-missing',
179
245
  pluginInstalled: true,
180
246
  agentAvailable: false,
181
247
  warnings: [
182
- '`everything-claude-code` is installed but its `code-review` agent is not registered.'
248
+ '`ecc` is installed but its `code-reviewer` agent is not registered.'
183
249
  ],
184
250
  nextActions: [
185
251
  'Verify the plugin is enabled in your Claude Code settings (MCP servers / agent registry).',
252
+ `If your registry still exposes the old \`everything-claude-code\` plugin id, pin the native agent id explicitly (\`${DEFAULT_NATIVE_ECC_AGENT_ID}\`) instead of relying on detection.`,
253
+ 'Or run `peaks ecc install` to populate the cache-backed generic agent path.',
186
254
  'Until resolved, peaks-rd falls back to inline code review.'
187
255
  ]
188
256
  };
@@ -226,17 +294,69 @@ export function detectEcc(input) {
226
294
  /**
227
295
  * Convenience: run `detectEcc` and `adaptEccEnvelopeToRdCodeReview`
228
296
  * in one call. The parent RD loop calls this after the Agent tool
229
- * returns. When `detectEcc` returns state !== 'ready', the caller
230
- * should NOT call `adaptEccEnvelopeToRdCodeReview` — fall back to
231
- * inline review instead.
297
+ * returns. When `detectEcc` returns a non-dispatchable state
298
+ * (`plugin-missing` / `agent-missing` / `dispatch-failed` /
299
+ * `envelope-malformed`), the caller should NOT call
300
+ * `adaptEccEnvelopeToRdCodeReview` — fall back to inline review instead.
301
+ *
302
+ * Both `ready` (native plugin) and `ready-via-cache` (materialized generic
303
+ * agent) adapt the envelope through the SAME bridge.
232
304
  */
233
305
  export function runEccCodeReview(input) {
234
306
  const detect = detectEcc(input);
235
- if (detect.state !== 'ready' || input.envelope === undefined) {
307
+ const dispatchable = detect.state === 'ready' || detect.state === 'ready-via-cache';
308
+ if (!dispatchable || input.envelope === undefined) {
236
309
  return { detect, doc: null };
237
310
  }
238
- // Detect.state === 'ready' implies input.envelope passed isEccEnvelope; the runtime
311
+ // A dispatchable state implies input.envelope passed isEccEnvelope; the runtime
239
312
  // type is therefore EccEnvelope, not `unknown`. Cast is justified by detect's contract.
240
313
  const env = input.envelope;
241
314
  return { detect, doc: adaptEccEnvelopeToRdCodeReview(env, { rid: input.rid, generatedAt: input.generatedAt }) };
242
315
  }
316
+ /**
317
+ * The exact JSON shape the generic (cache-backed) sub-agent MUST return.
318
+ * Kept as a const so the RD reference docs and the prompt builder cannot
319
+ * drift: `isEccEnvelope` is the authoritative validator.
320
+ */
321
+ export const ECC_OUTPUT_CONTRACT = [
322
+ 'Return ONLY a single JSON object (no prose, no markdown fence) with this exact shape:',
323
+ '{',
324
+ ' "passed": boolean,',
325
+ ' "violations": [{ "kind": string, "line": number, "snippet": string, "hint": string }],',
326
+ ' "gateAction": "pass" | "warn" | "block"',
327
+ '}',
328
+ '`kind` is one of: correctness, type-safety, error-handling, mutation, file-size, naming, dead-code, regression, contract-drift, other.',
329
+ '`gateAction: "block"` requires at least one violation; `pass` requires an empty violations array.'
330
+ ].join('\n');
331
+ /**
332
+ * Compose the plugin-free dispatch prompt for sub-agent 1.
333
+ *
334
+ * `instructions` is the body of the materialized review agent
335
+ * (`~/.peaks/agents/ecc/code-reviewer.md` — resolve the real name with
336
+ * `resolveMaterializedAgentName`, never hardcode `code-review.md`);
337
+ * `diff` is the slice diff. The
338
+ * composed prompt is handed to a GENERIC sub-agent (no ECC plugin needed)
339
+ * and its reply is validated with `isEccEnvelope`, then rendered by
340
+ * `adaptEccEnvelopeToRdCodeReview` — the same bridge the native path uses.
341
+ */
342
+ export function buildCacheBackedEccPrompt(input) {
343
+ return [
344
+ '# Code review (ECC cache-backed generic agent)',
345
+ '',
346
+ `Request: ${input.rid}`,
347
+ '',
348
+ `## Agent instructions (materialized from ${DEFAULT_NATIVE_ECC_AGENT_ID})`,
349
+ '',
350
+ input.instructions.trim(),
351
+ '',
352
+ '## Diff under review',
353
+ '',
354
+ '```diff',
355
+ input.diff.trim(),
356
+ '```',
357
+ '',
358
+ '## Required output',
359
+ '',
360
+ ECC_OUTPUT_CONTRACT
361
+ ].join('\n');
362
+ }
@@ -12,12 +12,6 @@ export type ProjectDashboardOpenSpec = {
12
12
  count: number;
13
13
  changes: OpenSpecChangeSummary[];
14
14
  };
15
- export type ProjectDashboardUnderstand = {
16
- exists: boolean;
17
- graphExists: boolean;
18
- graphPath: string;
19
- parseError?: string;
20
- };
21
15
  export type ProjectDashboardDoctor = {
22
16
  ok: boolean;
23
17
  passed: number;
@@ -69,7 +63,6 @@ export type ProjectDashboard = {
69
63
  okPolicy: DashboardOkPolicy;
70
64
  requests: ProjectDashboardRequests;
71
65
  openspec: ProjectDashboardOpenSpec;
72
- understand: ProjectDashboardUnderstand;
73
66
  doctor: ProjectDashboardDoctor;
74
67
  runbookHealth: ProjectDashboardRunbookHealth;
75
68
  capabilities: ProjectDashboardCapabilities;
@@ -1,6 +1,5 @@
1
1
  import { listRequestArtifacts } from '../artifacts/request-artifact-service.js';
2
2
  import { scanOpenSpec } from '../openspec/openspec-scan-service.js';
3
- import { scanUnderstandAnything } from '../understand/understand-scan-service.js';
4
3
  import { seedCapabilityItems } from '../recommendations/capability-seed-items.js';
5
4
  import { requiredSkillNames } from 'peaks-loop-shared/paths';
6
5
  import { getSkillPresence } from '../skills/skill-presence-service.js';
@@ -107,10 +106,9 @@ export async function loadProjectDashboard(options) {
107
106
  const clock = options.clock ?? defaultClock;
108
107
  const sampleSize = options.sampleCapabilities ?? 8;
109
108
  const okPolicy = options.okPolicy ?? 'workspace-only';
110
- const [items, openspecReport, understandReport, doctorAndRunbook] = await Promise.all([
109
+ const [items, openspecReport, doctorAndRunbook] = await Promise.all([
111
110
  listRequestArtifacts({ projectRoot: options.projectRoot }),
112
111
  scanOpenSpec({ openspecRoot: `${options.projectRoot}/openspec` }),
113
- scanUnderstandAnything({ projectRoot: options.projectRoot }),
114
112
  loadDoctorAndRunbookHealth(options.doctorReport, options.runbookHealth)
115
113
  ]);
116
114
  const okVerdict = resolveDashboardOk({
@@ -133,12 +131,6 @@ export async function loadProjectDashboard(options) {
133
131
  count: openspecReport.changes.length,
134
132
  changes: openspecReport.changes
135
133
  },
136
- understand: {
137
- exists: understandReport.exists,
138
- graphExists: understandReport.graph.exists,
139
- graphPath: understandReport.graph.path,
140
- ...(understandReport.graph.parseError !== undefined ? { parseError: understandReport.graph.parseError } : {})
141
- },
142
134
  doctor: doctorAndRunbook.doctor,
143
135
  runbookHealth: doctorAndRunbook.runbookHealth,
144
136
  capabilities: buildCapabilitiesSummary(sampleSize),
@@ -76,7 +76,16 @@ export interface ProjectPreferences {
76
76
  * dispatch *shape* (worker graph vs flat dispatch).
77
77
  */
78
78
  readonly swarmMode: boolean;
79
+ /**
80
+ * Legacy UA opt-in prompt decision. The UA knowledge-graph surface was
81
+ * removed in 2026-09-09 (project analysis is codegraph-first); the field
82
+ * is retained only so legacy `.peaks/preferences.json` files keep parsing.
83
+ */
79
84
  readonly uaPrompt: UaPromptDecision;
85
+ /**
86
+ * Legacy AgentShield opt-in prompt decision. The ECC AgentShield
87
+ * subprocess was removed in 4.0.0-beta.11; retained for legacy parse.
88
+ */
80
89
  readonly agentShieldPrompt: UaPromptDecision;
81
90
  readonly classifyConservatism: ClassifyConservatism;
82
91
  readonly classifyRules: ClassifyRuleOverrides;
@@ -84,15 +93,15 @@ export interface ProjectPreferences {
84
93
  /** Loop Autonomous (L4 14.5) toggle. Default: false — never auto-enable. */
85
94
  readonly loopAutonomousEnabled: boolean;
86
95
  /**
87
- * L2.3 P2-a: ECC AgentShield subprocess toggle. Default: false.
96
+ * L2.3 P2-a: ECC AgentShield subprocess toggle — **frozen, no-op**.
88
97
  *
89
- * When true, `peaks audit static` spawns `npx ecc-agentshield scan --json`
90
- * and merges its findings into the audit report. When false (default),
91
- * the audit runs peaks-loop-only and the subprocess is never spawned.
98
+ * Slice 3 of 4.0.0-beta.11 removed the ECC AgentShield subprocess
99
+ * entirely (upstream ships no `ecc-agentshield` binary). The field is
100
+ * retained only so legacy `.peaks/preferences.json` files still parse
101
+ * (`static-service.ts` calls `loadPreferences` for back-compat and
102
+ * ignores the value). Setting it to `true` has no effect.
92
103
  *
93
- * The preference is independent of whether ECC is installed — i.e.
94
- * `agentShieldEnabled: true` with ECC missing surfaces a soft
95
- * "ECC not installed" warning and the audit still completes.
104
+ * Default: false.
96
105
  */
97
106
  readonly agentShieldEnabled: boolean;
98
107
  /**
@@ -98,7 +98,6 @@ export const seedCapabilityItems = [
98
98
  capability('gitnexus.repo-intelligence', 'gitnexus', 'GitNexus Repository Intelligence', 'cli', 'repo-intelligence', ['engineer'], 'medium', 'local-repo-scan', 'Use local project scanning through Peaks RD.', 'Repository Intelligence', '仓库智能分析', 'Repository intelligence should be proxied through Peaks before use.', '仓库智能分析应先通过 Peaks 代理边界再使用。'),
99
99
  capability('claude-code-best-practice.workflow-guidance', 'claude-code-best-practice', 'Claude Code Best Practice', 'doc', 'workflow-guidance', ['engineer'], 'low', 'peaks-built-in-rules', 'Use Peaks built-in workflow and review rules.', 'Claude Code Best Practice', 'Claude Code 最佳实践', 'Guidance for Claude Code engineering workflows.', 'Claude Code 工程工作流指导。'),
100
100
  capability('superpowers.workflow-methodology', 'superpowers', 'Superpowers Methodology', 'workflow', 'workflow-methodology', ['product', 'engineer'], 'low', 'peaks-workflow-route', 'Use Peaks route/autonomous plans.', 'Superpowers Methodology', 'Superpowers 方法论', 'Workflow and artifact methodology reference.', '工作流与 artifact 方法论参考。'),
101
- capability('understand-anything.knowledge-graph', 'understand-anything', 'Understand Anything Knowledge Graph', 'skill', 'project-analysis', ['engineer', 'product', 'qa'], 'low', 'peaks-codegraph-or-rd-scan', 'Use peaks codegraph or peaks RD project scan when Understand Anything is not installed. Optionally suggest installing it via Claude Code: /plugin marketplace add Lum1104/Understand-Anything then /plugin install understand-anything.', 'Understand Anything Knowledge Graph', 'Understand Anything 知识图谱', 'Multi-agent project knowledge graph that Peaks reads from .understand-anything/knowledge-graph.json when it has been generated by the Understand Anything plugin in Claude Code.', '通过 Claude Code 的 Understand Anything 插件生成的多 agent 知识图谱,Peaks 在 .understand-anything/knowledge-graph.json 存在时读取它作为项目分析证据。'),
102
101
  capability('penpot.design-source', 'penpot', 'Penpot Design Source', 'doc', 'design-source', ['designer', 'engineer'], 'medium', 'manual-design-input', 'Ask for exported design notes or screenshots.', 'Penpot Design Source', 'Penpot 设计来源', 'Design-source reference for UI planning.', '用于 UI 规划的设计来源参考。'),
103
102
  capability('gstack.product-stack-guidance', 'gstack', 'Product Stack Guidance', 'doc', 'product-guidance', ['product'], 'low', 'peaks-prd', 'Use peaks-prd for product goals and non-goals.', 'Product Stack Guidance', '产品栈指导', 'Product and startup stack guidance reference.', '产品与创业栈指导参考。'),
104
103
  capability('awesome-design-md.design-reference', 'awesome-design-md', 'Design Markdown Reference', 'doc', 'design-reference', ['designer', 'engineer'], 'low', 'peaks-ui-visual-direction', 'Use peaks-ui to define visual direction and interaction constraints.', 'Design Reference', '设计参考', 'Design markdown examples and inspiration.', '设计 markdown 示例与灵感。'),
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Read-only "is this capability already satisfied on this machine?"
3
+ * detector for the capability catalog (`peaks capability status` /
4
+ * `peaks capabilities --source ...`).
5
+ *
6
+ * Slice B1 of 2026-09-09-ecc-dynamic-and-cleanup: the previous
7
+ * `getInstalledCapabilityIds()` returned `[]` unconditionally, so the
8
+ * catalog could never report anything as installed. This module answers
9
+ * the question from evidence that already exists on disk — no install
10
+ * side effect, no network, no subprocess:
11
+ *
12
+ * - `everything-claude-code.*` — satisfied when the ECC cache is
13
+ * populated (`~/.peaks/cache/ecc-installed.json` + at least one
14
+ * cached/materialized agent).
15
+ * - a source that maps to an npm package — satisfied when that package
16
+ * (or its `.bin` shim) is present in the project's `node_modules`.
17
+ *
18
+ * Every probe is fail-soft: a throw degrades to "not installed" so the
19
+ * catalog never breaks because a probe failed.
20
+ */
21
+ export type InstalledCapabilityProbe = {
22
+ /** Project whose `node_modules` is probed. */
23
+ readonly projectRoot: string;
24
+ /** Override for tests; defaults to `<projectRoot>/node_modules`. */
25
+ readonly nodeModulesDir?: string;
26
+ /** Override for tests; defaults to a live ECC cache probe. */
27
+ readonly eccCacheAvailable?: boolean;
28
+ };
29
+ /**
30
+ * Return the capabilityIds whose acquisition is already satisfied.
31
+ * Sorted + de-duplicated so the JSON envelope is stable across runs.
32
+ */
33
+ export declare function detectInstalledCapabilityIds(probe: InstalledCapabilityProbe): string[];
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Read-only "is this capability already satisfied on this machine?"
3
+ * detector for the capability catalog (`peaks capability status` /
4
+ * `peaks capabilities --source ...`).
5
+ *
6
+ * Slice B1 of 2026-09-09-ecc-dynamic-and-cleanup: the previous
7
+ * `getInstalledCapabilityIds()` returned `[]` unconditionally, so the
8
+ * catalog could never report anything as installed. This module answers
9
+ * the question from evidence that already exists on disk — no install
10
+ * side effect, no network, no subprocess:
11
+ *
12
+ * - `everything-claude-code.*` — satisfied when the ECC cache is
13
+ * populated (`~/.peaks/cache/ecc-installed.json` + at least one
14
+ * cached/materialized agent).
15
+ * - a source that maps to an npm package — satisfied when that package
16
+ * (or its `.bin` shim) is present in the project's `node_modules`.
17
+ *
18
+ * Every probe is fail-soft: a throw degrades to "not installed" so the
19
+ * catalog never breaks because a probe failed.
20
+ */
21
+ import { existsSync } from 'node:fs';
22
+ import { join } from 'node:path';
23
+ import { hasMaterializedEccAgents, listCachedAgents } from 'peaks-loop-mut';
24
+ import { seedCapabilityItems } from './capability-seed-items.js';
25
+ const ECC_SOURCE_ID = 'everything-claude-code';
26
+ /**
27
+ * `sourceId` → npm package whose presence in `node_modules` satisfies the
28
+ * item's acquisition. Deliberately explicit: the catalog mixes repos,
29
+ * skills packages, MCP servers, and docs, and only a small subset is
30
+ * actually an npm dependency we can probe. Adding an entry here is the
31
+ * only change needed to make that source's items detectable.
32
+ */
33
+ const NPM_PACKAGE_BY_SOURCE = {
34
+ codegraph: 'codegraph'
35
+ };
36
+ function packagePresent(nodeModulesDir, packageName) {
37
+ try {
38
+ return (existsSync(join(nodeModulesDir, packageName)) ||
39
+ existsSync(join(nodeModulesDir, '.bin', packageName)));
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
45
+ function eccCacheAvailable() {
46
+ try {
47
+ // Cached agents imply the manifest + sha dir exist; the materialized
48
+ // plugin-free copy is checked independently so a hand-populated
49
+ // `~/.peaks/agents/ecc/` still counts as satisfied.
50
+ return listCachedAgents().length > 0 || hasMaterializedEccAgents();
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ /**
57
+ * Return the capabilityIds whose acquisition is already satisfied.
58
+ * Sorted + de-duplicated so the JSON envelope is stable across runs.
59
+ */
60
+ export function detectInstalledCapabilityIds(probe) {
61
+ const nodeModulesDir = probe.nodeModulesDir ?? join(probe.projectRoot, 'node_modules');
62
+ const eccAvailable = probe.eccCacheAvailable ?? eccCacheAvailable();
63
+ const installed = new Set();
64
+ for (const item of seedCapabilityItems) {
65
+ if (item.sourceId === ECC_SOURCE_ID) {
66
+ if (eccAvailable)
67
+ installed.add(item.capabilityId);
68
+ continue;
69
+ }
70
+ const packageName = NPM_PACKAGE_BY_SOURCE[item.sourceId];
71
+ if (packageName !== undefined && packagePresent(nodeModulesDir, packageName)) {
72
+ installed.add(item.capabilityId);
73
+ }
74
+ }
75
+ return [...installed].sort();
76
+ }
@@ -26,7 +26,7 @@ const PARENT_COMMANDS = new Set([
26
26
  'qa', 'sc', 'txt', 'code-review', 'rd', 'sh', 'config', 'audit', 'codegraph',
27
27
  'context', 'agent', 'capability', 'classify', 'gstack', 'gate',
28
28
  'hook', 'hooks', 'log', 'loop', 'memory', 'perf', 'playwright', 'preferences',
29
- 'project', 'retrospective', 'slice', 'statusline', 'understand', 'workflow',
29
+ 'project', 'retrospective', 'slice', 'statusline', 'workflow',
30
30
  'migrate', 'mcp', 'doctor', 'help'
31
31
  ]);
32
32
  const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'coverage']);
@@ -65,7 +65,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
65
65
  const wantFile = granularity === 'file' || granularity === 'both' || granularity === 'auto';
66
66
  const passes = [];
67
67
  let codegraph = zeroCodegraph();
68
- let understandAnything = zeroUnderstand();
69
68
  let pass1WorkUnits = [];
70
69
  if (wantService) {
71
70
  const result1 = await decomposeSlices(rid, prdMarkdown, projectRoot, opts);
@@ -77,7 +76,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
77
76
  internalEdges: v1EdgesToV2(result1.dependencyDAG.edges)
78
77
  });
79
78
  codegraph = result1.codegraph;
80
- understandAnything = result1.understandAnything;
81
79
  }
82
80
  if (wantFile) {
83
81
  if (pass1WorkUnits.length > 0) {
@@ -115,7 +113,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
115
113
  internalEdges: v1EdgesToV2(result.dependencyDAG.edges)
116
114
  });
117
115
  codegraph = result.codegraph;
118
- understandAnything = result.understandAnything;
119
116
  }
120
117
  }
121
118
  let crossPassEdges = [];
@@ -136,7 +133,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
136
133
  crossPassEdges,
137
134
  llmArbitrations,
138
135
  codegraph,
139
- understandAnything,
140
136
  partial: false
141
137
  };
142
138
  }
@@ -214,12 +210,3 @@ function zeroCodegraph() {
214
210
  note: 'no decomposition run'
215
211
  };
216
212
  }
217
- function zeroUnderstand() {
218
- return {
219
- kgNodes: 0,
220
- kgEdges: 0,
221
- available: false,
222
- fallback: 'structural-only',
223
- note: 'no decomposition run'
224
- };
225
- }
@@ -4,5 +4,5 @@
4
4
  * the 800 LOC cap (mechanical verbatim move). `buildDependencyEdges` is
5
5
  * the only exported entry point; the rest are internal helpers.
6
6
  */
7
- import type { DependencyEdge, ImportEdge, KnowledgeGraph, WorkUnit } from './slice-decompose-types.js';
8
- export declare function buildDependencyEdges(wus: readonly WorkUnit[], importEdges: readonly ImportEdge[], kg: KnowledgeGraph | null, projectRoot: string): DependencyEdge[];
7
+ import type { DependencyEdge, ImportEdge, WorkUnit } from './slice-decompose-types.js';
8
+ export declare function buildDependencyEdges(wus: readonly WorkUnit[], importEdges: readonly ImportEdge[], projectRoot: string): DependencyEdge[];