peaks-loop 4.0.51 → 4.0.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/dist/cli/commands/baseline-commands.js +11 -1
- package/dist/cli/commands/codegraph-command-runtime.d.ts +28 -0
- package/dist/cli/commands/codegraph-command-runtime.js +72 -0
- package/dist/cli/commands/codegraph-commands.d.ts +2 -11
- package/dist/cli/commands/codegraph-commands.js +173 -228
- package/dist/cli/commands/codegraph-status-command.d.ts +22 -0
- package/dist/cli/commands/codegraph-status-command.js +299 -0
- package/dist/cli/commands/core/memory-command.js +6 -2
- package/dist/cli/commands/job-commands.js +121 -30
- package/dist/cli/commands/project-commands.js +13 -3
- package/dist/cli/commands/request-commands.js +19 -8
- package/dist/cli/commands/slice-commands.js +2 -2
- package/dist/services/artifacts/artifact-prerequisites.js +23 -1
- package/dist/services/codegraph/codegraph-autorefresh.d.ts +16 -0
- package/dist/services/codegraph/codegraph-autorefresh.js +51 -5
- package/dist/services/codegraph/codegraph-config-repair-writer.d.ts +88 -0
- package/dist/services/codegraph/codegraph-config-repair-writer.js +322 -0
- package/dist/services/codegraph/codegraph-exclude-integrity.d.ts +20 -2
- package/dist/services/codegraph/codegraph-exclude-integrity.js +24 -3
- package/dist/services/codegraph/codegraph-exclude-reconciler.d.ts +23 -2
- package/dist/services/codegraph/codegraph-exclude-reconciler.js +123 -12
- package/dist/services/codegraph/codegraph-exclude-repair.d.ts +109 -55
- package/dist/services/codegraph/codegraph-exclude-repair.js +249 -195
- package/dist/services/codegraph/codegraph-include-reconciler.d.ts +10 -0
- package/dist/services/codegraph/codegraph-include-reconciler.js +160 -0
- package/dist/services/codegraph/codegraph-index-integrity.d.ts +268 -0
- package/dist/services/codegraph/codegraph-index-integrity.js +471 -0
- package/dist/services/codegraph/codegraph-service.d.ts +54 -0
- package/dist/services/codegraph/codegraph-service.js +84 -1
- package/dist/services/doctor/doctor-service/checks/codegraph-exclude-integrity.js +19 -4
- package/dist/services/doctor/doctor-service/checks/codegraph-index-integrity.d.ts +54 -0
- package/dist/services/doctor/doctor-service/checks/codegraph-index-integrity.js +151 -0
- package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.js +10 -10
- package/dist/services/doctor/doctor-service/plugin-registry.js +2 -0
- package/dist/services/doctor/doctor-service/types.d.ts +25 -0
- package/dist/services/memory/project-memory-service/index/kind-dispatch.js +48 -13
- package/dist/services/memory/project-memory-service/index.d.ts +5 -3
- package/dist/services/memory/project-memory-service/index.js +2 -2
- package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +15 -1
- package/dist/services/memory/project-memory-service/parsers/frontmatter.js +34 -6
- package/dist/services/memory/project-memory-service/parsers/markdown-pure.d.ts +27 -1
- package/dist/services/memory/project-memory-service/parsers/markdown-pure.js +92 -7
- package/dist/services/memory/project-memory-service/types.d.ts +86 -0
- package/dist/services/slice/slice-check-types.d.ts +1 -1
- package/dist/services/workspace/runtime-layout.d.ts +91 -0
- package/dist/services/workspace/runtime-layout.js +148 -0
- package/dist/services/workspace/workspace-claude-settings-materializer.js +14 -0
- package/package.json +6 -6
- package/scripts/clean-dist.mjs +15 -3
- package/scripts/sync-version.mjs +26 -4
- package/skills/bee/peaks-prd/SKILL.md +1 -1
- package/skills/bee/peaks-qa/references/qa-skill-presence.md +1 -1
- package/skills/bee/peaks-rd/references/skill-presence-and-title.md +1 -1
- package/skills/bee/peaks-sc/SKILL.md +1 -1
- package/skills/bee/peaks-txt/SKILL.md +3 -3
- package/skills/peaks-code/SKILL.md +1 -1
- package/skills/peaks-code/references/project-memory-loading.md +19 -1
- package/skills/peaks-code/references/step-11-memory-sediment.md +1 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { loadProjectDashboard } from '../../services/dashboard/project-dashboard-service.js';
|
|
2
2
|
import { generateProjectContext, readProjectContext } from '../../services/memory/project-context-service.js';
|
|
3
|
-
import { extractSessionMemories, readMemoryIndex, readProjectMemories, readProjectMemoryBody } from '../../services/memory/project-memory-service.js';
|
|
3
|
+
import { describeMemoryBlockDrops, describeSessionScanFailures, extractSessionMemories, readMemoryIndex, readProjectMemories, readProjectMemoryBody, VALID_PROJECT_MEMORY_KINDS } from '../../services/memory/project-memory-service.js';
|
|
4
4
|
import { readBusinessKnowledge } from '../../services/prd/project-scan-reader.js';
|
|
5
5
|
import { applyStalePolicy, DEFAULT_STALE_DAYS } from '../../shared/stale-policy.js';
|
|
6
6
|
import { formatMdCompact } from '../../shared/format-md-compact.js';
|
|
7
7
|
import { fail, ok } from 'peaks-loop-shared/result';
|
|
8
8
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
9
|
+
/** Derived from the canonical kind vocabulary — never hand-maintain a list here. */
|
|
10
|
+
const KIND_HELP = VALID_PROJECT_MEMORY_KINDS.join(', ');
|
|
9
11
|
export function registerProjectCommands(program, io) {
|
|
10
12
|
const project = program.command('project').description('Aggregate Peaks state for a target project (read-only)');
|
|
11
13
|
addJsonOption(project
|
|
@@ -111,7 +113,15 @@ export function registerProjectCommands(program, io) {
|
|
|
111
113
|
writtenFiles: result.writtenFiles,
|
|
112
114
|
memoryDir: result.primaryMemoryDir,
|
|
113
115
|
indexUpdated: result.updatedIndex
|
|
114
|
-
}
|
|
116
|
+
}, [
|
|
117
|
+
// Two drop axes, one channel. `droppedBlocks` = a block was found and
|
|
118
|
+
// rejected (or a marker-shaped comment was not findable at all);
|
|
119
|
+
// `scanFailures` = the whole artifact could not be read, so its blocks
|
|
120
|
+
// were never candidates. `data` is unchanged by either — same contract
|
|
121
|
+
// as `memory.extract`.
|
|
122
|
+
...describeMemoryBlockDrops(result.droppedBlocks),
|
|
123
|
+
...describeSessionScanFailures(result.scanFailures)
|
|
124
|
+
]), options.json);
|
|
115
125
|
}
|
|
116
126
|
catch (error) {
|
|
117
127
|
printResult(io, fail('project.memories:extract', 'MEMORY_EXTRACT_FAILED', getErrorMessage(error), { sessionId: options.sessionId, projectRoot: options.project }, ['Check the session-id and project path']), options.json);
|
|
@@ -141,7 +151,7 @@ export function registerProjectCommands(program, io) {
|
|
|
141
151
|
.command('memories')
|
|
142
152
|
.description('Read durable project memories (decisions, conventions, modules, rules) from .peaks/memory for LLM consumption')
|
|
143
153
|
.requiredOption('--project <path>', 'target project root')
|
|
144
|
-
.option('--kind <kind>',
|
|
154
|
+
.option('--kind <kind>', `filter by memory kind (one of: ${KIND_HELP})`)).action((options) => {
|
|
145
155
|
try {
|
|
146
156
|
const result = readProjectMemories(options.project);
|
|
147
157
|
if (options.kind) {
|
|
@@ -8,7 +8,7 @@ import { lintRequestArtifact } from '../../services/artifacts/artifact-lint-serv
|
|
|
8
8
|
import { getRepairCycleStatus } from '../../services/artifacts/repair-cycle-service.js';
|
|
9
9
|
import { fail, ok } from 'peaks-loop-shared/result';
|
|
10
10
|
import { triggerBestPracticeScan } from '../../services/prd/best-practice-auto-trigger.js';
|
|
11
|
-
import { refreshCodegraphAfterSlice, } from '../../services/codegraph/codegraph-autorefresh.js';
|
|
11
|
+
import { codegraphRefreshNotice, refreshCodegraphAfterSlice, } from '../../services/codegraph/codegraph-autorefresh.js';
|
|
12
12
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
13
13
|
import { isUnsafePathInput } from '../../shared/path-safety.js';
|
|
14
14
|
/**
|
|
@@ -296,6 +296,9 @@ export function registerRequestCommands(program, io) {
|
|
|
296
296
|
// RD → QA slice-complete boundary. Set only for rd:qa-handoff; null
|
|
297
297
|
// otherwise. Best-effort and fail-silent — never blocks the transition.
|
|
298
298
|
let codegraphRefresh = null;
|
|
299
|
+
// A2 (2026-09-17): the human-visible half of the refresh outcome. null
|
|
300
|
+
// when the refresh succeeded or when no codegraph store was in use.
|
|
301
|
+
let codegraphWarning = null;
|
|
299
302
|
if (role === 'rd' && newState === 'qa-handoff') {
|
|
300
303
|
try {
|
|
301
304
|
const { maybePreCompactCheckpoint } = await import('../../services/compact/request-transition-hook.js');
|
|
@@ -319,6 +322,11 @@ export function registerRequestCommands(program, io) {
|
|
|
319
322
|
// The refresh is best-effort; never block the transition.
|
|
320
323
|
codegraphRefresh = { refreshed: false, reason: 'unavailable', note: 'auto codegraph refresh failed after transition' };
|
|
321
324
|
}
|
|
325
|
+
// A2 (2026-09-17): never BLOCKING is kept; never VISIBLE is not. A
|
|
326
|
+
// non-refresh while a codegraph store is in use becomes a warning
|
|
327
|
+
// line beside the transition's own notes. See
|
|
328
|
+
// `codegraphRefreshNotice` for why `no-codegraph-dir` stays silent.
|
|
329
|
+
codegraphWarning = codegraphRefreshNotice(codegraphRefresh);
|
|
322
330
|
}
|
|
323
331
|
// v2.13.2 AC-4 — auto-regen prd/handoff.md on prd:handed-off success.
|
|
324
332
|
// Only fires when the handoff is missing; existing handoffs are not overwritten.
|
|
@@ -360,13 +368,16 @@ export function registerRequestCommands(program, io) {
|
|
|
360
368
|
// transition is not a slice boundary), preCompact is null and
|
|
361
369
|
// we emit a `preCompactCheckpoint: null` field so LLM callers
|
|
362
370
|
// can branch on it without re-deriving zone membership.
|
|
363
|
-
printResult(io, ok('request.transition', { ...result, preCompactCheckpoint: preCompact?.data ?? null, codegraphRefresh },
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
371
|
+
printResult(io, ok('request.transition', { ...result, preCompactCheckpoint: preCompact?.data ?? null, codegraphRefresh }, [
|
|
372
|
+
...(preCompact !== null
|
|
373
|
+
? [
|
|
374
|
+
`Pre-compact checkpoint written at ratio=${typeof preCompact.data === 'object' && preCompact.data !== null && 'ratio' in preCompact.data
|
|
375
|
+
? String(preCompact.data.ratio)
|
|
376
|
+
: 'unknown'} (zone=pre-compact)`
|
|
377
|
+
]
|
|
378
|
+
: []),
|
|
379
|
+
...(codegraphWarning === null ? [] : [codegraphWarning])
|
|
380
|
+
]), options.json);
|
|
370
381
|
}
|
|
371
382
|
catch (error) {
|
|
372
383
|
if (error instanceof InvalidArgumentError) {
|
|
@@ -35,7 +35,7 @@ export function registerSliceCommands(program, io) {
|
|
|
35
35
|
'Each stage reports pass / fail / skipped. ' +
|
|
36
36
|
'Exit 0 only if every stage passes or is skipped.')
|
|
37
37
|
.option('--project <path>', 'target project root', '.')
|
|
38
|
-
.option('--rid <rid>', 'request id;
|
|
38
|
+
.option('--rid <rid>', 'request id; REQUIRED — there is no binding to fall back to, and slice check fails without it')
|
|
39
39
|
.option('--refresh-fanout', 're-run the 3-way review fan-out (peaks-rd) even if the review files already exist', false)
|
|
40
40
|
.option('--run-tests', 'opt in to the FULL test suite at the boundary (default is the changed-only suite via `vitest run --changed`); use the peaks-test skill to run the full suite standalone', false)
|
|
41
41
|
.option('--skip-tests', 'skip the unit-test stage entirely (e.g. docs-only slices); use the peaks-test skill to run the full suite manually if you want a separate check', false)
|
|
@@ -60,7 +60,7 @@ export function registerSliceCommands(program, io) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
catch (error) {
|
|
63
|
-
printResult(io, fail('slice.check', 'SLICE_CHECK_FAILED', getErrorMessage(error), { projectRoot: options.project }, [
|
|
63
|
+
printResult(io, fail('slice.check', 'SLICE_CHECK_FAILED', getErrorMessage(error), { projectRoot: options.project }, [`Verify the project path is a peaks repo and --rid names a slice (letters, digits, dots, underscores or dashes): ${options.rid ?? '(no --rid given)'}`]), options.json ?? false);
|
|
64
64
|
process.exitCode = 1;
|
|
65
65
|
}
|
|
66
66
|
});
|
|
@@ -237,10 +237,32 @@ const PRD_CONTENT = {
|
|
|
237
237
|
description: 'PRD artifact must contain Goal and Acceptance criteria sections before handoff',
|
|
238
238
|
mustContain: ['## Goals', '## Acceptance']
|
|
239
239
|
};
|
|
240
|
+
// R10 (2026-09-16): the second marker used to be the bare `test(` inside
|
|
241
|
+
// `mustContain` — i.e. ALL markers required. That made the gate satisfiable
|
|
242
|
+
// only by MENTIONING the literal string in prose, never by honest test code,
|
|
243
|
+
// because this repo's own BDD Test Style Contract
|
|
244
|
+
// (`skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md:170`) names BOTH
|
|
245
|
+
// idioms — "The first string-literal argument of `it()` / `test()` MUST
|
|
246
|
+
// describe business behavior" — and `it(` is the dominant one by ~8x.
|
|
247
|
+
// Measured 2026-09-16 (`grep -rho '\bit(' tests | wc -l`, same for `test(`):
|
|
248
|
+
// `it(` 3015 vs `test(` 372 across `tests/`; the three test files slice
|
|
249
|
+
// 2026-09-16-codegraph-index-integrity added contain `it(` 58 / `test(` 0.
|
|
250
|
+
// The gate was in fact passing on artifacts whose ONLY `test(` was prose —
|
|
251
|
+
// including this repo's own generated template `buildTestCases`
|
|
252
|
+
// (`src/services/evidence/evidence-generator.ts`), which literally reads
|
|
253
|
+
// "no new tests; behavior preserved" and satisfied the marker anyway.
|
|
254
|
+
// Both idioms are therefore accepted via `mustContainAny` — an existing
|
|
255
|
+
// field on this type, already used by PERF_BASELINE / AUDIT_SECURITY /
|
|
256
|
+
// MUT_REPORT — while `## Test cases` stays a hard `mustContain`.
|
|
257
|
+
// Do NOT "tighten" this back to a single idiom in `mustContain`: that
|
|
258
|
+
// restores exactly the pass-by-mentioning hole `headingMustContain` was
|
|
259
|
+
// introduced to close (see the field doc comment above), and no honest test
|
|
260
|
+
// file in this repo could then satisfy the gate.
|
|
240
261
|
const UNIT_TESTS = {
|
|
241
262
|
relativePath: 'qa/test-cases/<rid>.md',
|
|
242
263
|
description: 'Unit test files for the implemented changes (enforces peaks-rd Gate B2)',
|
|
243
|
-
mustContain: ['## Test cases',
|
|
264
|
+
mustContain: ['## Test cases'],
|
|
265
|
+
mustContainAny: ['test(', 'it(']
|
|
244
266
|
};
|
|
245
267
|
const QA_INITIATED = {
|
|
246
268
|
relativePath: 'qa/.initiated',
|
|
@@ -6,6 +6,22 @@ export type CodegraphAutorefreshResult = {
|
|
|
6
6
|
reason: 'no-codegraph-dir' | 'index-failed' | 'unavailable';
|
|
7
7
|
note: string;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* The operator-facing line for a refresh that did not happen, or `null` when
|
|
11
|
+
* there is nothing to report. The ONE place the report/no-report rule lives,
|
|
12
|
+
* shared by both slice-boundary call sites (`peaks job checkpoint --state
|
|
13
|
+
* done` and `peaks request transition rd:qa-handoff`) so the two cannot
|
|
14
|
+
* drift into disagreeing about what counts as visible.
|
|
15
|
+
*
|
|
16
|
+
* Returns the result's own `note` verbatim rather than rebuilding a
|
|
17
|
+
* sentence: that note is where the reason (exit code / upstream error line)
|
|
18
|
+
* and the remedy live, and a second wording here would be a second truth.
|
|
19
|
+
*
|
|
20
|
+
* `no-codegraph-dir` is deliberately SILENT — see the header's A2 note. A
|
|
21
|
+
* store that exists and did not refresh is a warning; a project that never
|
|
22
|
+
* set codegraph up is not a defect to report at every slice boundary.
|
|
23
|
+
*/
|
|
24
|
+
export declare function codegraphRefreshNotice(result: CodegraphAutorefreshResult): string | null;
|
|
9
25
|
/**
|
|
10
26
|
* True when `<projectRoot>/.codegraph/` exists and is a directory.
|
|
11
27
|
* Pure fs probe; never throws.
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
// hook install surface needed), fires exactly once at the true slice
|
|
16
16
|
// boundary, and needs no IDE hook plumbing.
|
|
17
17
|
//
|
|
18
|
-
// The refresh is best-effort and
|
|
19
|
-
//
|
|
18
|
+
// The refresh is best-effort and never throws, and it never blocks the
|
|
19
|
+
// checkpoint/transition ok envelope:
|
|
20
20
|
// - No `<projectRoot>/.codegraph/` directory → skip (codegraph was
|
|
21
21
|
// never initialized for this project; `peaks codegraph init` is a
|
|
22
22
|
// one-time setup the orchestrator owns).
|
|
@@ -29,6 +29,24 @@
|
|
|
29
29
|
// human-readable note.
|
|
30
30
|
// - Any unexpected error → return `unavailable` with a note.
|
|
31
31
|
//
|
|
32
|
+
// A2 (`2026-09-17-codegraph-msg-and-refresh`). The old header said
|
|
33
|
+
// "FAIL-SILENT", and it was: both call sites discarded this result's `note`,
|
|
34
|
+
// so a refresh that DID NOT HAPPEN and one that did were indistinguishable
|
|
35
|
+
// to the operator — the same silent-failure class this job exists to close.
|
|
36
|
+
// "Never blocks the caller" is the correct half and is KEPT; "never tells
|
|
37
|
+
// anyone" was not.
|
|
38
|
+
//
|
|
39
|
+
// The two halves are split by whether a refresh was EXPECTED:
|
|
40
|
+
// - `no-codegraph-dir` → the project has no codegraph store (or has a
|
|
41
|
+
// foreign, never-initialized one). Nothing was expected, so nothing is
|
|
42
|
+
// reported: warning on every slice boundary of every project that never
|
|
43
|
+
// opted in is noise that trains the reader to skip the line that
|
|
44
|
+
// matters. The note is still in the JSON envelope.
|
|
45
|
+
// - `index-failed` / `unavailable` → a store EXISTS and is in use, so the
|
|
46
|
+
// refresh was expected and did not happen. That is a real failure and
|
|
47
|
+
// `codegraphRefreshNotice` turns it into an operator-visible warning
|
|
48
|
+
// naming the reason and the remedy.
|
|
49
|
+
//
|
|
32
50
|
// We do NOT auto-init a genuinely fresh (no `.codegraph/` dir) project:
|
|
33
51
|
// the orchestrator owns that one-time setup. The dangling self-heal above
|
|
34
52
|
// IS an auto-init, but `codegraph init` (WITHOUT `--index`) is fast (~1 s)
|
|
@@ -39,6 +57,28 @@ import { existsSync, statSync } from 'node:fs';
|
|
|
39
57
|
import { join } from 'node:path';
|
|
40
58
|
import { CODEGRAPH_DIR_NAME, CODEGRAPH_MARKER_NAME, createCodegraphInvocation, executeCodegraphInvocation, isCodegraphInitialized, } from './codegraph-service.js';
|
|
41
59
|
import { repairCodegraphExcludeFromProject } from './codegraph-exclude-repair.js';
|
|
60
|
+
/**
|
|
61
|
+
* The operator-facing line for a refresh that did not happen, or `null` when
|
|
62
|
+
* there is nothing to report. The ONE place the report/no-report rule lives,
|
|
63
|
+
* shared by both slice-boundary call sites (`peaks job checkpoint --state
|
|
64
|
+
* done` and `peaks request transition rd:qa-handoff`) so the two cannot
|
|
65
|
+
* drift into disagreeing about what counts as visible.
|
|
66
|
+
*
|
|
67
|
+
* Returns the result's own `note` verbatim rather than rebuilding a
|
|
68
|
+
* sentence: that note is where the reason (exit code / upstream error line)
|
|
69
|
+
* and the remedy live, and a second wording here would be a second truth.
|
|
70
|
+
*
|
|
71
|
+
* `no-codegraph-dir` is deliberately SILENT — see the header's A2 note. A
|
|
72
|
+
* store that exists and did not refresh is a warning; a project that never
|
|
73
|
+
* set codegraph up is not a defect to report at every slice boundary.
|
|
74
|
+
*/
|
|
75
|
+
export function codegraphRefreshNotice(result) {
|
|
76
|
+
if (result.refreshed)
|
|
77
|
+
return null;
|
|
78
|
+
if (result.reason === 'no-codegraph-dir')
|
|
79
|
+
return null;
|
|
80
|
+
return result.note;
|
|
81
|
+
}
|
|
42
82
|
/**
|
|
43
83
|
* True when `<projectRoot>/.codegraph/` exists and is a directory.
|
|
44
84
|
* Pure fs probe; never throws.
|
|
@@ -62,6 +102,12 @@ function isCodegraphPeaksLoopManaged(projectRoot) {
|
|
|
62
102
|
function errorMessage(error) {
|
|
63
103
|
return error instanceof Error ? error.message : String(error);
|
|
64
104
|
}
|
|
105
|
+
// A2: the remedy half of an operator-visible failure note. Phrased like the
|
|
106
|
+
// existing follow-up-index warning in `codegraph-exclude-repair.ts` so the
|
|
107
|
+
// two read as one voice; it names the command the LLM/orchestrator re-runs,
|
|
108
|
+
// never a verb the user is asked to type (Human-NL-Choice-Only — the same
|
|
109
|
+
// posture that warning already ships with).
|
|
110
|
+
const REFRESH_REMEDY = 'Run `peaks codegraph index --project <root>` to refresh the codegraph index.';
|
|
65
111
|
function firstMeaningfulLine(text) {
|
|
66
112
|
const trimmed = text.trim();
|
|
67
113
|
if (trimmed.length === 0)
|
|
@@ -105,7 +151,7 @@ export async function refreshCodegraphAfterSlice(projectRoot, runner) {
|
|
|
105
151
|
return {
|
|
106
152
|
refreshed: false,
|
|
107
153
|
reason: 'index-failed',
|
|
108
|
-
note: `auto codegraph refresh self-heal init failed (exit ${String(initResult.exitCode)}): ${firstMeaningfulLine(initResult.stderr || initResult.stdout)}`,
|
|
154
|
+
note: `auto codegraph refresh self-heal init failed (exit ${String(initResult.exitCode)}): ${firstMeaningfulLine(initResult.stderr || initResult.stdout)}. ${REFRESH_REMEDY}`,
|
|
109
155
|
};
|
|
110
156
|
}
|
|
111
157
|
// That init just wrote upstream's 99-rule default `exclude`
|
|
@@ -126,7 +172,7 @@ export async function refreshCodegraphAfterSlice(projectRoot, runner) {
|
|
|
126
172
|
return {
|
|
127
173
|
refreshed: false,
|
|
128
174
|
reason: 'index-failed',
|
|
129
|
-
note: `auto codegraph refresh failed (exit ${String(result.exitCode)}): ${firstMeaningfulLine(result.stderr || result.stdout)}`,
|
|
175
|
+
note: `auto codegraph refresh failed (exit ${String(result.exitCode)}): ${firstMeaningfulLine(result.stderr || result.stdout)}. ${REFRESH_REMEDY}`,
|
|
130
176
|
};
|
|
131
177
|
}
|
|
132
178
|
return { refreshed: true };
|
|
@@ -135,7 +181,7 @@ export async function refreshCodegraphAfterSlice(projectRoot, runner) {
|
|
|
135
181
|
return {
|
|
136
182
|
refreshed: false,
|
|
137
183
|
reason: 'unavailable',
|
|
138
|
-
note: `auto codegraph refresh unavailable: ${errorMessage(error)}`,
|
|
184
|
+
note: `auto codegraph refresh unavailable: ${errorMessage(error)}. ${REFRESH_REMEDY}`,
|
|
139
185
|
};
|
|
140
186
|
}
|
|
141
187
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Suffix of the byte-exact pre-repair copy kept next to the config. */
|
|
2
|
+
export declare const CODEGRAPH_CONFIG_BACKUP_SUFFIX = ".bak";
|
|
3
|
+
export type CodegraphExcludeRepairPlan = {
|
|
4
|
+
/** True when at least one rule would actually be dropped. */
|
|
5
|
+
readonly changed: boolean;
|
|
6
|
+
/** The `exclude` array after the removal. */
|
|
7
|
+
readonly exclude: readonly string[];
|
|
8
|
+
/** Rules actually present in `exclude` and dropped, in config order. */
|
|
9
|
+
readonly removedRules: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Pure: given the current `exclude` list and the rules to drop, return
|
|
13
|
+
* the new list. No fs, no clock, no serialization.
|
|
14
|
+
*
|
|
15
|
+
* A rule named in `rulesToRemove` but absent from `exclude` is NOT
|
|
16
|
+
* invented — the result is a subset of the input, so a caller can
|
|
17
|
+
* never add a rule by accident. Removing an already-absent rule is a
|
|
18
|
+
* no-op, which is what makes the whole repair idempotent: feeding the
|
|
19
|
+
* repaired list back in yields `changed: false`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function repairCodegraphExclude(input: {
|
|
22
|
+
readonly exclude: readonly string[];
|
|
23
|
+
readonly rulesToRemove: readonly string[];
|
|
24
|
+
}): CodegraphExcludeRepairPlan;
|
|
25
|
+
/**
|
|
26
|
+
* Pure: given the current `include` list and the patterns to append, return
|
|
27
|
+
* the new list. The mirror of `repairCodegraphExclude`, in the other
|
|
28
|
+
* direction — a SUPERSET operation instead of a subset one.
|
|
29
|
+
*
|
|
30
|
+
* A pattern already present is not appended twice: the include reconciler
|
|
31
|
+
* already guarantees that, but this function is the writer's own last line
|
|
32
|
+
* of defence, and a duplicated glob in a third-party config would be a
|
|
33
|
+
* visible defect even though it changes no matching behaviour.
|
|
34
|
+
*/
|
|
35
|
+
export declare function repairCodegraphInclude(input: {
|
|
36
|
+
readonly include: readonly string[];
|
|
37
|
+
readonly patternsToAdd: readonly string[];
|
|
38
|
+
}): {
|
|
39
|
+
readonly changed: boolean;
|
|
40
|
+
readonly include: readonly string[];
|
|
41
|
+
readonly addedPatterns: readonly string[];
|
|
42
|
+
};
|
|
43
|
+
export type CodegraphConfigRepairPlan = {
|
|
44
|
+
/** Rules from the exclude reconciler to drop. */
|
|
45
|
+
readonly rulesToRemove: readonly string[];
|
|
46
|
+
/** Patterns from the include reconciler to append. */
|
|
47
|
+
readonly includePatternsToAdd: readonly string[];
|
|
48
|
+
};
|
|
49
|
+
export type CodegraphConfigRepairOutcome = {
|
|
50
|
+
readonly applied: false;
|
|
51
|
+
readonly reason: 'nothing-to-repair';
|
|
52
|
+
readonly removedRules: readonly string[];
|
|
53
|
+
readonly addedIncludePatterns: readonly string[];
|
|
54
|
+
} | {
|
|
55
|
+
readonly applied: true;
|
|
56
|
+
readonly configPath: string;
|
|
57
|
+
readonly backupPath: string;
|
|
58
|
+
readonly removedRules: readonly string[];
|
|
59
|
+
readonly addedIncludePatterns: readonly string[];
|
|
60
|
+
readonly excludeCountBefore: number;
|
|
61
|
+
readonly excludeCountAfter: number;
|
|
62
|
+
readonly includeCountBefore: number;
|
|
63
|
+
readonly includeCountAfter: number;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Apply BOTH config repairs to `<projectRoot>/.codegraph/config.json` in one
|
|
67
|
+
* atomic rewrite.
|
|
68
|
+
*
|
|
69
|
+
* No-op (and no write, no mtime change, no backup) when both lists are
|
|
70
|
+
* empty. Otherwise: back up the original bytes to `config.json.bak`, then
|
|
71
|
+
* rewrite the file with `exclude` reduced by exactly the rules that were
|
|
72
|
+
* both requested and present, and `include` extended by exactly the
|
|
73
|
+
* patterns that were both requested and absent.
|
|
74
|
+
*
|
|
75
|
+
* ONE rewrite, not two. A caller that widened `include` in one write and
|
|
76
|
+
* dropped the newly-offending `exclude` rules in a second would leave a
|
|
77
|
+
* window in which the config on disk is worse than it started (the widened
|
|
78
|
+
* include admits a file that a surviving rule then hides from the index),
|
|
79
|
+
* and would need two backups to stay rollback-exact. One rewrite through
|
|
80
|
+
* the same-directory temp file has neither property.
|
|
81
|
+
*
|
|
82
|
+
* Throws only on real fs/parse failures and on the containment refusal
|
|
83
|
+
* above — the caller decides whether that is fatal (`repair-exclude` /
|
|
84
|
+
* `repair-index` → non-zero exit) or a surfaced warning (`init` → keep
|
|
85
|
+
* going, the init itself already succeeded). Both are refusals BEFORE
|
|
86
|
+
* anything is written, so a throw can never half-apply.
|
|
87
|
+
*/
|
|
88
|
+
export declare function applyCodegraphConfigRepair(projectRoot: string, repair: CodegraphConfigRepairPlan): CodegraphConfigRepairOutcome;
|