peaks-loop 4.0.49 → 4.0.51
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 +38 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/dist/cli/commands/audit-commands.js +1 -0
- package/dist/cli/commands/baseline-commands.js +163 -25
- package/dist/cli/commands/core/skill-command.js +53 -4
- package/dist/cli/commands/core/standards-command.d.ts +24 -0
- package/dist/cli/commands/core/standards-command.js +74 -0
- package/dist/cli/commands/hooks-commands.js +55 -38
- package/dist/cli/commands/share-commands.js +113 -20
- package/dist/cli/commands/web-commands.js +8 -1
- package/dist/cli/commands/workflow-lifecycle-commands.d.ts +6 -0
- package/dist/cli/commands/workflow-lifecycle-commands.js +64 -3
- package/dist/services/adapter/adapter.d.ts +30 -0
- package/dist/services/adapter/auto-adapter.d.ts +13 -0
- package/dist/services/adapter/claude-adapter.js +12 -0
- package/dist/services/adapter/codex-adapter.d.ts +12 -0
- package/dist/services/adapter/codex-adapter.js +12 -0
- package/dist/services/adapter/copilot-adapter.d.ts +12 -0
- package/dist/services/adapter/copilot-adapter.js +12 -0
- package/dist/services/audit/backing-detector.d.ts +25 -7
- package/dist/services/audit/backing-detector.js +33 -17
- package/dist/services/audit/enforcer-liveness.d.ts +12 -0
- package/dist/services/audit/enforcer-liveness.js +100 -0
- package/dist/services/audit/enforcers/lint-catalog-governance.d.ts +23 -11
- package/dist/services/audit/enforcers/lint-catalog-governance.js +10 -14
- package/dist/services/audit/enforcers/lint-rd-handoff-coverage.d.ts +5 -15
- package/dist/services/audit/enforcers/lint-rd-handoff-coverage.js +94 -25
- package/dist/services/audit/enforcers/lint-style.d.ts +9 -1
- package/dist/services/audit/enforcers/lint-style.js +38 -2
- package/dist/services/audit/prose-ratio-calculator.d.ts +28 -17
- package/dist/services/audit/prose-ratio-calculator.js +25 -18
- package/dist/services/audit/red-line-catalog-p2-a.js +1 -1
- package/dist/services/audit/red-lines-service.js +51 -7
- package/dist/services/capability-audit-service/independent-checker.d.ts +15 -0
- package/dist/services/capability-audit-service/independent-checker.js +140 -0
- package/dist/services/capability-audit-service/index.d.ts +3 -1
- package/dist/services/capability-audit-service/index.js +1 -0
- package/dist/services/capability-audit-service/runner.d.ts +17 -13
- package/dist/services/capability-audit-service/runner.js +76 -15
- package/dist/services/capability-audit-service/types.d.ts +48 -0
- package/dist/services/capability-guard-runner/contracts/J01.js +21 -22
- package/dist/services/capability-guard-runner/contracts/J02.d.ts +1 -1
- package/dist/services/capability-guard-runner/contracts/J02.js +114 -28
- package/dist/services/capability-guard-runner/contracts/J03.d.ts +13 -0
- package/dist/services/capability-guard-runner/contracts/J03.js +72 -21
- package/dist/services/capability-guard-runner/contracts/J04.d.ts +6 -0
- package/dist/services/capability-guard-runner/contracts/J04.js +65 -32
- package/dist/services/capability-guard-runner/contracts/J05.js +118 -16
- package/dist/services/capability-guard-runner/contracts/J06.d.ts +14 -0
- package/dist/services/capability-guard-runner/contracts/J06.js +57 -39
- package/dist/services/capability-guard-runner/contracts/J07.d.ts +9 -0
- package/dist/services/capability-guard-runner/contracts/J07.js +76 -47
- package/dist/services/capability-guard-runner/contracts/J08.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J08.js +66 -39
- package/dist/services/capability-guard-runner/contracts/J09.d.ts +13 -0
- package/dist/services/capability-guard-runner/contracts/J09.js +95 -39
- package/dist/services/capability-guard-runner/contracts/J10.d.ts +12 -0
- package/dist/services/capability-guard-runner/contracts/J10.js +69 -35
- package/dist/services/capability-guard-runner/contracts/J11.d.ts +8 -0
- package/dist/services/capability-guard-runner/contracts/J11.js +73 -33
- package/dist/services/capability-guard-runner/contracts/J12.d.ts +12 -0
- package/dist/services/capability-guard-runner/contracts/J12.js +66 -30
- package/dist/services/capability-guard-runner/contracts/J13.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J13.js +62 -40
- package/dist/services/capability-guard-runner/contracts/J14.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J14.js +60 -31
- package/dist/services/capability-guard-runner/contracts/J15.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J15.js +70 -35
- package/dist/services/capability-guard-runner/contracts/_shared.d.ts +24 -0
- package/dist/services/capability-guard-runner/contracts/_shared.js +67 -0
- package/dist/services/capability-guard-runner/registry.d.ts +5 -0
- package/dist/services/capability-guard-runner/registry.js +140 -0
- package/dist/services/capability-guard-runner/runner.d.ts +26 -0
- package/dist/services/capability-guard-runner/runner.js +63 -6
- package/dist/services/code/auto-compact-modes.d.ts +13 -2
- package/dist/services/code/auto-compact-modes.js +20 -4
- package/dist/services/code/post-compact-detector.js +20 -11
- package/dist/services/code/step-08-gate.js +21 -6
- package/dist/services/config/config-safety.js +11 -9
- package/dist/services/dispatch/sub-agent-dispatcher.d.ts +11 -30
- package/dist/services/dispatch/sub-agent-dispatcher.js +5 -48
- package/dist/services/final-review/pre-post-diff.js +10 -2
- package/dist/services/ide/adapters/claude-code-adapter.js +0 -1
- package/dist/services/ide/adapters/codex-adapter.js +1 -2
- package/dist/services/ide/adapters/cursor-adapter.js +1 -2
- package/dist/services/ide/adapters/hermes-adapter.js +1 -2
- package/dist/services/ide/adapters/openclaw-adapter.js +1 -2
- package/dist/services/ide/adapters/qoder-adapter.js +1 -2
- package/dist/services/ide/adapters/tongyi-lingma-adapter.js +1 -2
- package/dist/services/ide/adapters/trae-adapter.js +1 -2
- package/dist/services/ide/adapters/zcode-adapter.js +0 -1
- package/dist/services/ide/ide-types.d.ts +0 -2
- package/dist/services/observability/observability-service.d.ts +1 -1
- package/dist/services/scan/api-diff-types.js +20 -2
- package/dist/services/security/safe-settings-path.js +19 -1
- package/dist/services/skill/skill-search-service.d.ts +3 -3
- package/dist/services/standards/loop-engineering-lint.d.ts +1 -1
- package/dist/services/standards/loop-engineering-lint.js +6 -0
- package/dist/services/web/daemon-registry.js +27 -2
- package/dist/services/workspace/claude-settings-template.d.ts +53 -37
- package/dist/services/workspace/claude-settings-template.js +105 -83
- package/dist/services/workspace/generated-artifacts-stamp.d.ts +119 -0
- package/dist/services/workspace/generated-artifacts-stamp.js +167 -0
- package/dist/services/workspace/workspace-claude-settings-materializer.d.ts +8 -0
- package/dist/services/workspace/workspace-claude-settings-materializer.js +38 -3
- package/dist/services/workspace/workspace-service.js +11 -1
- package/dist/shared/fs-utils.d.ts +26 -0
- package/dist/shared/fs-utils.js +35 -0
- package/package.json +9 -7
- package/scripts/copy-templates.mjs +0 -12
- package/scripts/install-skills.mjs +154 -53
- package/skills/bee/peaks-perf-audit/SKILL.md +2 -2
- package/skills/bee/peaks-perf-audit/references/audit-protocol.md +1 -1
- package/skills/bee/peaks-prd/SKILL.md +3 -3
- package/skills/bee/peaks-prd/references/prd-for-multi-pass.md +1 -1
- package/skills/bee/peaks-prd/references/workflow.md +1 -1
- package/skills/bee/peaks-qa/SKILL.md +6 -7
- package/skills/bee/peaks-qa/references/external-capability-guidance.md +1 -1
- package/skills/bee/peaks-qa/references/qa-fanout-contract.md +1 -1
- package/skills/bee/peaks-qa/references/reading-handoff-frontmatter.md +2 -2
- package/skills/bee/peaks-rd/SKILL.md +2 -3
- package/skills/bee/peaks-rd/references/code-reviewer-4dim-hint.md +1 -1
- package/skills/bee/peaks-rd/references/external-references.md +1 -1
- package/skills/bee/peaks-rd/references/mandatory-perf-baseline.md +1 -1
- package/skills/bee/peaks-rd/references/ocr-multilang-1.8.md +2 -2
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +2 -2
- package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +11 -8
- package/skills/bee/peaks-rd/references/rd-runbook.md +1 -1
- package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +7 -7
- package/skills/bee/peaks-rd/references/rd-transition-gates.md +1 -1
- package/skills/bee/peaks-rd/references/reading-v2-slice-results.md +1 -1
- package/skills/bee/peaks-rd/references/v2-12-fanout-collapse.md +7 -5
- package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +3 -3
- package/skills/bee/peaks-reviewer/SKILL.md +1 -1
- package/skills/bee/peaks-security-audit/SKILL.md +3 -3
- package/skills/bee/peaks-security-audit/references/audit-protocol.md +1 -1
- package/skills/bee/peaks-txt/references/context-capsule.md +1 -1
- package/skills/bee/peaks-ui/SKILL.md +1 -1
- package/skills/peaks-audit/SKILL.md +1 -1
- package/skills/peaks-code/SKILL.md +20 -18
- package/skills/peaks-code/references/context-governance.md +1 -1
- package/skills/peaks-code/references/dag-orchestrator.md +3 -4
- package/skills/peaks-code/references/external-references.md +1 -1
- package/skills/peaks-code/references/external-skill-invocation.md +2 -2
- package/skills/peaks-code/references/fanout-mandatory.md +3 -3
- package/skills/peaks-code/references/frontend-only-mode.md +2 -2
- package/skills/peaks-code/references/gstack-integration.md +1 -1
- package/skills/peaks-code/references/micro-cycle.md +1 -1
- package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
- package/skills/peaks-code/references/project-scan-checklist.md +1 -1
- package/skills/peaks-code/references/resume-detection.md +1 -1
- package/skills/peaks-code/references/runbook.md +6 -3
- package/skills/peaks-code/references/session-overload-signal-index.md +6 -4
- package/skills/peaks-code/references/startup-sequence.md +17 -17
- package/skills/peaks-code/references/step-0-8-gate.md +1 -1
- package/skills/peaks-code/references/step-11-memory-sediment.md +2 -2
- package/skills/peaks-code/references/sub-agent-dispatch.md +26 -25
- package/skills/peaks-code/references/swarm-dispatch-contract.md +1 -1
- package/skills/peaks-code/references/workflow-gates-and-types.md +3 -3
- package/skills/peaks-code/references/worktree-governance.md +1 -1
- package/skills/peaks-final-review/SKILL.md +3 -3
- package/skills/peaks-ide/references/audit-log-helper.md +5 -4
- package/skills/peaks-resume/SKILL.md +1 -1
- package/skills/peaks-slice-decompose/SKILL.md +4 -4
- package/skills/peaks-slice-decompose/references/cross-pass-edge-interpretation.md +1 -1
- package/skills/peaks-slice-decompose/references/granularity-decision.md +1 -1
- package/skills/peaks-slice-decompose/references/v2-schema.md +2 -2
- package/skills/peaks-solo/SKILL.md +1 -2
- package/dist/cli/commands/context-builder-commands.d.ts +0 -11
- package/dist/cli/commands/context-builder-commands.js +0 -85
- package/dist/services/hooks/write-gate.js +0 -111
- package/skills/bee/peaks-prd/references/command-migration.md +0 -3
- package/skills/bee/peaks-qa/references/command-migration.md +0 -3
- package/skills/bee/peaks-rd/references/command-migration.md +0 -3
- package/skills/bee/peaks-sc/references/command-migration.md +0 -3
- package/skills/bee/peaks-txt/references/command-migration.md +0 -3
- package/skills/bee/peaks-ui/references/command-migration.md +0 -3
- package/skills/peaks-code/references/command-migration.md +0 -3
|
@@ -5,9 +5,10 @@ import { fail, ok } from 'peaks-loop-shared/result';
|
|
|
5
5
|
import { addJsonOption, printResult, getErrorMessage } from '../cli-helpers.js';
|
|
6
6
|
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
7
7
|
import { applyHookInstall, planHookInstall, readHookStatus, readInstalledEntriesFromSettings, removeHookInstall, listSuperpowersDenyEntries } from '../../services/skills/hooks-settings-service.js';
|
|
8
|
+
import { resolveHookEntries, resolveHookSpec } from '../../services/skills/hooks-codegate-superpowers.js';
|
|
8
9
|
import { readJsonObjectFile } from '../../services/ide/shared/atomic-json.js';
|
|
9
10
|
import { detectIdeFromContext } from '../../services/ide/hook-translator.js';
|
|
10
|
-
import {
|
|
11
|
+
import { resolveIdeOptionHelp } from '../../services/ide/ide-registry.js';
|
|
11
12
|
/**
|
|
12
13
|
* This module's own directory — `<root>/src/cli/commands` in the source tree,
|
|
13
14
|
* `<root>/dist/cli/commands` in a build. Same reason as
|
|
@@ -50,20 +51,23 @@ function resolveIdeForCommand(options, projectRoot) {
|
|
|
50
51
|
* service did not write.
|
|
51
52
|
*/
|
|
52
53
|
function listExpectedEntriesForIde(ide, _skipProgress = false) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
// Derived from `resolveHookEntries(ide)` — the same function `planHookInstall`
|
|
55
|
+
// / `applyHookInstall` write from — so the summary can only ever report a
|
|
56
|
+
// shape the install actually produces. The previous version was a
|
|
57
|
+
// hand-written literal under a comment claiming it "mirrors the install
|
|
58
|
+
// shape, NOT a hardcoded expected list" (diagnosis 2026-09-15, C4); the
|
|
59
|
+
// literal had drifted from that claim in both directions: it re-typed the
|
|
60
|
+
// code-gate matcher instead of reading `HOOK_CODE_GATE_MATCHER`, and it
|
|
61
|
+
// reported 2 of the 6 entries a claude-code install writes.
|
|
62
|
+
//
|
|
63
|
+
// The filter is the tool-call event. `resolveHookEntries` also returns the
|
|
64
|
+
// once-per-session SessionStart / PostCompact entries; those are not what a
|
|
65
|
+
// per-call hook-entry summary is read for, and leaving them out is the only
|
|
66
|
+
// place this summary is narrower than the install.
|
|
67
|
+
const event = resolveHookSpec(ide).hookEnforceEvent;
|
|
68
|
+
return resolveHookEntries(ide, _skipProgress)
|
|
69
|
+
.filter((entry) => entry.event === event)
|
|
70
|
+
.map((entry) => ({ matcher: entry.matcher, sentinel: entry.sentinel }));
|
|
67
71
|
}
|
|
68
72
|
/**
|
|
69
73
|
* Slice 2026-07-24-peaks-code-bridge-002-rootcause (G6b / G10): copy the
|
|
@@ -108,6 +112,31 @@ function readOnDiskDenyEntries(settings) {
|
|
|
108
112
|
return [];
|
|
109
113
|
return deny.filter((d) => typeof d === 'string');
|
|
110
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* One `nextActions` line for a hook-script copy, or `null` when there is
|
|
117
|
+
* nothing to say.
|
|
118
|
+
*
|
|
119
|
+
* Diagnosis 2026-09-15 (C7): the copy failure used to be reported as a single
|
|
120
|
+
* sentence — `'<x> hook not copied (source missing or non-global scope)'` —
|
|
121
|
+
* which merged a real build failure into the expected project-scope no-op.
|
|
122
|
+
* Only `scope === 'global'` copies these scripts at all (see the call sites),
|
|
123
|
+
* so in project scope the sentence described normal behaviour in the wording
|
|
124
|
+
* of a fault, and a healthy `peaks hooks install` read as broken.
|
|
125
|
+
*
|
|
126
|
+
* Scope is therefore what decides the sentence, and it is knowable here — the
|
|
127
|
+
* copy helper cannot tell the two cases apart because both return
|
|
128
|
+
* `copied: false`, but the caller can. Project scope emits no line: there is
|
|
129
|
+
* no action to take, and the envelope's `bridgeHookCopy` / `codeGateHookCopy`
|
|
130
|
+
* field still reports `copied: false` for anyone reading the JSON.
|
|
131
|
+
*/
|
|
132
|
+
function describeHookCopy(label, copy, scope, dryRun) {
|
|
133
|
+
if (scope !== 'global')
|
|
134
|
+
return null;
|
|
135
|
+
if (copy.copied) {
|
|
136
|
+
return dryRun ? `would copy ${label} from ${copy.source} to ${copy.target}` : `Copied ${label}: ${copy.target}`;
|
|
137
|
+
}
|
|
138
|
+
return `${label} NOT copied — source missing at ${copy.source}. Run the build (\`pnpm build\`) so the script ships with the package.`;
|
|
139
|
+
}
|
|
111
140
|
function copyBridgeHookIfPresent(userHome, dryRun = false) {
|
|
112
141
|
const source = resolve(MODULE_DIR, '..', '..', 'services', 'hooks', 'pre-tool-superpowers-bridge.sh');
|
|
113
142
|
const target = resolve(userHome, '.claude', 'skills', 'peaks-code', 'hooks', 'pre-tool-superpowers-bridge.sh');
|
|
@@ -202,22 +231,16 @@ export function registerHooksCommands(program, io) {
|
|
|
202
231
|
// committed, shared file.
|
|
203
232
|
...plan.entryTargets.map((entry) => `would write ${entry.matcher || '(no matcher)'} → ${entry.sentinel} to ${entry.settingsPath}`),
|
|
204
233
|
`would write ${listSuperpowersDenyEntries().length} permissions.deny entries (Layer 3 worktree governance)`,
|
|
205
|
-
bridgeCopy
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
codeGateCopy.copied
|
|
209
|
-
? `would copy code-gate hook from ${codeGateCopy.source} to ${codeGateCopy.target}`
|
|
210
|
-
: 'would not copy code-gate hook (source missing or non-global scope)'
|
|
211
|
-
]), options.json);
|
|
234
|
+
describeHookCopy('bridge hook', bridgeCopy, scope, true),
|
|
235
|
+
describeHookCopy('code-gate hook', codeGateCopy, scope, true)
|
|
236
|
+
].filter((line) => line !== null)), options.json);
|
|
212
237
|
return;
|
|
213
238
|
}
|
|
214
239
|
const result = applyHookInstall(scope, projectRoot, { ide, skipProgress });
|
|
215
|
-
// Slice #3: build the per-IDE entries summary
|
|
216
|
-
//
|
|
217
|
-
// claude-code default). The
|
|
218
|
-
//
|
|
219
|
-
// entry; the summary mirrors the install shape, NOT a hardcoded
|
|
220
|
-
// expected list.
|
|
240
|
+
// Slice #3: build the per-IDE entries summary for the IDE the user
|
|
241
|
+
// targeted, not the slice #1 PEAKS_HOOK_ENTRIES constant (which is the
|
|
242
|
+
// claude-code default). The summary is derived from the install's own
|
|
243
|
+
// entry table — see `listExpectedEntriesForIde`.
|
|
221
244
|
const installedEntries = listExpectedEntriesForIde(ide, skipProgress);
|
|
222
245
|
// Slice 2026-07-24-peaks-code-bridge-002-rootcause (G6b / G10): when
|
|
223
246
|
// the install targets global scope, also copy the superpowers-bridge
|
|
@@ -242,16 +265,10 @@ export function registerHooksCommands(program, io) {
|
|
|
242
265
|
// Slice 2026-07-29-worktree-layer3-deny: surface L3 deny
|
|
243
266
|
// write alongside the hook install — single atomic write.
|
|
244
267
|
`Layer 3 deny: wrote ${listSuperpowersDenyEntries().length} permissions.deny entries (worktree governance)`,
|
|
245
|
-
bridgeCopy
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
? `Copied code-gate hook: ${codeGateCopy.target}`
|
|
250
|
-
: 'Code-gate hook not copied (source missing or non-global scope)'
|
|
251
|
-
]
|
|
252
|
-
: (bridgeCopy.copied
|
|
253
|
-
? [`Bridge hook copied: ${bridgeCopy.target}`]
|
|
254
|
-
: []);
|
|
268
|
+
describeHookCopy('bridge hook', bridgeCopy, scope, false),
|
|
269
|
+
describeHookCopy('code-gate hook', codeGateCopy, scope, false)
|
|
270
|
+
].filter((line) => line !== null)
|
|
271
|
+
: [describeHookCopy('bridge hook', bridgeCopy, scope, false)].filter((line) => line !== null);
|
|
255
272
|
// Slice 2026-07-29-worktree-layer3-deny: emit L3 deny bookkeeping
|
|
256
273
|
// in the JSON envelope so downstream automation (audit / sc) can
|
|
257
274
|
// confirm Layer 3 was applied without re-reading the file. When
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync, readdirSync, realpathSync as realpathSyncNative } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
2
3
|
import { fail, getErrorMessage, ok } from 'peaks-loop-shared/result';
|
|
3
4
|
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
4
5
|
import { readSharedChannel, writeSharedEntry, SHARED_CHANNEL_SOFT_VALUE_WARN } from 'peaks-loop-shared-channel';
|
|
@@ -275,25 +276,45 @@ export function registerAwaitCommand(parent, io) {
|
|
|
275
276
|
const adapter = getAdapter(ide);
|
|
276
277
|
const dispatcher = adapter.subAgentDispatcher;
|
|
277
278
|
if (typeof dispatcher.awaitBatch !== 'function') {
|
|
278
|
-
printResult(io, fail('sub-agent.await', 'IDE_NOT_SUPPORTED', `IDE ${ide} does not support awaitBatch
|
|
279
|
-
'
|
|
279
|
+
printResult(io, fail('sub-agent.await', 'IDE_NOT_SUPPORTED', `IDE ${ide} does not support awaitBatch`, { ok: false }, [
|
|
280
|
+
'Every built-in adapter has an awaitBatch since slice 1.3; a dispatcher without one is a custom adapter registered outside the built-in set.'
|
|
280
281
|
]), asJson);
|
|
281
282
|
process.exitCode = 1;
|
|
282
283
|
return;
|
|
283
284
|
}
|
|
284
|
-
// 1.2 MVP: we don't keep a separate record path index for DAG-dispatched
|
|
285
|
-
// batches yet; the caller is expected to have a single shared record
|
|
286
|
-
// directory. We pass the empty list — the MVP runner tracks outcomes
|
|
287
|
-
// through its own contract-store writes; the dispatcher just signals
|
|
288
|
-
// "ready to await" through the awaitBatch LRU queue (slice 1.3
|
|
289
|
-
// upgrades to cross-process heartbeat polling).
|
|
290
|
-
const input = {
|
|
291
|
-
batchId: options.batch,
|
|
292
|
-
dispatchCount: 1,
|
|
293
|
-
recordPaths: [],
|
|
294
|
-
...(timeoutMs !== undefined ? { timeoutMs } : {})
|
|
295
|
-
};
|
|
296
285
|
try {
|
|
286
|
+
// Slice 2026-09-16-n1-await-reports: resolve the batch's records from the
|
|
287
|
+
// session's dispatch directory. `recordPaths` used to be hardcoded to
|
|
288
|
+
// `[]`, and `awaitBatch` short-circuits on an empty list
|
|
289
|
+
// (await-batch.ts:134) — so `await` reported zero results and exited 0
|
|
290
|
+
// for every batch, forever. A batch we cannot locate is now a failure,
|
|
291
|
+
// not a success with nothing in it.
|
|
292
|
+
const { readRecord } = await import('../../services/dispatch/dispatch-record-writer.js');
|
|
293
|
+
const scan = resolveBatchRecords({
|
|
294
|
+
projectRoot,
|
|
295
|
+
sessionId: sid,
|
|
296
|
+
batchId: options.batch,
|
|
297
|
+
readOne: readRecord
|
|
298
|
+
});
|
|
299
|
+
if (scan.recordPaths.length === 0) {
|
|
300
|
+
const unreadableNote = scan.unreadable.length > 0
|
|
301
|
+
? ` ${scan.unreadable.length} dispatch record(s) there could not be read, so they could not be matched to this batch: ${scan.unreadable.join(', ')}`
|
|
302
|
+
: '';
|
|
303
|
+
printResult(io, fail('sub-agent.await', 'NO_DISPATCH_RECORDS', `No dispatch record with batchId=${options.batch} under ${scan.sessionDir}.${unreadableNote}`, {
|
|
304
|
+
ok: false,
|
|
305
|
+
batchId: options.batch,
|
|
306
|
+
sessionDir: scan.sessionDir,
|
|
307
|
+
unreadableRecords: scan.unreadable
|
|
308
|
+
}, [awaitErrorNextActions('NO_DISPATCH_RECORDS')]), asJson);
|
|
309
|
+
process.exitCode = 1;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const input = {
|
|
313
|
+
batchId: options.batch,
|
|
314
|
+
dispatchCount: scan.recordPaths.length,
|
|
315
|
+
recordPaths: scan.recordPaths,
|
|
316
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {})
|
|
317
|
+
};
|
|
297
318
|
const results = await dispatcher.awaitBatch(input);
|
|
298
319
|
const summary = summarizeBatchResults(results);
|
|
299
320
|
printResult(io, ok('sub-agent.await', {
|
|
@@ -302,9 +323,19 @@ export function registerAwaitCommand(parent, io) {
|
|
|
302
323
|
batchId: options.batch,
|
|
303
324
|
ide: dispatcher.label,
|
|
304
325
|
results,
|
|
305
|
-
summary
|
|
306
|
-
|
|
307
|
-
|
|
326
|
+
summary,
|
|
327
|
+
unreadableRecords: scan.unreadable
|
|
328
|
+
}, scan.unreadable.length > 0
|
|
329
|
+
? [`${scan.unreadable.length} unreadable dispatch record(s) in this session were skipped and are NOT part of the results: ${scan.unreadable.join(', ')}`]
|
|
330
|
+
: [], [
|
|
331
|
+
// Slice 2026-09-15-s9: corrected. This used to tell users that the
|
|
332
|
+
// four non-Claude IDEs would report `awaitByLlm: <ide> 1.2 fallback`,
|
|
333
|
+
// the slice-1.2 marker that slice 1.3 replaced with a real
|
|
334
|
+
// file-polling await. The text survived because nothing tested it —
|
|
335
|
+
// no adapter produces that note any more (asserted in
|
|
336
|
+
// sub-agent-dispatchers.test.ts), and the emitter that produced it,
|
|
337
|
+
// `awaitByLlmFallback`, has since been removed.
|
|
338
|
+
`Each non-claude-code IDE labels its own results (see the \`note\` field), so a timed-out slot is attributable to the adapter it came from.`
|
|
308
339
|
]), asJson);
|
|
309
340
|
}
|
|
310
341
|
catch (error) {
|
|
@@ -323,8 +354,70 @@ function awaitErrorNextActions(code) {
|
|
|
323
354
|
if (code === 'IDE_NOT_SUPPORTED') {
|
|
324
355
|
return 'Switch to claude-code, or rely on LLM-side await for non-claude-code IDEs in slice 1.3.';
|
|
325
356
|
}
|
|
357
|
+
if (code === 'NO_DISPATCH_RECORDS') {
|
|
358
|
+
return 'Check --session-id / --project: records live under .peaks/_sub_agents/<sessionId>/. Use the batchId exactly as printed by the dispatch envelope.';
|
|
359
|
+
}
|
|
326
360
|
return 'See error message; check that --batch matches the dispatch envelope and --timeout is a positive integer ms.';
|
|
327
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Resolve a record's on-disk path through `realpathSync` so the value returned
|
|
364
|
+
* here matches the value `writeInitialDispatchRecord` produces — which itself
|
|
365
|
+
* passes through `assertSafeDispatchRecordPath` and ends up canonicalized. On
|
|
366
|
+
* macOS, `mkdtempSync(join(tmpdir(), prefix))` returns `/var/folders/...`
|
|
367
|
+
* while `process.cwd()` after `chdir` returns `/private/var/folders/...`;
|
|
368
|
+
* without this helper the test fixture's `queuedPath` (canonical) and the
|
|
369
|
+
* envelope's `finalized[].recordPath` (raw) never compare equal. Falls back
|
|
370
|
+
* to the lexical path when the file is missing (race between scan and write).
|
|
371
|
+
*/
|
|
372
|
+
function safeRecordPath(p) {
|
|
373
|
+
try {
|
|
374
|
+
return realpathSyncNative(p);
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
return p;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Slice 2026-09-16-n1-await-reports: which on-disk records belong to a batch.
|
|
382
|
+
*
|
|
383
|
+
* Reuses the conventions already in the repo instead of inventing a new one:
|
|
384
|
+
* - records live at `.peaks/_sub_agents/<sid>/dispatch-<rid>-<ts>.json`
|
|
385
|
+
* (`dispatchRecordPath`, src/services/security/safe-settings-path.ts);
|
|
386
|
+
* - a record's batch is its own `batchId` field, written by
|
|
387
|
+
* `writeInitialDispatchRecord`. The `--batch` branch of `finalize` (below)
|
|
388
|
+
* and `findBatchRecords` in heartbeat-watch-command.ts resolve a batch the
|
|
389
|
+
* same way: scan the session dir, filter `dispatch-*.json`, compare field.
|
|
390
|
+
*
|
|
391
|
+
* `unreadable` lists the `dispatch-*.json` candidates whose batch could NOT be
|
|
392
|
+
* determined. The caller must not fold them into "no such batch": a record it
|
|
393
|
+
* cannot read is not evidence that the batch is empty.
|
|
394
|
+
*/
|
|
395
|
+
function resolveBatchRecords(input) {
|
|
396
|
+
const sessionDir = resolve(input.projectRoot, '.peaks', '_sub_agents', input.sessionId);
|
|
397
|
+
const recordPaths = [];
|
|
398
|
+
const unreadable = [];
|
|
399
|
+
if (!existsSync(sessionDir))
|
|
400
|
+
return { sessionDir, recordPaths, unreadable };
|
|
401
|
+
for (const name of readdirSync(sessionDir)) {
|
|
402
|
+
// `active-dispatches.json` (the index) and `batch-<uuid>.counter.json` are
|
|
403
|
+
// not records; neither carries a `version`, so `readRecord` would reject
|
|
404
|
+
// them as "version mismatch". Same filter as the `--batch` branch below.
|
|
405
|
+
if (!name.startsWith('dispatch-') || !name.endsWith('.json'))
|
|
406
|
+
continue;
|
|
407
|
+
const recordPath = safeRecordPath(join(sessionDir, name));
|
|
408
|
+
let batchId;
|
|
409
|
+
try {
|
|
410
|
+
batchId = input.readOne(recordPath).batchId;
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
unreadable.push(recordPath);
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (batchId === input.batchId)
|
|
417
|
+
recordPaths.push(recordPath);
|
|
418
|
+
}
|
|
419
|
+
return { sessionDir, recordPaths, unreadable };
|
|
420
|
+
}
|
|
328
421
|
export function registerFinalizeCommand(parent, io) {
|
|
329
422
|
addJsonOption(parent
|
|
330
423
|
.command('finalize')
|
|
@@ -418,7 +511,7 @@ export function registerFinalizeCommand(parent, io) {
|
|
|
418
511
|
// `--batch` branch below has always used this same filter.
|
|
419
512
|
if (!f.startsWith('dispatch-') || !f.endsWith('.json'))
|
|
420
513
|
continue;
|
|
421
|
-
const p = path2.join(dir, f);
|
|
514
|
+
const p = safeRecordPath(path2.join(dir, f));
|
|
422
515
|
const r = tryReadRecord(p);
|
|
423
516
|
if (r === null || r.requestId !== options.requestId)
|
|
424
517
|
continue;
|
|
@@ -470,7 +563,7 @@ export function registerFinalizeCommand(parent, io) {
|
|
|
470
563
|
for (const f of fs2.readdirSync(dir)) {
|
|
471
564
|
if (!f.startsWith('dispatch-') || !f.endsWith('.json'))
|
|
472
565
|
continue;
|
|
473
|
-
const p = path2.join(dir, f);
|
|
566
|
+
const p = safeRecordPath(path2.join(dir, f));
|
|
474
567
|
const r = tryReadRecord(p);
|
|
475
568
|
if (r === null)
|
|
476
569
|
continue;
|
|
@@ -161,7 +161,14 @@ export async function runWebOp(io, op, args, asJson) {
|
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
163
|
const info = await ensureDaemon(projectRoot, sessionId);
|
|
164
|
-
|
|
164
|
+
// The daemon reads `projectRoot` back from `daemon.json` — the record it
|
|
165
|
+
// itself wrote — and that round-trip is the only path that survives the
|
|
166
|
+
// macOS `/var` <-> `/private/var` symlink: the writer's prefix may not
|
|
167
|
+
// match `projectRoot` resolved from `process.cwd()` (the kernel resolves
|
|
168
|
+
// symlinks on `chdir`), and the integration test asserts on that exact
|
|
169
|
+
// round-tripped value.
|
|
170
|
+
const daemonProjectRoot = info.projectRoot;
|
|
171
|
+
const response = await new WebDaemonClient(info).call(op, { ...opArgs, dispatchId: dispatchId(), projectRoot: daemonProjectRoot, sessionId }, OP_TIMEOUT_MS);
|
|
165
172
|
if (!response.ok || response.data === null) {
|
|
166
173
|
const code = safeDaemonCode(response.code);
|
|
167
174
|
// The daemon no longer downloads (R3), so "the browser is not installed"
|
|
@@ -60,4 +60,10 @@ export interface WorkflowTerminalizeOptions {
|
|
|
60
60
|
readonly requireConsumed?: boolean;
|
|
61
61
|
readonly json?: boolean;
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The literal that means "no session could be resolved". Kept as a named
|
|
65
|
+
* constant because three call sites now have to ASK whether resolution
|
|
66
|
+
* failed rather than consume the value as if it were an id.
|
|
67
|
+
*/
|
|
68
|
+
export declare const UNKNOWN_SESSION_ID = "unknown-sid";
|
|
63
69
|
export declare function registerWorkflowLifecycleCommand(parent: Command, io: ProgramIO): void;
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import { fail, ok, getErrorMessage } from 'peaks-loop-shared/result';
|
|
10
10
|
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
11
11
|
import { resolveCallerId } from '../../services/session/resolve-caller-id.js';
|
|
12
|
+
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
13
|
+
import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
|
|
12
14
|
import { initWorkflow, terminalizeWorkflow, } from '../../services/workflow/workflow-presence-lifecycle.js';
|
|
13
15
|
import { readGraph, emptyGraph } from '../../services/workflow/workflow-graph-store.js';
|
|
14
16
|
import { WORKFLOW_ID_REGEX, TERMINAL_REASONS } from '../../services/workflow/workflow-graph-types.js';
|
|
@@ -26,8 +28,62 @@ function deriveCallerId() {
|
|
|
26
28
|
function deriveProjectRoot(options) {
|
|
27
29
|
return options.project ?? process.cwd();
|
|
28
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The literal that means "no session could be resolved". Kept as a named
|
|
33
|
+
* constant because three call sites now have to ASK whether resolution
|
|
34
|
+
* failed rather than consume the value as if it were an id.
|
|
35
|
+
*/
|
|
36
|
+
export const UNKNOWN_SESSION_ID = 'unknown-sid';
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the session id for every `workflow *` command.
|
|
39
|
+
*
|
|
40
|
+
* Tier order (matches the idiom every other command family in this CLI
|
|
41
|
+
* already uses — see `job-commands.ts:85`, `container-commands.ts:138`):
|
|
42
|
+
*
|
|
43
|
+
* 1. `--session-id <sid>` — explicit wins
|
|
44
|
+
* 2. `PEAKS_SESSION_ID` env var — scripted / sub-process callers
|
|
45
|
+
* 3. `getCurrentSessionId(projectRoot)` — this caller's OWN binding
|
|
46
|
+
* (`callers/<callerId>.json`) falling back to the project-global
|
|
47
|
+
* `.peaks/_runtime/session.json`, the same resolver `peaks session
|
|
48
|
+
* info --active` and `peaks session checkpoint` use
|
|
49
|
+
* 4. `unknown-sid` — nothing is bound
|
|
50
|
+
*
|
|
51
|
+
* Tier 3 was MISSING until 2026-09-15 (S6), and its absence is the whole
|
|
52
|
+
* defect: `workflow init` resolved on tiers 1–2 only, so with no flag and no
|
|
53
|
+
* env var it wrote the graph into `.peaks/_runtime/unknown-sid/graphs/`
|
|
54
|
+
* regardless of what the binding file said. `peaks workflow node prepare`
|
|
55
|
+
* then ran under the CORRECT session dir and reported `PEAKS_GRAPH_NOT_FOUND`
|
|
56
|
+
* — the graph it was asking for had been written one bucket over. The bucket
|
|
57
|
+
* accumulated artifacts from at least 4 distinct caller ids between 2026-09-01
|
|
58
|
+
* and 2026-09-15, i.e. it had never worked for anyone.
|
|
59
|
+
*
|
|
60
|
+
* The same prior-art sweep (`.peaks/memory/archived/2026-06-26-unknown-sid-root-cause.md`)
|
|
61
|
+
* converted six inline `?? 'unknown-sid'` sites to this 4-tier chain;
|
|
62
|
+
* `workflow-lifecycle-commands.ts` was missed. This is that site.
|
|
63
|
+
*
|
|
64
|
+
* The project root passed to tier 3 goes through the same `findProjectRoot`
|
|
65
|
+
* walk `session info` uses, so a command run from a SUBDIRECTORY of the
|
|
66
|
+
* project resolves the project's binding instead of `cwd`'s (which has none).
|
|
67
|
+
*/
|
|
29
68
|
function deriveSessionId(options) {
|
|
30
|
-
|
|
69
|
+
if (typeof options.sessionId === 'string' && options.sessionId.length > 0)
|
|
70
|
+
return options.sessionId;
|
|
71
|
+
const fromEnv = process.env.PEAKS_SESSION_ID;
|
|
72
|
+
if (typeof fromEnv === 'string' && fromEnv.length > 0)
|
|
73
|
+
return fromEnv;
|
|
74
|
+
return getCurrentSessionId(findProjectRoot(options.project ?? process.cwd()) ?? (options.project ?? process.cwd())) ?? UNKNOWN_SESSION_ID;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Tier 3 resolving to `unknown-sid` means NO session is bound at all. A
|
|
78
|
+
* `workflow` subcommand that WRITES must fail loudly on that rather than
|
|
79
|
+
* create a bucket named after a failure — `graph list` already does (see its
|
|
80
|
+
* `PEAKS_SESSION_NOT_BOUND` guard below); `init` did not.
|
|
81
|
+
*/
|
|
82
|
+
function buildSessionNotBoundEnvelope(sessionId, projectRoot) {
|
|
83
|
+
return fail('workflow.init', 'PEAKS_SESSION_NOT_BOUND', `No peaks session is bound for '${projectRoot}' (derived session id '${sessionId}'): --session-id, PEAKS_SESSION_ID and the project binding are all absent.`, { workflowId: null }, [
|
|
84
|
+
'Run `peaks workspace init --project <p>` to bind a session, then re-run.',
|
|
85
|
+
'Or pass `--session-id <sid>` explicitly.',
|
|
86
|
+
]);
|
|
31
87
|
}
|
|
32
88
|
export function registerWorkflowLifecycleCommand(parent, io) {
|
|
33
89
|
// Reuse the existing `workflow` parent (created by `registerWorkflowCommands`)
|
|
@@ -46,8 +102,13 @@ export function registerWorkflowLifecycleCommand(parent, io) {
|
|
|
46
102
|
const asJson = options.json === true;
|
|
47
103
|
try {
|
|
48
104
|
const callerId = deriveCallerId();
|
|
49
|
-
const sessionId = deriveSessionId(options);
|
|
50
105
|
const projectRoot = deriveProjectRoot(options);
|
|
106
|
+
const sessionId = deriveSessionId(options);
|
|
107
|
+
if (sessionId === UNKNOWN_SESSION_ID) {
|
|
108
|
+
printResult(io, buildSessionNotBoundEnvelope(sessionId, projectRoot), asJson);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
51
112
|
const workflowId = options.workflowId ?? `wf-${Date.now().toString(36)}`;
|
|
52
113
|
if (!WORKFLOW_ID_REGEX.test(workflowId)) {
|
|
53
114
|
throw new Error(`workflowId shape invalid: ${workflowId}`);
|
|
@@ -120,7 +181,7 @@ export function registerWorkflowLifecycleCommand(parent, io) {
|
|
|
120
181
|
const asJson = options.json === true;
|
|
121
182
|
try {
|
|
122
183
|
const sessionId = deriveSessionId(options);
|
|
123
|
-
if (
|
|
184
|
+
if (sessionId === UNKNOWN_SESSION_ID) {
|
|
124
185
|
throw new Error('PEAKS_SESSION_NOT_BOUND: no session id');
|
|
125
186
|
}
|
|
126
187
|
const result = { envelopeVersion: '4.0.8', sessionId, graphs: [] };
|
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ DEAD CODE — NOT THE LIVE VENDOR LAYER, AND NOT THE LIVE IDE LAYER.
|
|
3
|
+
*
|
|
4
|
+
* Three things in this repo are named `<Vendor>Adapter`, and only one of
|
|
5
|
+
* them is live. Read this table before importing anything from here:
|
|
6
|
+
*
|
|
7
|
+
* 1. `src/services/runtime/vendors/<vendor>.ts`
|
|
8
|
+
* → `class CodexAdapter implements VendorAdapter` (runtime detect +
|
|
9
|
+
* compact). LIVE — wired by `RuntimeService`'s built-in list. This is
|
|
10
|
+
* the one a reader looking for "the codex adapter" wants.
|
|
11
|
+
* 2. `packages/peaks-loop-internal-runtime/src/vendor/<vendor>-adapter.ts`
|
|
12
|
+
* → a second `CodexAdapter`, different surface (binary / headlessArgs /
|
|
13
|
+
* parseStatusLine / detectInstalled) for the dispatched-sub-agent
|
|
14
|
+
* runtime. LIVE — exported by that package's public index.
|
|
15
|
+
* 3. THIS FILE'S SIBLINGS — `claude-adapter.ts` / `codex-adapter.ts` /
|
|
16
|
+
* `copilot-adapter.ts` + `auto-adapter.ts`.
|
|
17
|
+
* → **UNREFERENCED.** Zero importers in `src/`, `tests/`, `packages/`
|
|
18
|
+
* or `scripts/`. Every method throws `ADAPTER_NOT_IMPLEMENTED`; the
|
|
19
|
+
* one exception, `detect()` on the codex/copilot pair, is a hardcoded
|
|
20
|
+
* `return false`. `peaks skill adapter list` (the only CLI surface
|
|
21
|
+
* that names these) does not read them — it returns a literal array.
|
|
22
|
+
*
|
|
23
|
+
* The three layers diverged from a single planned skill-adapter surface and
|
|
24
|
+
* were never reconciled. These placeholder files are kept because they mark
|
|
25
|
+
* intent for unimplemented work; they are NOT kept because anything uses
|
|
26
|
+
* them. If you are here to implement them, the decision you actually need is
|
|
27
|
+
* which of layers 1–3 survives — see
|
|
28
|
+
* `tests/unit/runtime/vendor-adapter-layer.test.ts`, which pins which layer
|
|
29
|
+
* is live and fails the moment that stops being true.
|
|
30
|
+
*/
|
|
1
31
|
export interface AdapterSegment {
|
|
2
32
|
name: string;
|
|
3
33
|
skillMd: string;
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ DEAD CODE — zero importers in `src/`, `tests/`, `packages/`, `scripts/`.
|
|
3
|
+
* See the block comment on `src/services/adapter/adapter.ts`.
|
|
4
|
+
*
|
|
5
|
+
* `detectAndPick()` picks the first adapter whose `detect()` returns true.
|
|
6
|
+
* It cannot pick anything today: its only candidate implementations are its
|
|
7
|
+
* three dead siblings, and the codex/copilot pair hardcode `detect()` to
|
|
8
|
+
* `false` while the claude one hardcodes `true` — so the "pick" is a
|
|
9
|
+
* constant that never varies with the environment. It is not wired to
|
|
10
|
+
* `peaks skill adapter set-active` either; that CLI verb
|
|
11
|
+
* (`src/cli/commands/adapter-commands.ts`) echoes back the name it was given
|
|
12
|
+
* and reads no adapter file.
|
|
13
|
+
*/
|
|
1
14
|
import { Adapter } from "./adapter.js";
|
|
2
15
|
type Detectable = Pick<Adapter, "name" | "detect">;
|
|
3
16
|
export declare class AutoAdapter {
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ DEAD CODE — not a live adapter. See the block comment on
|
|
3
|
+
* `src/services/adapter/adapter.ts` for the three same-named layers.
|
|
4
|
+
*
|
|
5
|
+
* This is the only file in the cluster with a real implementation, which is
|
|
6
|
+
* what makes it the most misleading one: it writes a real
|
|
7
|
+
* `~/.claude/skills/peaks-bee-<name>.peaks-generated/SKILL.md` scratch dir if
|
|
8
|
+
* you call it. Nothing calls it — zero importers in `src/`, `tests/`,
|
|
9
|
+
* `packages/` or `scripts/`. The live Claude Code knowledge lives in
|
|
10
|
+
* `src/services/ide/adapters/claude-code-adapter.ts` (IDE surface) and
|
|
11
|
+
* `src/services/runtime/vendors/claude-code.ts` (runtime compact).
|
|
12
|
+
*/
|
|
1
13
|
import { writeFileSync, mkdirSync, rmSync, existsSync } from "node:fs";
|
|
2
14
|
import { join } from "node:path";
|
|
3
15
|
export class ClaudeAdapter {
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ DEAD CODE — not the live `CodexAdapter`. See the block comment on
|
|
3
|
+
* `src/services/adapter/adapter.ts` for the three same-named layers, and
|
|
4
|
+
* `tests/unit/runtime/vendor-adapter-layer.test.ts` for the guard.
|
|
5
|
+
*
|
|
6
|
+
* The class below has zero importers anywhere in this repo. The live codex
|
|
7
|
+
* adapter is `src/services/runtime/vendors/codex.ts` (runtime detect +
|
|
8
|
+
* compact, wired into `RuntimeService`) and
|
|
9
|
+
* `packages/peaks-loop-internal-runtime/src/vendor/codex-adapter.ts` (the
|
|
10
|
+
* dispatched-sub-agent runtime). `detect()` here returns a hardcoded
|
|
11
|
+
* `false`; every other method throws `ADAPTER_NOT_IMPLEMENTED`.
|
|
12
|
+
*/
|
|
1
13
|
import { Adapter } from "./adapter.js";
|
|
2
14
|
export declare class CodexAdapter implements Pick<Adapter, "name"> {
|
|
3
15
|
private readonly _o;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ DEAD CODE — not the live `CodexAdapter`. See the block comment on
|
|
3
|
+
* `src/services/adapter/adapter.ts` for the three same-named layers, and
|
|
4
|
+
* `tests/unit/runtime/vendor-adapter-layer.test.ts` for the guard.
|
|
5
|
+
*
|
|
6
|
+
* The class below has zero importers anywhere in this repo. The live codex
|
|
7
|
+
* adapter is `src/services/runtime/vendors/codex.ts` (runtime detect +
|
|
8
|
+
* compact, wired into `RuntimeService`) and
|
|
9
|
+
* `packages/peaks-loop-internal-runtime/src/vendor/codex-adapter.ts` (the
|
|
10
|
+
* dispatched-sub-agent runtime). `detect()` here returns a hardcoded
|
|
11
|
+
* `false`; every other method throws `ADAPTER_NOT_IMPLEMENTED`.
|
|
12
|
+
*/
|
|
1
13
|
import { ADAPTER_NOT_IMPLEMENTED } from "./adapter.js";
|
|
2
14
|
export class CodexAdapter {
|
|
3
15
|
_o;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ DEAD CODE — not the live `CopilotAdapter`. See the block comment on
|
|
3
|
+
* `src/services/adapter/adapter.ts` for the three same-named layers, and
|
|
4
|
+
* `tests/unit/runtime/vendor-adapter-layer.test.ts` for the guard.
|
|
5
|
+
*
|
|
6
|
+
* The class below has zero importers anywhere in this repo. The live copilot
|
|
7
|
+
* adapter is `src/services/runtime/vendors/copilot.ts` (runtime detect +
|
|
8
|
+
* compact, wired into `RuntimeService`) and
|
|
9
|
+
* `packages/peaks-loop-internal-runtime/src/vendor/copilot-adapter.ts` (the
|
|
10
|
+
* dispatched-sub-agent runtime). `detect()` here returns a hardcoded
|
|
11
|
+
* `false`; every other method throws `ADAPTER_NOT_IMPLEMENTED`.
|
|
12
|
+
*/
|
|
1
13
|
import { Adapter } from "./adapter.js";
|
|
2
14
|
export declare class CopilotAdapter implements Pick<Adapter, "name"> {
|
|
3
15
|
private readonly _o;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ DEAD CODE — not the live `CopilotAdapter`. See the block comment on
|
|
3
|
+
* `src/services/adapter/adapter.ts` for the three same-named layers, and
|
|
4
|
+
* `tests/unit/runtime/vendor-adapter-layer.test.ts` for the guard.
|
|
5
|
+
*
|
|
6
|
+
* The class below has zero importers anywhere in this repo. The live copilot
|
|
7
|
+
* adapter is `src/services/runtime/vendors/copilot.ts` (runtime detect +
|
|
8
|
+
* compact, wired into `RuntimeService`) and
|
|
9
|
+
* `packages/peaks-loop-internal-runtime/src/vendor/copilot-adapter.ts` (the
|
|
10
|
+
* dispatched-sub-agent runtime). `detect()` here returns a hardcoded
|
|
11
|
+
* `false`; every other method throws `ADAPTER_NOT_IMPLEMENTED`.
|
|
12
|
+
*/
|
|
1
13
|
import { ADAPTER_NOT_IMPLEMENTED } from "./adapter.js";
|
|
2
14
|
export class CopilotAdapter {
|
|
3
15
|
_o;
|
|
@@ -2,23 +2,41 @@
|
|
|
2
2
|
* Backing detector — classifies each red line as `cli-backed`, `partial`,
|
|
3
3
|
* or `prose-only`. The classifier already sets the backing for catalog hits
|
|
4
4
|
* (cli-backed when an enforcer file path is present). This module exists to
|
|
5
|
-
* handle the post-classification
|
|
6
|
-
* (a gate exists but the LLM can bypass it)
|
|
7
|
-
*
|
|
5
|
+
* handle the post-classification nuances: heuristics for the "partial" tier
|
|
6
|
+
* (a gate exists but the LLM can bypass it), verification that the enforcer
|
|
7
|
+
* file exists on disk, and — since A9 of the 2026-09-15 diagnosis —
|
|
8
|
+
* verification that the enforcer is actually reachable from a call site.
|
|
9
|
+
*
|
|
10
|
+
* `cli-backed` means "a CLI surface runs this rule". A file that exists but
|
|
11
|
+
* that nothing imports runs nothing, so it is `prose-only` with the same
|
|
12
|
+
* weight as an unwritten rule. Callers pass the live set from
|
|
13
|
+
* `enforcer-liveness.ts`; `null` means "liveness undecidable for this
|
|
14
|
+
* project" (no `src/` tree), in which case the existing file is trusted.
|
|
8
15
|
*/
|
|
9
16
|
import type { RedLineEntry } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* `liveEnforcers` is the set of enforcerRefs with a call site (see
|
|
19
|
+
* `enforcer-liveness.ts`), or `null` when the project has no `src/` tree to
|
|
20
|
+
* scan and liveness therefore cannot be decided.
|
|
21
|
+
*/
|
|
22
|
+
export type LiveEnforcerSet = ReadonlySet<string> | null;
|
|
10
23
|
export interface BackingResult {
|
|
11
24
|
readonly entry: RedLineEntry;
|
|
12
25
|
readonly enforcerExists: boolean;
|
|
26
|
+
/** True when the enforcer file exists but no call site imports it. */
|
|
27
|
+
readonly enforcerDead: boolean;
|
|
13
28
|
}
|
|
14
29
|
/**
|
|
15
30
|
* Re-classify a single RedLineEntry. Returns a new entry with the
|
|
16
|
-
* `backing` field updated
|
|
17
|
-
*
|
|
31
|
+
* `backing` field updated; `enforcerRef` is preserved even when the
|
|
32
|
+
* backing is downgraded, because the dead reference is the triage
|
|
33
|
+
* information the report needs.
|
|
18
34
|
*/
|
|
19
|
-
export declare function classifyBacking(entry: RedLineEntry, projectRoot: string): BackingResult;
|
|
35
|
+
export declare function classifyBacking(entry: RedLineEntry, projectRoot: string, liveEnforcers: LiveEnforcerSet): BackingResult;
|
|
20
36
|
export interface BackingBatchResult {
|
|
21
37
|
readonly entries: readonly RedLineEntry[];
|
|
22
38
|
readonly warnings: readonly string[];
|
|
39
|
+
/** Sorted, deduplicated enforcerRefs downgraded for having no call site. */
|
|
40
|
+
readonly deadEnforcers: readonly string[];
|
|
23
41
|
}
|
|
24
|
-
export declare function classifyBackingBatch(entries: readonly RedLineEntry[], projectRoot: string): BackingBatchResult;
|
|
42
|
+
export declare function classifyBackingBatch(entries: readonly RedLineEntry[], projectRoot: string, liveEnforcers: LiveEnforcerSet): BackingBatchResult;
|