peaks-loop 4.0.32 → 4.0.34

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 (111) 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/code-commands.js +2 -0
  9. package/dist/cli/commands/code-mode-gate-commands.js +3 -3
  10. package/dist/cli/commands/code-mode-status-command.d.ts +17 -0
  11. package/dist/cli/commands/code-mode-status-command.js +51 -0
  12. package/dist/cli/commands/code-run-command.js +8 -1
  13. package/dist/cli/commands/code-runtime-commands.js +13 -5
  14. package/dist/cli/commands/core/skill-command.js +1 -1
  15. package/dist/cli/commands/ecc-commands.js +3 -2
  16. package/dist/cli/commands/project-commands.js +1 -1
  17. package/dist/cli/commands/request-commands.js +3 -7
  18. package/dist/cli/commands/session-24h-mode.js +9 -1
  19. package/dist/cli/commands/shadcn-commands.d.ts +32 -0
  20. package/dist/cli/commands/shadcn-commands.js +120 -0
  21. package/dist/cli/commands/slice-commands.js +1 -4
  22. package/dist/services/24h-mode/auto-engage.d.ts +39 -0
  23. package/dist/services/24h-mode/auto-engage.js +46 -0
  24. package/dist/services/24h-mode/decider.d.ts +11 -5
  25. package/dist/services/24h-mode/decider.js +13 -13
  26. package/dist/services/audit/enforcers/active-skill-resolver.d.ts +3 -1
  27. package/dist/services/audit/enforcers/active-skill-resolver.js +3 -5
  28. package/dist/services/audit/static-service.d.ts +1 -1
  29. package/dist/services/audit/static-service.js +1 -1
  30. package/dist/services/code/auto-compact-lifecycle.d.ts +1 -1
  31. package/dist/services/code/auto-compact-lifecycle.js +1 -1
  32. package/dist/services/code/auto-compact-modes.d.ts +1 -1
  33. package/dist/services/code/auto-compact-modes.js +1 -1
  34. package/dist/services/code/auto-compact-orchestrator.js +18 -22
  35. package/dist/services/code/mode-gate.d.ts +6 -2
  36. package/dist/services/code/mode-gate.js +9 -5
  37. package/dist/services/code/orchestrator-can-do.js +2 -2
  38. package/dist/services/code-review/ecc-bridge.d.ts +94 -15
  39. package/dist/services/code-review/ecc-bridge.js +133 -13
  40. package/dist/services/config/config-service.d.ts +17 -0
  41. package/dist/services/config/config-service.js +34 -0
  42. package/dist/services/context/auto-compact-dispatcher.d.ts +2 -2
  43. package/dist/services/context/auto-compact-dispatcher.js +4 -4
  44. package/dist/services/context/auto-compact-reader.js +8 -1
  45. package/dist/services/context/auto-compact-types.d.ts +13 -1
  46. package/dist/services/context/build-dispatch-system-prompt.js +2 -2
  47. package/dist/services/context/context-schema.d.ts +1 -1
  48. package/dist/services/dashboard/project-dashboard-service.d.ts +0 -7
  49. package/dist/services/dashboard/project-dashboard-service.js +1 -9
  50. package/dist/services/hooks/auto-compact-hook-install.d.ts +2 -2
  51. package/dist/services/hooks/auto-compact-hook-install.js +2 -2
  52. package/dist/services/ide/adapters/claude-code-adapter.d.ts +51 -0
  53. package/dist/services/ide/adapters/claude-code-adapter.js +84 -14
  54. package/dist/services/ide/ide-types.d.ts +10 -1
  55. package/dist/services/mode/mode-enforcement.d.ts +4 -1
  56. package/dist/services/mode/mode-enforcement.js +26 -28
  57. package/dist/services/mode/mode-status-service.d.ts +81 -0
  58. package/dist/services/mode/mode-status-service.js +108 -0
  59. package/dist/services/preferences/preferences-types.d.ts +16 -7
  60. package/dist/services/recommendations/capability-seed-items.js +0 -1
  61. package/dist/services/recommendations/installed-capability-detector.d.ts +33 -0
  62. package/dist/services/recommendations/installed-capability-detector.js +76 -0
  63. package/dist/services/scan/orphan-service.js +1 -1
  64. package/dist/services/skills/presence-lease-service.d.ts +21 -0
  65. package/dist/services/skills/presence-lease-service.js +26 -0
  66. package/dist/services/skills/skill-presence-service.d.ts +10 -1
  67. package/dist/services/skills/skill-presence-service.js +32 -4
  68. package/dist/services/skills/skill-statusline-service.js +6 -3
  69. package/dist/services/slice/multi-pass-orchestrator.js +0 -13
  70. package/dist/services/slice/slice-decompose-import-edges.d.ts +2 -2
  71. package/dist/services/slice/slice-decompose-import-edges.js +1 -29
  72. package/dist/services/slice/slice-decompose-runners.d.ts +2 -4
  73. package/dist/services/slice/slice-decompose-runners.js +1 -23
  74. package/dist/services/slice/slice-decompose-service.d.ts +3 -4
  75. package/dist/services/slice/slice-decompose-service.js +10 -29
  76. package/dist/services/slice/slice-decompose-types.d.ts +6 -47
  77. package/dist/services/slice/slice-decompose-types.js +3 -2
  78. package/dist/services/slice/slice-topology-types.d.ts +2 -3
  79. package/package.json +5 -5
  80. package/schemas/decomposition-v1.json +0 -12
  81. package/schemas/decomposition-v2.json +1 -15
  82. package/skills/bee/peaks-prd/SKILL.md +0 -1
  83. package/skills/bee/peaks-rd/SKILL.md +1 -1
  84. package/skills/bee/peaks-rd/references/external-references.md +1 -3
  85. package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -2
  86. package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +22 -8
  87. package/skills/bee/peaks-rd/references/rd-runbook.md +0 -2
  88. package/skills/bee/peaks-txt/SKILL.md +0 -12
  89. package/skills/peaks-code/SKILL.md +9 -9
  90. package/skills/peaks-code/references/mode-selection.md +7 -3
  91. package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
  92. package/skills/peaks-code/references/runbook.md +10 -5
  93. package/skills/peaks-code/references/session-overload-signal-index.md +2 -2
  94. package/skills/peaks-code/references/standards-preflight.md +1 -1
  95. package/skills/peaks-code/references/startup-sequence.md +1 -1
  96. package/skills/peaks-code/references/step-0-8-gate.md +1 -1
  97. package/skills/peaks-code/references/swarm-dispatch-contract.md +2 -2
  98. package/skills/peaks-code/references/workflow-gates-and-types.md +1 -1
  99. package/skills/peaks-doctor/SKILL.md +1 -1
  100. package/skills/peaks-slice-decompose/SKILL.md +0 -1
  101. package/skills/peaks-slice-decompose/references/v2-schema.md +1 -12
  102. package/dist/cli/commands/understand-commands.d.ts +0 -3
  103. package/dist/cli/commands/understand-commands.js +0 -148
  104. package/dist/services/understand/hybrid-types.d.ts +0 -32
  105. package/dist/services/understand/hybrid-types.js +0 -1
  106. package/dist/services/understand/understand-hybrid-service.d.ts +0 -10
  107. package/dist/services/understand/understand-hybrid-service.js +0 -100
  108. package/dist/services/understand/understand-scan-service.d.ts +0 -28
  109. package/dist/services/understand/understand-scan-service.js +0 -170
  110. package/dist/services/understand/understand-types.d.ts +0 -50
  111. package/dist/services/understand/understand-types.js +0 -1
