peaks-loop 4.0.51 → 4.0.53

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 (65) hide show
  1. package/CHANGELOG.md +59 -1
  2. package/README-en.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/cli/commands/baseline-commands.js +11 -1
  5. package/dist/cli/commands/codegraph-command-runtime.d.ts +28 -0
  6. package/dist/cli/commands/codegraph-command-runtime.js +72 -0
  7. package/dist/cli/commands/codegraph-commands.d.ts +2 -11
  8. package/dist/cli/commands/codegraph-commands.js +173 -228
  9. package/dist/cli/commands/codegraph-status-command.d.ts +22 -0
  10. package/dist/cli/commands/codegraph-status-command.js +299 -0
  11. package/dist/cli/commands/core/memory-command.js +6 -2
  12. package/dist/cli/commands/job-commands.js +121 -30
  13. package/dist/cli/commands/project-commands.js +13 -3
  14. package/dist/cli/commands/request-commands.js +19 -8
  15. package/dist/cli/commands/slice-commands.js +2 -2
  16. package/dist/services/artifacts/artifact-prerequisites.js +23 -1
  17. package/dist/services/codegraph/codegraph-autorefresh.d.ts +16 -0
  18. package/dist/services/codegraph/codegraph-autorefresh.js +51 -5
  19. package/dist/services/codegraph/codegraph-config-repair-writer.d.ts +88 -0
  20. package/dist/services/codegraph/codegraph-config-repair-writer.js +322 -0
  21. package/dist/services/codegraph/codegraph-exclude-integrity.d.ts +20 -2
  22. package/dist/services/codegraph/codegraph-exclude-integrity.js +24 -3
  23. package/dist/services/codegraph/codegraph-exclude-reconciler.d.ts +23 -2
  24. package/dist/services/codegraph/codegraph-exclude-reconciler.js +123 -12
  25. package/dist/services/codegraph/codegraph-exclude-repair.d.ts +109 -55
  26. package/dist/services/codegraph/codegraph-exclude-repair.js +249 -195
  27. package/dist/services/codegraph/codegraph-include-reconciler.d.ts +10 -0
  28. package/dist/services/codegraph/codegraph-include-reconciler.js +160 -0
  29. package/dist/services/codegraph/codegraph-index-integrity.d.ts +268 -0
  30. package/dist/services/codegraph/codegraph-index-integrity.js +471 -0
  31. package/dist/services/codegraph/codegraph-service.d.ts +54 -0
  32. package/dist/services/codegraph/codegraph-service.js +84 -1
  33. package/dist/services/dispatch/sub-agent-dispatcher.d.ts +0 -1
  34. package/dist/services/dispatch/sub-agent-dispatcher.js +0 -3
  35. package/dist/services/doctor/doctor-service/checks/codegraph-exclude-integrity.js +19 -4
  36. package/dist/services/doctor/doctor-service/checks/codegraph-index-integrity.d.ts +54 -0
  37. package/dist/services/doctor/doctor-service/checks/codegraph-index-integrity.js +151 -0
  38. package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.js +10 -10
  39. package/dist/services/doctor/doctor-service/plugin-registry.js +2 -0
  40. package/dist/services/doctor/doctor-service/types.d.ts +25 -0
  41. package/dist/services/memory/project-memory-service/index/kind-dispatch.js +48 -13
  42. package/dist/services/memory/project-memory-service/index.d.ts +5 -3
  43. package/dist/services/memory/project-memory-service/index.js +2 -2
  44. package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +15 -1
  45. package/dist/services/memory/project-memory-service/parsers/frontmatter.js +34 -6
  46. package/dist/services/memory/project-memory-service/parsers/markdown-pure.d.ts +27 -1
  47. package/dist/services/memory/project-memory-service/parsers/markdown-pure.js +92 -7
  48. package/dist/services/memory/project-memory-service/types.d.ts +86 -0
  49. package/dist/services/prd/handoff-gate-evidence.d.ts +1 -0
  50. package/dist/services/prd/handoff-gate-evidence.js +77 -0
  51. package/dist/services/slice/slice-check-types.d.ts +1 -1
  52. package/dist/services/workspace/runtime-layout.d.ts +91 -0
  53. package/dist/services/workspace/runtime-layout.js +148 -0
  54. package/dist/services/workspace/workspace-claude-settings-materializer.js +14 -0
  55. package/package.json +6 -6
  56. package/scripts/clean-dist.mjs +15 -3
  57. package/scripts/sync-version.mjs +26 -4
  58. package/skills/bee/peaks-prd/SKILL.md +1 -1
  59. package/skills/bee/peaks-qa/references/qa-skill-presence.md +1 -1
  60. package/skills/bee/peaks-rd/references/skill-presence-and-title.md +1 -1
  61. package/skills/bee/peaks-sc/SKILL.md +1 -1
  62. package/skills/bee/peaks-txt/SKILL.md +3 -3
  63. package/skills/peaks-code/SKILL.md +1 -1
  64. package/skills/peaks-code/references/project-memory-loading.md +19 -1
  65. package/skills/peaks-code/references/step-11-memory-sediment.md +1 -1