@@ -185,11 +185,11 @@ export function buildOrchestratorCanDoResult(input, signals) {
185
185
  // Q4 — context ratio gate. ≥0.95 → red-line; ≥0.85 → pre-compact.
186
186
  if (signals.q4ContextRatio >= ORCHESTRATOR_REDLINE_RATIO) {
187
187
  blockers.push(`context red-line (ratio=${signals.q4ContextRatio.toFixed(2)} ≥ ${ORCHESTRATOR_REDLINE_RATIO}); auto-compact now or push to next session`);
188
- suggestions.push('peaks compact auto --execute');
188
+ suggestions.push('peaks code auto-compact');
189
189
  }
190
190
  else if (signals.q4ContextRatio >= ORCHESTRATOR_PRECOMPACT_RATIO) {
191
191
  blockers.push(`context near limit (ratio=${signals.q4ContextRatio.toFixed(2)} ≥ ${ORCHESTRATOR_PRECOMPACT_RATIO}); auto-compact or push to next session`);
192
- suggestions.push('peaks compact auto --execute');
192
+ suggestions.push('peaks code auto-compact');
193
193
  }
194
194
  // Q3 — user-decision keywords → soft warning, NOT a blocker. The
195
195
  // LLM should AskUserQuestion, which is cheap.
@@ -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
+ }
@@ -15,6 +15,23 @@ export { resolveProjectRootForConfig, resolveCanonicalProjectRoot } from './conf
15
15
  */