@@ -1,11 +1,27 @@
1
+ // src/cli/commands/codegraph-commands.ts
2
+ //
3
+ // The codegraph verbs that MUTATE or PROXY: `repair-exclude`, `repair-index`,
4
+ // `init`, `affected`, plus the commander registration for every codegraph
5
+ // subcommand.
6
+ //
7
+ // D1 (rid 2026-09-17-oversize-and-scale, the 800-line file-size cap) moved
8
+ // the shared invocation runtime to `codegraph-command-runtime.ts` and the
9
+ // `status` integrity gate to `codegraph-status-command.ts`, both verbatim.
10
+ // This path keeps its public surface: `registerCodegraphCommands` is defined
11
+ // here and `rewriteBareCodegraphHints` / `attributeUpstreamUpToDateLine` are
12
+ // re-exported below, so every existing importer still resolves.
1
13
  import { InvalidArgumentError } from 'commander';
2
14
  import { statSync } from 'node:fs';
3
15
  import { resolve } from 'node:path';
4
- import { createCodegraphInvocation, executeCodegraphInvocation, defaultCodegraphInitGuard, writeCodegraphMarker, writeCodegraphAffectedContext, CodegraphInitConflictError } from '../../services/codegraph/codegraph-service.js';
5
- import { CODEGRAPH_INTEGRITY_EXIT_CODE, inspectCodegraphExcludeIntegrity, isCodegraphExcludeConfigPresent, renderCodegraphExcludeIntegrityLines } from '../../services/codegraph/codegraph-exclude-integrity.js';
16
+ import { createCodegraphInvocation, executeCodegraphInvocation, defaultCodegraphInitGuard, resolveProjectRoot, writeCodegraphMarker, writeCodegraphAffectedContext, CodegraphInitConflictError } from '../../services/codegraph/codegraph-service.js';
6
17
  import { repairCodegraphExcludeFromProject } from '../../services/codegraph/codegraph-exclude-repair.js';
7
18
  import { fail, ok } from 'peaks-loop-shared/result';
8
- import { getErrorMessage, printResult, redactSensitiveErrorMessage } from '../cli-helpers.js';
19
+ import { printResult, redactSensitiveErrorMessage } from '../cli-helpers.js';
20
+ import { printCodegraphFailure, runCodegraphCommand } from './codegraph-command-runtime.js';
21
+ import { runCodegraphStatusCommand } from './codegraph-status-command.js';
22
+ // Re-exported so the D1 split is invisible to importers of THIS path.
23
+ export { rewriteBareCodegraphHints } from './codegraph-command-runtime.js';
24
+ export { attributeUpstreamUpToDateLine } from './codegraph-status-command.js';
9
25
  function addPeaksJsonOption(command) {
10
26
  return command.option('--peaks-json', 'print Peaks error envelope as machine-readable JSON');
11
27
  }
@@ -22,219 +38,118 @@ function parsePositiveInteger(value) {
22
38
  }
23
39
  return parsed;
24
40
  }