16
16
  export declare function loadGlobalConfig(): ConfigV2 | null;
17
17
  export declare function isConfigLayer(value: string): value is ConfigLayer;
18
+ /**
19
+ * Machine-scoped context-window override key, the config twin of the
20
+ * `PEAKS_CONTEXT_WINDOW_TOKENS` env var (slice
21
+ * 2026-09-09-context-window-override). Written with
22
+ * `peaks config set --key context.windowTokens --value <positive-int>`
23
+ * (user layer) so the user pins it once instead of exporting an env var
24
+ * in every shell.
25
+ */
26
+ export declare const CONTEXT_WINDOW_TOKENS_CONFIG_KEY = "context.windowTokens";
27
+ /**
28
+ * Read the raw `context.windowTokens` value from the merged config, project
29
+ * layer over user layer (most specific wins). Returns the RAW value —
30
+ * validation lives in the context-window resolver so exactly one warning is
31
+ * emitted per bad value. Returns `undefined` when absent or unreadable;
32
+ * never throws (a corrupt config must not crash a context probe).
33
+ */
34
+ export declare function readContextWindowTokensOverride(projectRoot?: string | null): unknown;
18
35
  export declare function isSensitiveConfigPath(path: string): boolean;
19
36
  export declare function isLegacyConfigKey(path: string): boolean;
20
37
  export declare function containsSensitiveConfigValue(value: unknown): boolean;
@@ -176,6 +176,40 @@ function removeProjectSensitiveConfig(config) {
176
176
  export function isConfigLayer(value) {
177
177
  return value === 'user' || value === 'project';
178
178
  }
179
+ /**
180
+ * Machine-scoped context-window override key, the config twin of the
181
+ * `PEAKS_CONTEXT_WINDOW_TOKENS` env var (slice
182
+ * 2026-09-09-context-window-override). Written with
183
+ * `peaks config set --key context.windowTokens --value <positive-int>`
184
+ * (user layer) so the user pins it once instead of exporting an env var
185
+ * in every shell.
186
+ */
187
+ export const CONTEXT_WINDOW_TOKENS_CONFIG_KEY = 'context.windowTokens';
188
+ /**
189
+ * Read the raw `context.windowTokens` value from the merged config, project
190
+ * layer over user layer (most specific wins). Returns the RAW value —
191
+ * validation lives in the context-window resolver so exactly one warning is
192
+ * emitted per bad value. Returns `undefined` when absent or unreadable;
193
+ * never throws (a corrupt config must not crash a context probe).
194
+ */
195
+ export function readContextWindowTokensOverride(projectRoot) {
196
+ try {
197
+ const projectRaw = readProjectJsonFile(projectRoot ?? null);
198
+ if (isRecord(projectRaw)) {
199
+ const fromProject = getNestedValue(projectRaw, CONTEXT_WINDOW_TOKENS_CONFIG_KEY);
200
+ if (fromProject !== undefined)
201
+ return fromProject;
202
+ }
203
+ const userRaw = readUserJsonFile();
204
+ if (isRecord(userRaw)) {
205
+ return getNestedValue(userRaw, CONTEXT_WINDOW_TOKENS_CONFIG_KEY);
206
+ }
207
+ return undefined;
208
+ }
209
+ catch { // TODO(g2): legacy silent catch — never let config IO break a probe (grace: 1 minor release, v2.14.0)
210
+ return undefined;
211
+ }
212
+ }
179
213
  export function isSensitiveConfigPath(path) {
180
214
  const normalized = path.toLowerCase().replace(/[^a-z0-9]/g, '');
181
215
  return normalized.includes('apikey') || normalized.includes('accesskey') || normalized.includes('privatekey') || normalized.includes('token') || normalized.includes('secret') || normalized.includes('password') || normalized.includes('bearer') || normalized.includes('credential') || normalized.includes('auth');
@@ -45,7 +45,7 @@ export interface DispatchIdeCompactInput {
45
45
  * the compact should target. Default `'main'` — the orchestrator
46
46
  * (peaks-code body) runs in the main-session Claude Code window and
47
47
  * wants to compress *its* context, not a sub-agent's. Sub-agent
48
- * shells that spawn their own `peaks compact auto` flow pass
48
+ * shells that spawn their own `peaks code auto-compact` flow pass
49
49
  * `'sub-agent'` to preserve the legacy shell-spawn behaviour.
50
50
  *
51
51
  * Behaviour matrix (claude-code MVP):
@@ -66,7 +66,7 @@ export interface DispatchIdeCompactInput {
66
66
  * when the dispatch path completed without error — for
67
67
  * `llm-self-compress` the LLM still has to do the actual summary,
68
68
  * so the orchestrator MUST NOT treat `ok: true` as proof that the
69
- * context actually shrunk; the next `peaks compact auto` probe
69
+ * context actually shrunk; the next `peaks code auto-compact` probe
70
70
  * confirms.
71
71
  */
72
72
  export declare function dispatchIdeCompact(input: DispatchIdeCompactInput): Promise<CompactDispatchResult>;
@@ -7,7 +7,7 @@ import { getAdapter } from '../ide/ide-registry.js';
7
7
  * when the dispatch path completed without error — for
8
8
  * `llm-self-compress` the LLM still has to do the actual summary,
9
9
  * so the orchestrator MUST NOT treat `ok: true` as proof that the
10
- * context actually shrunk; the next `peaks compact auto` probe
10
+ * context actually shrunk; the next `peaks code auto-compact` probe
11
11
  * confirms.
12
12
  */
13
13
  export async function dispatchIdeCompact(input) {
@@ -77,7 +77,7 @@ export async function dispatchIdeCompact(input) {
77
77
  case 'ide-native':
78
78
  // Slice 2026-07-02-auto-compact-zero-pause: write the auto-compact
79
79
  // PreToolUse hook into `.claude/settings.local.json`. The hook
80
- // command (`peaks compact auto`) reads
80
+ // command (`peaks code auto-compact`) reads
81
81
  // `CLAUDE_CONTEXT_USAGE_PERCENT` on every subsequent Bash/Task
82
82
  // tool call from the runner and, at ratio ≥ 0.95, in-band spawns
83
83
  // `claude --compact` against the CURRENT runner (not a child
@@ -106,7 +106,7 @@ export async function dispatchIdeCompact(input) {
106
106
  };
107
107
  }
108
108
  // Lazy install: we only get here when the caller explicitly
109
- // invokes `peaks compact auto --execute`, so the user has
109
+ // invokes `peaks code auto-compact`, so the user has
110
110
  // already opted in. No zero-touch surprise on workspace init.
111
111
  return await dispatchIdeNativeHook({
112
112
  projectRoot: input.projectRoot,
@@ -144,7 +144,7 @@ export async function dispatchIdeCompact(input) {
144
144
  * into `.claude/settings.local.json` (idempotent; the install
145
145
  * service is a no-op if the hook is already present). On the next
146
146
  * Bash/Task tool call from the runner, the hook fires
147
- * `peaks compact auto` which in-band spawns
147
+ * `peaks code auto-compact` which in-band spawns
148
148
  * `claude --compact` against the CURRENT runner session.
149
149
  *
150
150
  * Returns `ok: true, pathway: 'ide-native'` regardless of install
@@ -1,5 +1,6 @@
1
1
  import { detectIdeFromEnv } from './ide-detect.js';
2
2
  import { getAdapter } from '../ide/ide-registry.js';
3
+ import { readContextWindowTokensOverride } from '../config/config-service.js';
3
4
  /**
4
5
  * Read env-var-based context percentage. Returns `null` when the env
5
6
  * is absent or unparseable. Adapter-driven: the caller passes the
@@ -74,11 +75,17 @@ export function readContextPercent(input) {
74
75
  // Fallback: the adapter owns any vendor-specific statusline /
75
76
  // transcript probe. When it returns a probe, honor it; otherwise
76
77
  // fall through to conservative-fallback.
78
+ //
79
+ // Slice 2026-09-09-context-window-override: the generic reader also
80
+ // hands the adapter the raw `context.windowTokens` config override, so
81
+ // the adapter's window resolver can prefer an explicit user value over
82
+ // its model-name heuristics (the env override arrives via `env`).
77
83
  const fallback = adapter.compact.readContextPercentFallback?.({
78
84
  projectRoot: input.projectRoot,
79
85
  sessionId: input.sessionId,
80
86
  outerSessionId: input.outerSessionId,
81
- env
87
+ env,
88
+ configWindowTokens: readContextWindowTokensOverride(input.projectRoot)
82
89
  });
83
90
  if (fallback)
84
91
  return fallback;
@@ -45,7 +45,7 @@ export type CompactTrigger = {
45
45
  }
46
46
  /**
47
47
  * Part 22: auto-fire zone (0.80 ≤ ratio < 0.85). peaks-loop
48
- * preempts and runs `peaks compact auto --execute` itself
48
+ * preempts and runs `peaks code auto-compact` itself
49
49
  * without LLM involvement. The LLM is not asked to "decide";
50
50
  * the toolkit is applied synchronously.
51
51
  */
@@ -186,6 +186,18 @@ export interface ContextPercentProbe {
186
186
  * percent sources.
187
187
  */
188
188
  readonly capacityTokens?: number;
189
+ /**
190
+ * Which layer produced `capacityTokens` (token-based sources only):
191
+ * - `env-override` — `PEAKS_CONTEXT_WINDOW_TOKENS`
192
+ * - `config` — `context.windowTokens` (`peaks config set`)
193
+ * - `model-heuristic` — `[1M]` suffix / known-1M model allowlist
194
+ * - `default` — 200K safe default
195
+ * Undefined for byte / percent sources (`user-overridden`, `${ideId}-env`,
196
+ * `statusline-poll`), which have no token window. Slice
197
+ * 2026-09-09-context-window-override: lets a wrong window be diagnosed in
198
+ * one read instead of guessing which heuristic fired.
199
+ */
200
+ readonly capacitySource?: string;
189
201
  readonly ide: string;
190
202
  readonly capturedAt: string;
191
203
  }
@@ -191,9 +191,9 @@ function renderContextBlock(probe) {
191
191
  const usedPct = (probe.ratio * 100).toFixed(1);
192
192
  const freePct = ((1 - probe.ratio) * 100).toFixed(1);
193
193
  const action = probe.ratio >= 0.95
194
- ? 'RED-LINE — call `peaks compact auto --execute` immediately.'
194
+ ? 'RED-LINE — call `peaks code auto-compact` immediately.'
195
195
  : probe.ratio >= 0.85
196
- ? 'pre-compact zone — consider running `peaks compact auto --execute` proactively.'
196
+ ? 'pre-compact zone — consider running `peaks code auto-compact` proactively.'
197
197
  : probe.ratio >= 0.5
198
198
  ? 'soft-warn zone — continue working; the next dispatch will re-check.'
199
199
  : 'plenty of room — continue without compacting.';
@@ -13,8 +13,8 @@ export declare const ContextJsonSchema: z.ZodObject<{
13
13
  path: z.ZodString;
14
14
  kind: z.ZodEnum<{
15
15
  doc: "doc";
16
- source: "source";
17
16
  config: "config";
17
+ source: "source";
18
18
  test: "test";
19
19
  }>;
20
20
  lines: z.ZodNumber;
@@ -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),