25
- function printCodegraphFailure(io, command, error, asJson, exitCode = 1) {
26
- printResult(io, fail(command, 'CODEGRAPH_COMMAND_FAILED', redactSensitiveErrorMessage(getErrorMessage(error)), {}, ['Check the codegraph command options and project path before retrying']), asJson);
27
- process.exitCode = exitCode;
28
- }
29
- /**
30
- * Rewrites bare upstream `codegraph <subcommand>` hints to the peaks-loop
31
- * equivalent (`peaks codegraph <subcommand>`). The upstream binary is a
32
- * nested transitive dependency and is NOT on PATH, so an LLM that follows
33
- * a bare hint like `Run "codegraph init" to initialize` would hit
34
- * "command not found". Already-prefixed `peaks codegraph ...` hints and
35
- * other `codegraph` references (e.g. `@colbymchenry/codegraph`) are left
36
- * untouched.
37
- */
38
- export function rewriteBareCodegraphHints(text) {
39
- return text.replace(/(?<![\w-])(?<!peaks\s)codegraph(?=\s+(?:status|init|index|query|files|context|affected)\b)/g, 'peaks codegraph');
40
- }
41
- const ANSI_SGR_PATTERN = /\x1b\[[0-9;]*m/g;
42
- /**
43
- * Upstream `status` answers a different question than peaks-loop's
44
- * integrity gate: upstream says "the on-disk graph matches the last scan"
45
- * (true), peaks says "that graph covers the repository" (false when rules
46
- * exclude tracked files). Both verdicts are correct, but an unqualified
47
- * `[OK] Index is up to date` printed above our `[FAIL] ...` reads as
48
- * "nothing to see here" — and the OK is the line the eye lands on first.
49
- * The exit code and the JSON envelope are already right; only this line
50
- * lies by juxtaposition.
51
- *
52
- * So: keep upstream's wording — the line stays recognizable, and the
53
- * Files/Nodes counts around it are untouched — but drop the bare OK
54
- * marker and name the only question it answers. Clean runs never reach
55
- * this, so their output stays byte-identical.
56
- *
57
- * The match is anchored to THAT line. An earlier version keyed on
58
- * `includes('up to date')`, which is content-blind: upstream prints other
59
- * `[OK] ... are up to date` lines (a language-server or watcher line is the
60
- * observed one), and each of those was rewritten into a claim about the
61
- * INDEX — a misattribution introduced by a change whose entire purpose was
62
- * to stop misleading output. Anything that is not the index line is passed
63
- * through byte-for-byte, tail note and all.
64
- */
65
- const INDEX_UP_TO_DATE_RE = /^\[OK\]\s+Index is up to date\b/i;
66
- export function attributeUpstreamUpToDateLine(stdout) {
67
- return stdout
68
- .split('\n')
69
- .map((line) => {
70
- const visible = line.replace(ANSI_SGR_PATTERN, '').trim();
71
- if (!INDEX_UP_TO_DATE_RE.test(visible)) {
72
- return line;
73
- }
74
- // Only the OK marker is downgraded and the attribution appended: the
75
- // rest of the line — including whatever upstream wrote after it — is
76
- // preserved, so nothing upstream actually said is replaced.
77
- const withoutOk = visible.replace(/^\[OK\]\s*/, '');
78
- return `[i] ${withoutOk} (upstream: matches the last scan only; repository coverage is answered below)`;
79
- })
80
- .join('\n');
81
- }
82
- async function runCodegraphCommand(io, command, options, asJson, attributeStdout) {
83
- try {
84
- const invocation = createCodegraphInvocation(options);
85
- const result = await executeCodegraphInvocation(invocation);
86
- if (result.exitCode !== null && result.exitCode !== 0 && asJson === true) {
87
- printCodegraphFailure(io, command, new Error(result.stderr || result.stdout || `codegraph exited with code ${result.exitCode}`), true, result.exitCode);
88
- return;
89
- }
90
- const didFail = result.exitCode !== null && result.exitCode !== 0;
91
- const rewritten = rewriteBareCodegraphHints(result.stdout);
92
- const stdout = attributeStdout === undefined ? rewritten : attributeStdout(rewritten);
93
- const stderr = rewriteBareCodegraphHints(result.stderr);
94
- if (stdout.length > 0) {
95
- io.stdout((didFail ? redactSensitiveErrorMessage(stdout) : stdout).trimEnd());
96
- }
97
- if (stderr.length > 0) {
98
- io.stderr((didFail ? redactSensitiveErrorMessage(stderr) : stderr).trimEnd());
99
- }
100
- if (didFail) {
101
- process.exitCode = result.exitCode;
102
- }
103
- }
104
- catch (error) {
105
- printCodegraphFailure(io, command, error, asJson);
41
+ // " Include now admits 7 of 9 extractor-supported tracked file(s)." — printed
42
+ // only when the ratio reports a SHORTFALL, i.e. when the repair has left
43
+ // files the extractor supports unadmitted. That is the one reading of this
44
+ // sentence an operator can act on.
45
+ //
46
+ // What this used to say, and why it was wrong: the denominator was fed from
47
+ // the reconciler's admitted count — the NUMERATOR's own expression — so the
48
+ // clause could only ever print "N of N", and it was gated on
49
+ // `after > before` with a "(was N)" trailer computed by a second full glob
50
+ // pass. A repo with 10 supported tracked files of which 5 were admitted was
51
+ // told "5 of 5", asserting total coverage that nothing had measured. The
52
+ // denominator is now an independent measurement (see the report field), and
53
+ // the trailer — whose only consumer was this sentence — is gone with the
54
+ // pass that produced it (perf: 11.73 ms of the repair entry's +14.13 ms, on
55
+ // every automatic seam including no-ops).
56
+ //
57
+ // Silence when the ratio is complete is deliberate: "2 of 2" is true but
58
+ // says nothing, and the patterns that changed are already named by the
59
+ // caller's own sentence.
60
+ function admittingClause(report) {
61
+ if (report.trackedSourceCount === 0 || report.includeAdmittedAfter >= report.trackedSourceCount) {
62
+ return '';
106
63
  }
64
+ return ` Include now admits ${report.includeAdmittedAfter} of ${report.trackedSourceCount} extractor-supported tracked file(s).`;
107
65
  }
108
- /**
109
- * `--peaks-json` machine report for `status`. Carries the upstream
110
- * result AND the peaks-loop integrity verdict as one JSON document so a
111
- * CI job can gate on `data.integrity.gap` / `data.integrity.rulesToRemove`
112
- * without scraping human text.
113
- */
114
- async function runCodegraphStatusJson(io, options, integrity, integrityWarning) {
115
- let result;
116
- try {
117
- result = await executeCodegraphInvocation(createCodegraphInvocation({ subcommand: 'status', project: options.project }));
118
- }
119
- catch (error) {
120
- printCodegraphFailure(io, 'codegraph.status', error, true);
121
- return;
122
- }
123
- const upstream = {
124
- exitCode: result.exitCode,
125
- stdout: rewriteBareCodegraphHints(result.stdout).trimEnd(),
126
- stderr: redactSensitiveErrorMessage(rewriteBareCodegraphHints(result.stderr)).trimEnd()
127
- };
128
- const upstreamFailed = result.exitCode !== null && result.exitCode !== 0;
129
- if (integrity?.gap === true) {
130
- printResult(io, fail('codegraph.status', 'CODEGRAPH_INDEX_INCOMPLETE', `codegraph index is incomplete: ${integrity.excludedTrackedCount} of ${integrity.trackedSourceCount} tracked source files are excluded by ${integrity.rulesToRemove.length} rule(s).`, { upstream, integrity, integrityWarning }, ['Run `peaks codegraph repair-exclude --project <root>` to drop the offending rules and rebuild the index.']), true);
131
- }
132
- else if (upstreamFailed) {
133
- printResult(io, fail('codegraph.status', 'CODEGRAPH_COMMAND_FAILED', redactSensitiveErrorMessage(upstream.stderr || upstream.stdout || `codegraph exited with code ${String(result.exitCode)}`), { upstream, integrity, integrityWarning }, ['Check the codegraph project path before retrying']), true);
134
- }
135
- else {
136
- printResult(io, ok('codegraph.status', { upstream, integrity, integrityWarning }), true);
137
- }
138
- if (upstreamFailed) {
139
- process.exitCode = result.exitCode ?? 1;
66
+ const REPAIR_MODES = {
67
+ exclude: {
68
+ commandId: 'codegraph.repair-exclude',
69
+ reindex: true,
70
+ noopNote: 'Nothing to repair in the codegraph config; nothing was written.'
71
+ },
72
+ index: {
73
+ commandId: 'codegraph.repair-index',
74
+ // 'force' is upstream's `clear()` + full re-index — the only documented
75
+ // way to drop rows for files deleted in an earlier commit.
76
+ reindex: 'force',
77
+ noopNote: 'The codegraph config already admits every supported tracked file and blocks none; the index was still rebuilt from scratch.'
140
78
  }
79
+ };
80
+ // One sentence, the same shape in both modes, because both modes run the
81
+ // same two-axis repair — only the rebuild differs.
82
+ //
83
+ // A1 (`2026-09-17-codegraph-msg-and-refresh`): EACH COUNT NAMES ITS OWN AXIS.
84
+ // The previous wording ended one sentence about both axes with a single
85
+ // "recovering N tracked source file(s)", fed by `filesRecovered` — the
86
+ // EXCLUDE axis' counter. On this repo it printed "Added 5 include pattern(s)
87
+ // and removed 0 exclude rule(s), recovering 0 tracked source file(s)." while
88
+ // those 5 patterns had just admitted 31 tracked source files (30 `.mjs` + 1
89
+ // `.cjs`): a true statement about the axis that did not move, read as a
90
+ // verdict on the one that did. The `admittingClause` could not correct it
91
+ // either — it is silent when the coverage ratio is complete, which is
92
+ // exactly the state a fresh include repair produces.
93
+ //
94
+ // So: the include clause carries the include axis' own file delta and the
95
+ // exclude clause carries the exclude axis' own count, and a reader cannot
96
+ // attribute either number to the other clause. No combined total is printed,
97
+ // because there is no honest single number here — the two axes recover
98
+ // disjoint sets (one widens admission, the other unblocks admitted files)
99
+ // and only the first of them is cheap enough to measure on every repair.
100
+ function appliedRepairNote(report) {
101
+ return (`Added ${report.includePatternsAdded.length} include pattern(s), newly admitting ${report.includeFilesRecovered} tracked source file(s), and removed ${report.rulesRemoved.length} exclude rule(s), recovering ${report.filesRecovered} tracked source file(s) that a rule had been hiding.` +
102
+ admittingClause(report) +
103
+ ` Config backed up to ${report.backupPath ?? ''}.`);
141
104
  }
142
105
  /**
143
- * `peaks codegraph status` with an integrity gate.
144
- *
145
- * The upstream status is still proxied verbatim (that is what the
146
- * command has always done), but a clean upstream "index is up to date"
147
- * is no longer sufficient: when git-tracked source files are being
148
- * excluded by the config, the command says so, names the rules and
149
- * files, and exits non-zero.
150
- *
151
- * Read-only by construction — it imports the integrity inspector, never
152
- * the repair writer. Fixing the config is `peaks codegraph init`
153
- * (fresh) or `peaks codegraph repair-exclude` (explicit).
154
- */
155
- async function runCodegraphStatusCommand(io, options, asJson) {
156
- let integrity = null;
157
- let integrityWarning = null;
158
- const projectRoot = resolve(options.project);
159
- try {
160
- // Never initialized here → no exclude list is in play, so there is
161
- // nothing to report. Staying silent keeps `status` honest and
162
- // unchanged for projects that do not use codegraph at all.
163
- integrity = isCodegraphExcludeConfigPresent(projectRoot)
164
- ? inspectCodegraphExcludeIntegrity(projectRoot)
165
- : null;
166
- }
167
- catch (error) {
168
- // Not a git work tree, no config yet, malformed config — the
169
- // upstream status is still worth printing, so degrade to a warning
170
- // instead of failing the whole command.
171
- integrityWarning = getErrorMessage(error);
172
- }
173
- if (asJson === true) {
174
- await runCodegraphStatusJson(io, options, integrity, integrityWarning);
175
- }
176
- else {
177
- // Only when the gate found a gap: upstream's `[OK] Index is up to
178
- // date` answers "consistent with the last scan", and printing it
179
- // unqualified right above our `[FAIL]` tells the reader two opposite
180
- // things at once. Clean runs get no transform and stay byte-identical.
181
- await runCodegraphCommand(io, 'codegraph.status', { subcommand: 'status', project: options.project }, false, integrity?.gap === true ? attributeUpstreamUpToDateLine : undefined);
182
- if (integrityWarning !== null) {
183
- io.stdout(`[WARN] codegraph exclude integrity not evaluated: ${integrityWarning}`);
184
- }
185
- else if (integrity !== null) {
186
- for (const line of renderCodegraphExcludeIntegrityLines(integrity)) {
187
- io.stdout(line);
188
- }
189
- }
190
- }
191
- if (integrity?.gap === true) {
192
- process.exitCode = CODEGRAPH_INTEGRITY_EXIT_CODE;
193
- }
194
- }
195
- /**
196
- * Explicit repair path: reconcile → drop offending rules → back up the
197
- * config → rebuild the index. Mirrors the automatic step `init` runs
106
+ * Explicit repair path: reconcile both config axes → back up the config →
107
+ * rewrite it → rebuild the index. Mirrors the automatic step `init` runs
198
108
  * after a fresh upstream init, for workspaces that were already
199
109
  * initialized before the integrity gate existed.
200
110
  */
201
- async function runCodegraphRepairExcludeCommand(io, options, asJson) {
111
+ async function runCodegraphRepairCommand(io, options, asJson, mode) {
112
+ const spec = REPAIR_MODES[mode];
202
113
  let projectRoot;
203
114
  try {
204
- const candidate = resolve(options.project);
205
- if (!statSync(candidate).isDirectory()) {
206
- throw new Error('Project path must exist and be a directory');
207
- }
208
- projectRoot = candidate;
115
+ // `resolveProjectRoot`, not a hand-rolled `resolve` + `statSync`: it is
116
+ // the canonicalizer every codegraph invocation already goes through
117
+ // (`createCodegraphInvocation`), so the config path this verb reports
118
+ // and writes names the SAME directory the spawn and the other verbs use
119
+ // even when `--project` is a symlink, a short name or a differently
120
+ // cased alias. The error path is unchanged.
121
+ projectRoot = resolveProjectRoot(options.project);
209
122
  }
210
123
  catch (error) {
211
- printCodegraphFailure(io, 'codegraph.repair-exclude', error, asJson);
124
+ printCodegraphFailure(io, spec.commandId, error, asJson);
212
125
  return;
213
126
  }
214
- const report = await repairCodegraphExcludeFromProject(projectRoot);
127
+ const report = await repairCodegraphExcludeFromProject(projectRoot, undefined, {
128
+ reindex: spec.reindex
129
+ });
215
130
  // Where the notes go matters: `printResult` renders every `warnings`
216
131
  // entry to stderr with a `warning: ` prefix, so a confirmation parked
217
132
  // in the third slot reads as a problem — and a real warning parked
218
133
  // there double-prefixes. Confirmations go to `nextActions`; only a
219
134
  // genuine `report.warning` reaches `warnings`, verbatim.
220
- const confirmations = [];
221
- if (report.applied) {
222
- confirmations.push(`Removed ${report.rulesRemoved.length} exclude rule(s), recovering ${report.filesRecovered} tracked source file(s). Config backed up to ${report.backupPath}.`);
223
- }
224
- else {
225
- confirmations.push('No tracked source file is excluded by the codegraph config; nothing to repair.');
226
- }
227
- if (report.applied) {
135
+ const confirmations = [
136
+ report.applied ? appliedRepairNote(report) : spec.noopNote
137
+ ];
138
+ if (report.applied || mode === 'index') {
228
139
  confirmations.push('Re-run `peaks codegraph status --project <root>` to confirm the gap is closed.');
229
140
  }
230
- printResult(io, ok('codegraph.repair-exclude', {
141
+ printResult(io, ok(spec.commandId, {
231
142
  applied: report.applied,
232
143
  rulesRemoved: report.rulesRemoved,
144
+ includePatternsAdded: report.includePatternsAdded,
233
145
  filesRecovered: report.filesRecovered,
146
+ includeFilesRecovered: report.includeFilesRecovered,
147
+ includeAdmittedAfter: report.includeAdmittedAfter,
234
148
  trackedSourceCount: report.trackedSourceCount,
235
149
  configPath: report.configPath,
236
150
  backupPath: report.backupPath,
237
151
  reindexed: report.reindexed,
152
+ forcedRebuild: report.forcedRebuild,
238
153
  warning: report.warning
239
154
  }, report.warning === null ? [] : [report.warning], confirmations), asJson);
240
155
  // A repair that could not run to completion (or could not reindex)
@@ -325,18 +240,20 @@ async function runCodegraphInitCommand(io, options, asJson) {
325
240
  catch {
326
241
  // intentionally swallowed — surface as warning below
327
242
  }
328
- // Upstream `init` writes its 99-rule default `exclude` template,
329
- // some of which collide with real source directories in this
330
- // project. Left alone, a fresh clone / new machine gets an index
331
- // that silently omits tracked source files while `status` says it
332
- // is up to date. Reconcile now, drop the offending rules, and
333
- // rebuild the index — a fresh init is the one moment this is both
334
- // safe (nothing has been indexed yet) and necessary (`.codegraph/`
335
- // is gitignored, so every clone starts from the default template).
243
+ // Upstream `init` writes BOTH default templates: a 99-rule `exclude`
244
+ // list (some rules collide with real source directories in this
245
+ // project) and a 32-entry `include` list (which omits five extensions
246
+ // upstream's own extractor supports). Left alone, a fresh clone / new
247
+ // machine gets an index that silently omits tracked source files while
248
+ // `status` says it is up to date. Reconcile now, append the missing
249
+ // include patterns, drop the offending rules, and rebuild the index — a
250
+ // fresh init is the one moment this is both safe (nothing has been
251
+ // indexed yet) and necessary (`.codegraph/` is gitignored, so every
252
+ // clone starts from the default templates).
336
253
  //
337
254
  // Never throws: a failure here is reported as a warning, not a
338
255
  // failed init (the init itself already succeeded).
339
- const excludeRepair = await repairCodegraphExcludeFromProject(projectRoot);
256
+ const configRepair = await repairCodegraphExcludeFromProject(projectRoot);
340
257
  // These are confirmations, not warnings: `printResult` renders every
341
258
  // `warnings` entry to stderr behind a `warning: ` prefix, so a fully
342
259
  // successful init used to print a wall of `warning:` lines for what
@@ -344,9 +261,19 @@ async function runCodegraphInitCommand(io, options, asJson) {
344
261
  const initNotes = [
345
262
  `Stamped peaks-loop marker at ${guardOutcome.codegraphDir}/.peaks-loop-marker`
346
263
  ];
347
- if (excludeRepair.applied) {
348
- initNotes.push(`Removed ${excludeRepair.rulesRemoved.length} exclude rule(s) that blocked tracked source files, recovering ${excludeRepair.filesRecovered} file(s); config backed up to ${excludeRepair.backupPath}.`);
349
- if (excludeRepair.reindexed) {
264
+ if (configRepair.applied) {
265
+ if (configRepair.includePatternsAdded.length > 0) {
266
+ // A1 (2026-09-17): the include axis names its OWN file delta here too.
267
+ // Naming the patterns alone left the same gap the repair note had —
268
+ // a reader learned which extensions were appended but not how many
269
+ // tracked files that admitted, and this note is the ONLY place a
270
+ // fresh `init` reports the include repair.
271
+ initNotes.push(`Added ${configRepair.includePatternsAdded.length} include pattern(s) upstream's extractor supports but its default template omits, newly admitting ${configRepair.includeFilesRecovered} tracked source file(s) (${configRepair.includePatternsAdded.join(', ')}).${admittingClause(configRepair)}`);
272
+ }
273
+ if (configRepair.rulesRemoved.length > 0) {
274
+ initNotes.push(`Removed ${configRepair.rulesRemoved.length} exclude rule(s) that blocked tracked source files, recovering ${configRepair.filesRecovered} file(s); config backed up to ${configRepair.backupPath ?? ''}.`);
275
+ }
276
+ if (configRepair.reindexed) {
350
277
  initNotes.push('Rebuilt the codegraph index over the recovered files.');
351
278
  }
352
279
  }
@@ -355,17 +282,21 @@ async function runCodegraphInitCommand(io, options, asJson) {
355
282
  codegraphDir: guardOutcome.codegraphDir,
356
283
  markerWritten: true,
357
284
  excludeRepair: {
358
- applied: excludeRepair.applied,
359
- rulesRemoved: excludeRepair.rulesRemoved,
360
- filesRecovered: excludeRepair.filesRecovered,
361
- reindexed: excludeRepair.reindexed,
362
- backupPath: excludeRepair.backupPath,
363
- warning: excludeRepair.warning
285
+ applied: configRepair.applied,
286
+ rulesRemoved: configRepair.rulesRemoved,
287
+ includePatternsAdded: configRepair.includePatternsAdded,
288
+ filesRecovered: configRepair.filesRecovered,
289
+ includeFilesRecovered: configRepair.includeFilesRecovered,
290
+ includeAdmittedAfter: configRepair.includeAdmittedAfter,
291
+ trackedSourceCount: configRepair.trackedSourceCount,
292
+ reindexed: configRepair.reindexed,
293
+ backupPath: configRepair.backupPath,
294
+ warning: configRepair.warning
364
295
  }
365
296
  },
366
297
  // Verbatim: `printResult` supplies the `warning: ` prefix, so a
367
298
  // prefix added here would render as `warning: warning: ...`.
368
- excludeRepair.warning === null ? [] : [excludeRepair.warning], initNotes), asJson);
299
+ configRepair.warning === null ? [] : [configRepair.warning], initNotes), asJson);
369
300
  }
370
301
  catch (error) {
371
302
  printCodegraphFailure(io, 'codegraph.init', error, asJson);
@@ -442,41 +373,55 @@ export function registerCodegraphCommands(program, io) {
442
373
  addProjectOption(codegraph.command('status').description('Show codegraph status, including the exclude integrity gate')).action((options) => runCodegraphStatusCommand(io, options, options.peaksJson));
443
374
  addProjectOption(codegraph
444
375
  .command('repair-exclude')
445
- .description('Drop codegraph exclude rules that block tracked source files, then rebuild the index')).action((options) => runCodegraphRepairExcludeCommand(io, options, options.peaksJson));
376
+ .description('Normalize the codegraph include list, drop exclude rules that block tracked source files, then rebuild the index')).action((options) => runCodegraphRepairCommand(io, options, options.peaksJson, 'exclude'));
377
+ addProjectOption(codegraph
378
+ .command('repair-index')
379
+ .description('Repair both codegraph config axes, then rebuild the index from scratch (drops rows for deleted files)')).action((options) => runCodegraphRepairCommand(io, options, options.peaksJson, 'index'));
446
380
  addProjectOption(codegraph.command('init').description('Initialize codegraph for a project')).action((options) => runCodegraphInitCommand(io, options, options.peaksJson));
447
381
  addProjectOption(codegraph
448
382
  .command('index')
449
383
  .description('Index a project with codegraph')
450
384
  .option('--force', 'force reindexing')
451
- .option('--quiet', 'reduce upstream output')).action((options) => runCodegraphCommand(io, 'codegraph.index', {
452
- subcommand: 'index',
453
- project: options.project,
454
- ...(options.force === true ? { force: true } : {}),
455
- ...(options.quiet === true ? { quiet: true } : {})
456
- }, options.peaksJson));
385
+ .option('--quiet', 'reduce upstream output')).action(async (options) => {
386
+ // `runCodegraphCommand` returns "upstream failed" for the one caller
387
+ // that ranks exit-code precedence (`status`); every other command just
388
+ // awaits it, so it is discarded here rather than leaked to commander.
389
+ await runCodegraphCommand(io, 'codegraph.index', {
390
+ subcommand: 'index',
391
+ project: options.project,
392
+ ...(options.force === true ? { force: true } : {}),
393
+ ...(options.quiet === true ? { quiet: true } : {})
394
+ }, options.peaksJson);
395
+ });
457
396
  addProjectOption(codegraph
458
397
  .command('query')
459
398
  .description('Query codegraph')
460
399
  .argument('<search>', 'search text')
461
400
  .option('--json', 'forward JSON output flag to upstream codegraph')
462
- .option('--limit <n>', 'maximum result count', parsePositiveInteger)).action((search, options) => runCodegraphCommand(io, 'codegraph.query', {
463
- subcommand: 'query',
464
- project: options.project,
465
- search,
466
- ...(options.json === true ? { json: true } : {}),
467
- ...(options.limit !== undefined ? { limit: options.limit } : {})
468
- }, options.peaksJson));
401
+ .option('--limit <n>', 'maximum result count', parsePositiveInteger)).action(async (search, options) => {
402
+ await runCodegraphCommand(io, 'codegraph.query', {
403
+ subcommand: 'query',
404
+ project: options.project,
405
+ search,
406
+ ...(options.json === true ? { json: true } : {}),
407
+ ...(options.limit !== undefined ? { limit: options.limit } : {})
408
+ }, options.peaksJson);
409
+ });
469
410
  addProjectOption(codegraph
470
411
  .command('files')
471
412
  .description('List codegraph files')
472
413
  .option('--json', 'forward JSON output flag to upstream codegraph')
473
- .option('--max-depth <n>', 'maximum traversal depth', parsePositiveInteger)).action((options) => runCodegraphCommand(io, 'codegraph.files', {
474
- subcommand: 'files',
475
- project: options.project,
476
- ...(options.json === true ? { json: true } : {}),
477
- ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {})
478
- }, options.peaksJson));
479
- addProjectOption(codegraph.command('context').description('Build task context with codegraph').argument('<task>', 'task text')).action((task, options) => runCodegraphCommand(io, 'codegraph.context', { subcommand: 'context', project: options.project, task }, options.peaksJson));
414
+ .option('--max-depth <n>', 'maximum traversal depth', parsePositiveInteger)).action(async (options) => {
415
+ await runCodegraphCommand(io, 'codegraph.files', {
416
+ subcommand: 'files',
417
+ project: options.project,
418
+ ...(options.json === true ? { json: true } : {}),
419
+ ...(options.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {})
420
+ }, options.peaksJson);
421
+ });
422
+ addProjectOption(codegraph.command('context').description('Build task context with codegraph').argument('<task>', 'task text')).action(async (task, options) => {
423
+ await runCodegraphCommand(io, 'codegraph.context', { subcommand: 'context', project: options.project, task }, options.peaksJson);
424
+ });
480
425
  addProjectOption(codegraph
481
426
  .command('affected')
482
427
  .description('Find code affected by files')
@@ -0,0 +1,22 @@
1
+ import { type ProgramIO } from '../cli-helpers.js';
2
+ import { type CommonCodegraphOptions } from './codegraph-command-runtime.js';
3
+ export declare function attributeUpstreamUpToDateLine(stdout: string): string;
4
+ /**
5
+ * `peaks codegraph status` with an integrity gate.
6
+ *
7
+ * The upstream status is still proxied verbatim (that is what the
8
+ * command has always done), but a clean upstream "index is up to date"
9
+ * is no longer sufficient: when git-tracked source files are being
10
+ * excluded by the config, or the index itself does not cover the
11
+ * repository, the command says so and names the rules and files.
12
+ *
13
+ * SEVERITY (user decision, option C): the exclude gate keeps its shipped
14
+ * blocking behaviour (exit 74); the index gate is ADVISORY by default and
15
+ * blocking only when `PEAKS_CODEGRAPH_INDEX_STRICT` is set. A detected
16
+ * gap is the finding either way — only the tag and the exit code move.
17
+ *
18
+ * Read-only by construction — it imports the integrity inspectors, never
19
+ * the repair writer. Fixing the config is `peaks codegraph init`
20
+ * (fresh) or `peaks codegraph repair-exclude` (explicit).
21
+ */
22
+ export declare function runCodegraphStatusCommand(io: ProgramIO, options: CommonCodegraphOptions, asJson?: boolean): Promise<void>;