peaks-loop 4.0.32 → 4.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README-en.md +3 -4
- package/README.md +20 -28
- package/dist/cli/commands/_register.js +4 -3
- package/dist/cli/commands/audit-commands.js +12 -39
- package/dist/cli/commands/capability-commands.d.ts +6 -0
- package/dist/cli/commands/capability-commands.js +20 -2
- package/dist/cli/commands/code-commands.js +2 -0
- package/dist/cli/commands/code-mode-gate-commands.js +3 -3
- package/dist/cli/commands/code-mode-status-command.d.ts +17 -0
- package/dist/cli/commands/code-mode-status-command.js +51 -0
- package/dist/cli/commands/code-run-command.js +8 -1
- package/dist/cli/commands/code-runtime-commands.js +13 -5
- package/dist/cli/commands/core/skill-command.js +1 -1
- package/dist/cli/commands/ecc-commands.js +3 -2
- package/dist/cli/commands/project-commands.js +1 -1
- package/dist/cli/commands/request-commands.js +3 -7
- package/dist/cli/commands/session-24h-mode.js +9 -1
- package/dist/cli/commands/shadcn-commands.d.ts +32 -0
- package/dist/cli/commands/shadcn-commands.js +120 -0
- package/dist/cli/commands/slice-commands.js +1 -4
- package/dist/services/24h-mode/auto-engage.d.ts +39 -0
- package/dist/services/24h-mode/auto-engage.js +46 -0
- package/dist/services/24h-mode/decider.d.ts +11 -5
- package/dist/services/24h-mode/decider.js +13 -13
- package/dist/services/audit/enforcers/active-skill-resolver.d.ts +3 -1
- package/dist/services/audit/enforcers/active-skill-resolver.js +3 -5
- package/dist/services/audit/static-service.d.ts +1 -1
- package/dist/services/audit/static-service.js +1 -1
- package/dist/services/code/auto-compact-lifecycle.d.ts +1 -1
- package/dist/services/code/auto-compact-lifecycle.js +1 -1
- package/dist/services/code/auto-compact-modes.d.ts +1 -1
- package/dist/services/code/auto-compact-modes.js +1 -1
- package/dist/services/code/auto-compact-orchestrator.js +18 -22
- package/dist/services/code/mode-gate.d.ts +6 -2
- package/dist/services/code/mode-gate.js +9 -5
- package/dist/services/code/orchestrator-can-do.js +2 -2
- package/dist/services/code-review/ecc-bridge.d.ts +94 -15
- package/dist/services/code-review/ecc-bridge.js +133 -13
- package/dist/services/config/config-service.d.ts +17 -0
- package/dist/services/config/config-service.js +34 -0
- package/dist/services/context/auto-compact-dispatcher.d.ts +2 -2
- package/dist/services/context/auto-compact-dispatcher.js +4 -4
- package/dist/services/context/auto-compact-reader.js +8 -1
- package/dist/services/context/auto-compact-types.d.ts +13 -1
- package/dist/services/context/build-dispatch-system-prompt.js +2 -2
- package/dist/services/context/context-schema.d.ts +1 -1
- package/dist/services/dashboard/project-dashboard-service.d.ts +0 -7
- package/dist/services/dashboard/project-dashboard-service.js +1 -9
- package/dist/services/hooks/auto-compact-hook-install.d.ts +2 -2
- package/dist/services/hooks/auto-compact-hook-install.js +2 -2
- package/dist/services/ide/adapters/claude-code-adapter.d.ts +51 -0
- package/dist/services/ide/adapters/claude-code-adapter.js +84 -14
- package/dist/services/ide/ide-types.d.ts +10 -1
- package/dist/services/mode/mode-enforcement.d.ts +4 -1
- package/dist/services/mode/mode-enforcement.js +26 -28
- package/dist/services/mode/mode-status-service.d.ts +81 -0
- package/dist/services/mode/mode-status-service.js +108 -0
- package/dist/services/preferences/preferences-types.d.ts +16 -7
- package/dist/services/recommendations/capability-seed-items.js +0 -1
- package/dist/services/recommendations/installed-capability-detector.d.ts +33 -0
- package/dist/services/recommendations/installed-capability-detector.js +76 -0
- package/dist/services/scan/orphan-service.js +1 -1
- package/dist/services/skills/presence-lease-service.d.ts +21 -0
- package/dist/services/skills/presence-lease-service.js +26 -0
- package/dist/services/skills/skill-presence-service.d.ts +10 -1
- package/dist/services/skills/skill-presence-service.js +32 -4
- package/dist/services/skills/skill-statusline-service.js +6 -3
- package/dist/services/slice/multi-pass-orchestrator.js +0 -13
- package/dist/services/slice/slice-decompose-import-edges.d.ts +2 -2
- package/dist/services/slice/slice-decompose-import-edges.js +1 -29
- package/dist/services/slice/slice-decompose-runners.d.ts +2 -4
- package/dist/services/slice/slice-decompose-runners.js +1 -23
- package/dist/services/slice/slice-decompose-service.d.ts +3 -4
- package/dist/services/slice/slice-decompose-service.js +10 -29
- package/dist/services/slice/slice-decompose-types.d.ts +6 -47
- package/dist/services/slice/slice-decompose-types.js +3 -2
- package/dist/services/slice/slice-topology-types.d.ts +2 -3
- package/package.json +5 -5
- package/schemas/decomposition-v1.json +0 -12
- package/schemas/decomposition-v2.json +1 -15
- package/skills/bee/peaks-prd/SKILL.md +0 -1
- package/skills/bee/peaks-rd/SKILL.md +1 -1
- package/skills/bee/peaks-rd/references/external-references.md +1 -3
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -2
- package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +22 -8
- package/skills/bee/peaks-rd/references/rd-runbook.md +0 -2
- package/skills/bee/peaks-txt/SKILL.md +0 -12
- package/skills/peaks-code/SKILL.md +9 -9
- package/skills/peaks-code/references/mode-selection.md +7 -3
- package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
- package/skills/peaks-code/references/runbook.md +10 -5
- package/skills/peaks-code/references/session-overload-signal-index.md +2 -2
- package/skills/peaks-code/references/standards-preflight.md +1 -1
- package/skills/peaks-code/references/startup-sequence.md +1 -1
- package/skills/peaks-code/references/step-0-8-gate.md +1 -1
- package/skills/peaks-code/references/swarm-dispatch-contract.md +2 -2
- package/skills/peaks-code/references/workflow-gates-and-types.md +1 -1
- package/skills/peaks-doctor/SKILL.md +1 -1
- package/skills/peaks-slice-decompose/SKILL.md +0 -1
- package/skills/peaks-slice-decompose/references/v2-schema.md +1 -12
- package/dist/cli/commands/understand-commands.d.ts +0 -3
- package/dist/cli/commands/understand-commands.js +0 -148
- package/dist/services/understand/hybrid-types.d.ts +0 -32
- package/dist/services/understand/hybrid-types.js +0 -1
- package/dist/services/understand/understand-hybrid-service.d.ts +0 -10
- package/dist/services/understand/understand-hybrid-service.js +0 -100
- package/dist/services/understand/understand-scan-service.d.ts +0 -28
- package/dist/services/understand/understand-scan-service.js +0 -170
- package/dist/services/understand/understand-types.d.ts +0 -50
- package/dist/services/understand/understand-types.js +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only "is this capability already satisfied on this machine?"
|
|
3
|
+
* detector for the capability catalog (`peaks capability status` /
|
|
4
|
+
* `peaks capabilities --source ...`).
|
|
5
|
+
*
|
|
6
|
+
* Slice B1 of 2026-09-09-ecc-dynamic-and-cleanup: the previous
|
|
7
|
+
* `getInstalledCapabilityIds()` returned `[]` unconditionally, so the
|
|
8
|
+
* catalog could never report anything as installed. This module answers
|
|
9
|
+
* the question from evidence that already exists on disk — no install
|
|
10
|
+
* side effect, no network, no subprocess:
|
|
11
|
+
*
|
|
12
|
+
* - `everything-claude-code.*` — satisfied when the ECC cache is
|
|
13
|
+
* populated (`~/.peaks/cache/ecc-installed.json` + at least one
|
|
14
|
+
* cached/materialized agent).
|
|
15
|
+
* - a source that maps to an npm package — satisfied when that package
|
|
16
|
+
* (or its `.bin` shim) is present in the project's `node_modules`.
|
|
17
|
+
*
|
|
18
|
+
* Every probe is fail-soft: a throw degrades to "not installed" so the
|
|
19
|
+
* catalog never breaks because a probe failed.
|
|
20
|
+
*/
|
|
21
|
+
export type InstalledCapabilityProbe = {
|
|
22
|
+
/** Project whose `node_modules` is probed. */
|
|
23
|
+
readonly projectRoot: string;
|
|
24
|
+
/** Override for tests; defaults to `<projectRoot>/node_modules`. */
|
|
25
|
+
readonly nodeModulesDir?: string;
|
|
26
|
+
/** Override for tests; defaults to a live ECC cache probe. */
|
|
27
|
+
readonly eccCacheAvailable?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Return the capabilityIds whose acquisition is already satisfied.
|
|
31
|
+
* Sorted + de-duplicated so the JSON envelope is stable across runs.
|
|
32
|
+
*/
|
|
33
|
+
export declare function detectInstalledCapabilityIds(probe: InstalledCapabilityProbe): string[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only "is this capability already satisfied on this machine?"
|
|
3
|
+
* detector for the capability catalog (`peaks capability status` /
|
|
4
|
+
* `peaks capabilities --source ...`).
|
|
5
|
+
*
|
|
6
|
+
* Slice B1 of 2026-09-09-ecc-dynamic-and-cleanup: the previous
|
|
7
|
+
* `getInstalledCapabilityIds()` returned `[]` unconditionally, so the
|
|
8
|
+
* catalog could never report anything as installed. This module answers
|
|
9
|
+
* the question from evidence that already exists on disk — no install
|
|
10
|
+
* side effect, no network, no subprocess:
|
|
11
|
+
*
|
|
12
|
+
* - `everything-claude-code.*` — satisfied when the ECC cache is
|
|
13
|
+
* populated (`~/.peaks/cache/ecc-installed.json` + at least one
|
|
14
|
+
* cached/materialized agent).
|
|
15
|
+
* - a source that maps to an npm package — satisfied when that package
|
|
16
|
+
* (or its `.bin` shim) is present in the project's `node_modules`.
|
|
17
|
+
*
|
|
18
|
+
* Every probe is fail-soft: a throw degrades to "not installed" so the
|
|
19
|
+
* catalog never breaks because a probe failed.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync } from 'node:fs';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { hasMaterializedEccAgents, listCachedAgents } from 'peaks-loop-mut';
|
|
24
|
+
import { seedCapabilityItems } from './capability-seed-items.js';
|
|
25
|
+
const ECC_SOURCE_ID = 'everything-claude-code';
|
|
26
|
+
/**
|
|
27
|
+
* `sourceId` → npm package whose presence in `node_modules` satisfies the
|
|
28
|
+
* item's acquisition. Deliberately explicit: the catalog mixes repos,
|
|
29
|
+
* skills packages, MCP servers, and docs, and only a small subset is
|
|
30
|
+
* actually an npm dependency we can probe. Adding an entry here is the
|
|
31
|
+
* only change needed to make that source's items detectable.
|
|
32
|
+
*/
|
|
33
|
+
const NPM_PACKAGE_BY_SOURCE = {
|
|
34
|
+
codegraph: 'codegraph'
|
|
35
|
+
};
|
|
36
|
+
function packagePresent(nodeModulesDir, packageName) {
|
|
37
|
+
try {
|
|
38
|
+
return (existsSync(join(nodeModulesDir, packageName)) ||
|
|
39
|
+
existsSync(join(nodeModulesDir, '.bin', packageName)));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function eccCacheAvailable() {
|
|
46
|
+
try {
|
|
47
|
+
// Cached agents imply the manifest + sha dir exist; the materialized
|
|
48
|
+
// plugin-free copy is checked independently so a hand-populated
|
|
49
|
+
// `~/.peaks/agents/ecc/` still counts as satisfied.
|
|
50
|
+
return listCachedAgents().length > 0 || hasMaterializedEccAgents();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Return the capabilityIds whose acquisition is already satisfied.
|
|
58
|
+
* Sorted + de-duplicated so the JSON envelope is stable across runs.
|
|
59
|
+
*/
|
|
60
|
+
export function detectInstalledCapabilityIds(probe) {
|
|
61
|
+
const nodeModulesDir = probe.nodeModulesDir ?? join(probe.projectRoot, 'node_modules');
|
|
62
|
+
const eccAvailable = probe.eccCacheAvailable ?? eccCacheAvailable();
|
|
63
|
+
const installed = new Set();
|
|
64
|
+
for (const item of seedCapabilityItems) {
|
|
65
|
+
if (item.sourceId === ECC_SOURCE_ID) {
|
|
66
|
+
if (eccAvailable)
|
|
67
|
+
installed.add(item.capabilityId);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const packageName = NPM_PACKAGE_BY_SOURCE[item.sourceId];
|
|
71
|
+
if (packageName !== undefined && packagePresent(nodeModulesDir, packageName)) {
|
|
72
|
+
installed.add(item.capabilityId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [...installed].sort();
|
|
76
|
+
}
|
|
@@ -26,7 +26,7 @@ const PARENT_COMMANDS = new Set([
|
|
|
26
26
|
'qa', 'sc', 'txt', 'code-review', 'rd', 'sh', 'config', 'audit', 'codegraph',
|
|
27
27
|
'context', 'agent', 'capability', 'classify', 'gstack', 'gate',
|
|
28
28
|
'hook', 'hooks', 'log', 'loop', 'memory', 'perf', 'playwright', 'preferences',
|
|
29
|
-
'project', 'retrospective', 'slice', 'statusline', '
|
|
29
|
+
'project', 'retrospective', 'slice', 'statusline', 'workflow',
|
|
30
30
|
'migrate', 'mcp', 'doctor', 'help'
|
|
31
31
|
]);
|
|
32
32
|
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'coverage']);
|
|
@@ -66,6 +66,27 @@ export interface GcPresenceLeasesInput {
|
|
|
66
66
|
readonly trigger?: 'manual' | 'workspace-init' | 'presence-set';
|
|
67
67
|
}
|
|
68
68
|
export declare function setPresenceLease(input: SetPresenceLeaseInput): SetPresenceLeaseResult;
|
|
69
|
+
/**
|
|
70
|
+
* Slice 2026-09-09-mode-consolidation (Slice B): stamp `mode` onto every
|
|
71
|
+
* in-flight lease of the bound session, in place.
|
|
72
|
+
*
|
|
73
|
+
* Used by the 24h auto-engage path (T3/T4, `peaks code run --24h`,
|
|
74
|
+
* `peaks session 24h-mode transition --state 24H_ACTIVE`) to record the
|
|
75
|
+
* autonomy level without re-creating the lease (a fresh `setPresenceLease`
|
|
76
|
+
* would reset `startedAt` / `status`). `lastHeartbeat` is intentionally
|
|
77
|
+
* NOT touched — stamping a mode is not a liveness signal.
|
|
78
|
+
*
|
|
79
|
+
* Returns the number of leases updated; 0 when there is nothing in flight.
|
|
80
|
+
* Callers are responsible for the "only `24h` may be auto-set" guard.
|
|
81
|
+
*/
|
|
82
|
+
export declare function stampPresenceLeaseMode(input: {
|
|
83
|
+
readonly projectRoot: string;
|
|
84
|
+
readonly sessionId: string;
|
|
85
|
+
readonly mode: string;
|
|
86
|
+
}): {
|
|
87
|
+
updated: number;
|
|
88
|
+
leasePaths: string[];
|
|
89
|
+
};
|
|
69
90
|
export declare function readPresenceLease(input: ReadPresenceLeaseInput): PresenceProjection;
|
|
70
91
|
export declare function markPresenceLost(input: MarkPresenceLostInput): SkillPresenceLease;
|
|
71
92
|
export declare function gcStalePresenceLeases(input: GcPresenceLeasesInput): GcResult;
|
|
@@ -175,6 +175,32 @@ export function setPresenceLease(input) {
|
|
|
175
175
|
gc,
|
|
176
176
|
};
|
|
177
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Slice 2026-09-09-mode-consolidation (Slice B): stamp `mode` onto every
|
|
180
|
+
* in-flight lease of the bound session, in place.
|
|
181
|
+
*
|
|
182
|
+
* Used by the 24h auto-engage path (T3/T4, `peaks code run --24h`,
|
|
183
|
+
* `peaks session 24h-mode transition --state 24H_ACTIVE`) to record the
|
|
184
|
+
* autonomy level without re-creating the lease (a fresh `setPresenceLease`
|
|
185
|
+
* would reset `startedAt` / `status`). `lastHeartbeat` is intentionally
|
|
186
|
+
* NOT touched — stamping a mode is not a liveness signal.
|
|
187
|
+
*
|
|
188
|
+
* Returns the number of leases updated; 0 when there is nothing in flight.
|
|
189
|
+
* Callers are responsible for the "only `24h` may be auto-set" guard.
|
|
190
|
+
*/
|
|
191
|
+
export function stampPresenceLeaseMode(input) {
|
|
192
|
+
const sessionId = validateSessionId(input.sessionId);
|
|
193
|
+
const leasePaths = [];
|
|
194
|
+
for (const lease of listPresenceLeases(input.projectRoot, sessionId)) {
|
|
195
|
+
if (lease.status !== 'preparing' && lease.status !== 'running')
|
|
196
|
+
continue;
|
|
197
|
+
const path = leaseFilePath(input.projectRoot, sessionId, lease.callerId, lease.workflowId);
|
|
198
|
+
const next = { ...lease, mode: input.mode };
|
|
199
|
+
writeAtomic(path, JSON.stringify(next, null, 2));
|
|
200
|
+
leasePaths.push(path);
|
|
201
|
+
}
|
|
202
|
+
return { updated: leasePaths.length, leasePaths };
|
|
203
|
+
}
|
|
178
204
|
export function readPresenceLease(input) {
|
|
179
205
|
const sessionId = validateSessionId(input.sessionId ?? null);
|
|
180
206
|
const callerId = validateCallerId(input.callerId);
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
export type SkillPresenceMode = 'full-auto' | 'assisted' | '
|
|
1
|
+
export type SkillPresenceMode = 'full-auto' | 'assisted' | 'strict' | '24h';
|
|
2
2
|
export declare const VALID_SKILL_PRESENCE_MODES: ReadonlyArray<SkillPresenceMode>;
|
|
3
|
+
export declare const LEGACY_SKILL_PRESENCE_MODE_ALIASES: Readonly<Record<string, SkillPresenceMode>>;
|
|
3
4
|
export declare function isSkillPresenceMode(value: string): value is SkillPresenceMode;
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a raw on-disk / CLI mode token to a current
|
|
7
|
+
* `SkillPresenceMode`. Current values pass through; the legacy
|
|
8
|
+
* `'swarm'` alias maps to `'full-auto'`; anything else returns
|
|
9
|
+
* `undefined` so the caller can decide (drop the field, not the
|
|
10
|
+
* presence). Never throws.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeSkillPresenceMode(value: string | undefined | null): SkillPresenceMode | undefined;
|
|
4
13
|
export type SkillPresence = {
|
|
5
14
|
skill: string;
|
|
6
15
|
mode?: SkillPresenceMode;
|
|
@@ -20,12 +20,33 @@ void null;
|
|
|
20
20
|
export const VALID_SKILL_PRESENCE_MODES = [
|
|
21
21
|
'full-auto',
|
|
22
22
|
'assisted',
|
|
23
|
-
'
|
|
24
|
-
'
|
|
23
|
+
'strict',
|
|
24
|
+
'24h'
|
|
25
25
|
];
|
|
26
|
+
// Migration note (slice 2026-09-09-mode-consolidation): `swarm` was REMOVED
|
|
27
|
+
// as a mode — parallel fan-out is now the default execution strategy in
|
|
28
|
+
// every mode. A legacy on-disk `mode: 'swarm'` is normalized on read to
|
|
29
|
+
// `'full-auto'` (never crash, never drop the presence).
|
|
30
|
+
export const LEGACY_SKILL_PRESENCE_MODE_ALIASES = {
|
|
31
|
+
swarm: 'full-auto'
|
|
32
|
+
};
|
|
26
33
|
export function isSkillPresenceMode(value) {
|
|
27
34
|
return VALID_SKILL_PRESENCE_MODES.includes(value);
|
|
28
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Normalize a raw on-disk / CLI mode token to a current
|
|
38
|
+
* `SkillPresenceMode`. Current values pass through; the legacy
|
|
39
|
+
* `'swarm'` alias maps to `'full-auto'`; anything else returns
|
|
40
|
+
* `undefined` so the caller can decide (drop the field, not the
|
|
41
|
+
* presence). Never throws.
|
|
42
|
+
*/
|
|
43
|
+
export function normalizeSkillPresenceMode(value) {
|
|
44
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (isSkillPresenceMode(value))
|
|
47
|
+
return value;
|
|
48
|
+
return LEGACY_SKILL_PRESENCE_MODE_ALIASES[value];
|
|
49
|
+
}
|
|
29
50
|
/**
|
|
30
51
|
* The current outer session id, exposed to Bash tool calls via the
|
|
31
52
|
* `PEAKS_OUTER_SESSION_ID` environment variable. Stamping it onto the
|
|
@@ -118,8 +139,15 @@ function readSkillPresenceFromLease(projectRootOverride) {
|
|
|
118
139
|
// The lease carries `callerId` (the adapter / harness session id),
|
|
119
140
|
// not the legacy `outerSessionId` field. Project it for back-compat
|
|
120
141
|
// with `clearStalePresenceOnRotation`'s outer-mismatch check.
|
|
142
|
+
//
|
|
143
|
+
// Slice 2026-09-09-mode-consolidation: `mode` is projected too (it was
|
|
144
|
+
// silently dropped before, which made `getSkillPresence().mode` always
|
|
145
|
+
// undefined and left every `presence.mode` consumer dead). Normalize on
|
|
146
|
+
// read so a legacy `'swarm'` lease surfaces as `'full-auto'`.
|
|
147
|
+
const leaseMode = normalizeSkillPresenceMode(lease.mode);
|
|
121
148
|
return {
|
|
122
149
|
skill: lease.skill,
|
|
150
|
+
...(leaseMode !== undefined ? { mode: leaseMode } : {}),
|
|
123
151
|
sessionId,
|
|
124
152
|
outerSessionId: lease.callerId,
|
|
125
153
|
setAt: lease.startedAt,
|
|
@@ -208,7 +236,7 @@ function getPreviousOuterSessionId(projectRootOverride) {
|
|
|
208
236
|
* `peakSessionId`, OR via `ensureSession` for the first-time case).
|
|
209
237
|
*/
|
|
210
238
|
export function setSkillPresenceForCaller(projectRootOverride, callerId, peakSessionId, skill, mode, gate) {
|
|
211
|
-
const validatedMode =
|
|
239
|
+
const validatedMode = normalizeSkillPresenceMode(mode);
|
|
212
240
|
const now = new Date().toISOString();
|
|
213
241
|
const presence = {
|
|
214
242
|
skill,
|
|
@@ -257,7 +285,7 @@ export function setSkillPresence(skill, mode, gate, projectRootOverride) {
|
|
|
257
285
|
// the old shape don't break) but we surface a warning to the
|
|
258
286
|
// CLI boundary via the `outerSessionMismatch` field.
|
|
259
287
|
// 3. delegates the canonical write to `setPresenceLease`.
|
|
260
|
-
const validatedMode =
|
|
288
|
+
const validatedMode = normalizeSkillPresenceMode(mode);
|
|
261
289
|
const sessionId = getCurrentSessionId(projectRootOverride);
|
|
262
290
|
const outerSessionId = getCurrentOuterSessionId();
|
|
263
291
|
const previousOuterSessionId = getPreviousOuterSessionId(projectRootOverride);
|
|
@@ -30,6 +30,7 @@ import { readActiveDispatchIndex } from '../dispatch/dispatch-record-writer.js';
|
|
|
30
30
|
* picks the most recent in-flight lease across all callers; this is the
|
|
31
31
|
* documented back-compat path.
|
|
32
32
|
*/
|
|
33
|
+
import { normalizeSkillPresenceMode } from './skill-presence-service.js';
|
|
33
34
|
const STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
34
35
|
/**
|
|
35
36
|
* Slice rid-statusline-24h-overlay (2026-08-10): name-distinct from
|
|
@@ -245,12 +246,14 @@ function readPresenceReadOnly(projectRoot, callerId) {
|
|
|
245
246
|
}
|
|
246
247
|
// Leases on disk may carry an optional `mode` field (the lease constructor
|
|
247
248
|
// spreads `input.mode` when present); the typed `SkillPresenceLease` does
|
|
248
|
-
// not declare it, so widen the read shape here.
|
|
249
|
-
|
|
249
|
+
// not declare it, so widen the read shape here. Normalized on read: a
|
|
250
|
+
// legacy `'swarm'` lease renders as `'full-auto'` (slice
|
|
251
|
+
// 2026-09-09-mode-consolidation).
|
|
252
|
+
const latestMode = normalizeSkillPresenceMode(latest.mode);
|
|
250
253
|
return {
|
|
251
254
|
presence: {
|
|
252
255
|
skill: latest.skill,
|
|
253
|
-
...(
|
|
256
|
+
...(latestMode !== undefined ? { mode: latestMode } : {}),
|
|
254
257
|
setAt: latest.startedAt,
|
|
255
258
|
},
|
|
256
259
|
invalid: false,
|
|
@@ -65,7 +65,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
|
|
|
65
65
|
const wantFile = granularity === 'file' || granularity === 'both' || granularity === 'auto';
|
|
66
66
|
const passes = [];
|
|
67
67
|
let codegraph = zeroCodegraph();
|
|
68
|
-
let understandAnything = zeroUnderstand();
|
|
69
68
|
let pass1WorkUnits = [];
|
|
70
69
|
if (wantService) {
|
|
71
70
|
const result1 = await decomposeSlices(rid, prdMarkdown, projectRoot, opts);
|
|
@@ -77,7 +76,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
|
|
|
77
76
|
internalEdges: v1EdgesToV2(result1.dependencyDAG.edges)
|
|
78
77
|
});
|
|
79
78
|
codegraph = result1.codegraph;
|
|
80
|
-
understandAnything = result1.understandAnything;
|
|
81
79
|
}
|
|
82
80
|
if (wantFile) {
|
|
83
81
|
if (pass1WorkUnits.length > 0) {
|
|
@@ -115,7 +113,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
|
|
|
115
113
|
internalEdges: v1EdgesToV2(result.dependencyDAG.edges)
|
|
116
114
|
});
|
|
117
115
|
codegraph = result.codegraph;
|
|
118
|
-
understandAnything = result.understandAnything;
|
|
119
116
|
}
|
|
120
117
|
}
|
|
121
118
|
let crossPassEdges = [];
|
|
@@ -136,7 +133,6 @@ export async function decompose(rid, prdMarkdown, projectRoot, opts = {}) {
|
|
|
136
133
|
crossPassEdges,
|
|
137
134
|
llmArbitrations,
|
|
138
135
|
codegraph,
|
|
139
|
-
understandAnything,
|
|
140
136
|
partial: false
|
|
141
137
|
};
|
|
142
138
|
}
|
|
@@ -214,12 +210,3 @@ function zeroCodegraph() {
|
|
|
214
210
|
note: 'no decomposition run'
|
|
215
211
|
};
|
|
216
212
|
}
|
|
217
|
-
function zeroUnderstand() {
|
|
218
|
-
return {
|
|
219
|
-
kgNodes: 0,
|
|
220
|
-
kgEdges: 0,
|
|
221
|
-
available: false,
|
|
222
|
-
fallback: 'structural-only',
|
|
223
|
-
note: 'no decomposition run'
|
|
224
|
-
};
|
|
225
|
-
}
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* the 800 LOC cap (mechanical verbatim move). `buildDependencyEdges` is
|
|
5
5
|
* the only exported entry point; the rest are internal helpers.
|
|
6
6
|
*/
|
|
7
|
-
import type { DependencyEdge, ImportEdge,
|
|
8
|
-
export declare function buildDependencyEdges(wus: readonly WorkUnit[], importEdges: readonly ImportEdge[],
|
|
7
|
+
import type { DependencyEdge, ImportEdge, WorkUnit } from './slice-decompose-types.js';
|
|
8
|
+
export declare function buildDependencyEdges(wus: readonly WorkUnit[], importEdges: readonly ImportEdge[], projectRoot: string): DependencyEdge[];
|
|
@@ -55,38 +55,10 @@ function collectImportEdges(importEdges, fileToWu, projectRoot, collector) {
|
|
|
55
55
|
pushIfNew(collector, edge);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
function
|
|
59
|
-
for (const e of kg.edges) {
|
|
60
|
-
if (e.type !== 'contains_flow' && e.type !== 'flow_step')
|
|
61
|
-
continue;
|
|
62
|
-
const fromNode = kg.nodes.find((n) => n.id === e.source);
|
|
63
|
-
const toNode = kg.nodes.find((n) => n.id === e.target);
|
|
64
|
-
if (!fromNode?.filePath || !toNode?.filePath)
|
|
65
|
-
continue;
|
|
66
|
-
const fromWu = fileToWu.get(fromNode.filePath);
|
|
67
|
-
const toWu = fileToWu.get(toNode.filePath);
|
|
68
|
-
if (!fromWu || !toWu || fromWu === toWu)
|
|
69
|
-
continue;
|
|
70
|
-
const weight = e.type === 'flow_step' ? 0.05 : 0.1;
|
|
71
|
-
const edge = {
|
|
72
|
-
from: fromWu,
|
|
73
|
-
to: toWu,
|
|
74
|
-
kind: e.type,
|
|
75
|
-
weight,
|
|
76
|
-
evidence: `understand-anything: ${e.type} ${e.source}->${e.target}`,
|
|
77
|
-
isSemantic: true,
|
|
78
|
-
confidence: 'semantic'
|
|
79
|
-
};
|
|
80
|
-
pushIfNew(collector, edge);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
export function buildDependencyEdges(wus, importEdges, kg, projectRoot) {
|
|
58
|
+
export function buildDependencyEdges(wus, importEdges, projectRoot) {
|
|
84
59
|
const fileToWu = indexFilesToWorkUnits(wus);
|
|
85
60
|
const collector = { edges: [], seen: new Set() };
|
|
86
61
|
collectImportEdges(importEdges, fileToWu, projectRoot, collector);
|
|
87
|
-
if (kg !== null) {
|
|
88
|
-
collectSemanticEdges(kg, fileToWu, collector);
|
|
89
|
-
}
|
|
90
62
|
return collector.edges;
|
|
91
63
|
}
|
|
92
64
|
function resolveRelativeImport(projectRoot, sourceFile, evidence) {
|
|
@@ -7,17 +7,15 @@
|
|
|
7
7
|
* does not inject fakes via `DecomposeOptions`. The 6-stage pure
|
|
8
8
|
* algorithm itself lives in `slice-decompose-service.ts`.
|
|
9
9
|
*
|
|
10
|
-
* Public surface (
|
|
10
|
+
* Public surface (2 exported factories):
|
|
11
11
|
*
|
|
12
12
|
* defaultCodegraphRunner() -- spawns `npx codegraph <cmd>`
|
|
13
|
-
* defaultUnderstandRunner() -- reads .understand-anything/knowledge-graph.json
|
|
14
13
|
* defaultImportEdgeRunner() -- reads source files for import statements
|
|
15
14
|
*
|
|
16
15
|
* v2.18.3 file-split: this module is the extracted sub-tree of the
|
|
17
16
|
* pre-split `slice-decompose-service.ts`. Function signatures are
|
|
18
17
|
* unchanged (verbatim move).
|
|
19
18
|
*/
|
|
20
|
-
import type { CodegraphRunner, ImportEdgeRunner
|
|
19
|
+
import type { CodegraphRunner, ImportEdgeRunner } from './slice-decompose-types.js';
|
|
21
20
|
export declare function defaultCodegraphRunner(): CodegraphRunner;
|
|
22
|
-
export declare function defaultUnderstandRunner(): UnderstandRunner;
|
|
23
21
|
export declare function defaultImportEdgeRunner(): ImportEdgeRunner;
|
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
* does not inject fakes via `DecomposeOptions`. The 6-stage pure
|
|
8
8
|
* algorithm itself lives in `slice-decompose-service.ts`.
|
|
9
9
|
*
|
|
10
|
-
* Public surface (
|
|
10
|
+
* Public surface (2 exported factories):
|
|
11
11
|
*
|
|
12
12
|
* defaultCodegraphRunner() -- spawns `npx codegraph <cmd>`
|
|
13
|
-
* defaultUnderstandRunner() -- reads .understand-anything/knowledge-graph.json
|
|
14
13
|
* defaultImportEdgeRunner() -- reads source files for import statements
|
|
15
14
|
*
|
|
16
15
|
* v2.18.3 file-split: this module is the extracted sub-tree of the
|
|
@@ -121,27 +120,6 @@ function runCodegraph(args, projectRoot) {
|
|
|
121
120
|
throw error;
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
|
-
export function defaultUnderstandRunner() {
|
|
125
|
-
return {
|
|
126
|
-
async read(projectRoot) {
|
|
127
|
-
const kgPath = join(projectRoot, '.understand-anything', 'knowledge-graph.json');
|
|
128
|
-
if (!existsSync(kgPath))
|
|
129
|
-
return null;
|
|
130
|
-
try {
|
|
131
|
-
const raw = readFileSync(kgPath, 'utf8');
|
|
132
|
-
const parsed = JSON.parse(raw);
|
|
133
|
-
return {
|
|
134
|
-
nodes: parsed.nodes ?? [],
|
|
135
|
-
edges: parsed.edges ?? [],
|
|
136
|
-
layers: parsed.layers ?? []
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
123
|
export function defaultImportEdgeRunner() {
|
|
146
124
|
return {
|
|
147
125
|
async importsOf(projectRoot, files) {
|
|
@@ -12,13 +12,12 @@
|
|
|
12
12
|
* Default runners (CLI-mode, real shell-outs):
|
|
13
13
|
*
|
|
14
14
|
* defaultCodegraphRunner() -- spawns `npx codegraph <cmd>`
|
|
15
|
-
* defaultUnderstandRunner() -- reads .understand-anything/knowledge-graph.json
|
|
16
15
|
* defaultImportEdgeRunner() -- reads source files for import statements
|
|
17
16
|
*
|
|
18
|
-
* Tests inject fakes via the `codegraphRunner
|
|
19
|
-
*
|
|
17
|
+
* Tests inject fakes via the `codegraphRunner` and `importEdgeRunner`
|
|
18
|
+
* fields of `DecomposeOptions`.
|
|
20
19
|
*/
|
|
21
20
|
import type { DecomposeOptions, DecompositionResult } from './slice-decompose-types.js';
|
|
22
21
|
export type { DecomposeOptions };
|
|
23
22
|
export declare function decomposeSlices(rid: string, prdMarkdown: string, projectRoot: string, options?: DecomposeOptions): Promise<DecompositionResult>;
|
|
24
|
-
export { defaultCodegraphRunner,
|
|
23
|
+
export { defaultCodegraphRunner, defaultImportEdgeRunner } from './slice-decompose-runners.js';
|
|
@@ -12,11 +12,10 @@
|
|
|
12
12
|
* Default runners (CLI-mode, real shell-outs):
|
|
13
13
|
*
|
|
14
14
|
* defaultCodegraphRunner() -- spawns `npx codegraph <cmd>`
|
|
15
|
-
* defaultUnderstandRunner() -- reads .understand-anything/knowledge-graph.json
|
|
16
15
|
* defaultImportEdgeRunner() -- reads source files for import statements
|
|
17
16
|
*
|
|
18
|
-
* Tests inject fakes via the `codegraphRunner
|
|
19
|
-
*
|
|
17
|
+
* Tests inject fakes via the `codegraphRunner` and `importEdgeRunner`
|
|
18
|
+
* fields of `DecomposeOptions`.
|
|
20
19
|
*/
|
|
21
20
|
import { basename } from 'node:path';
|
|
22
21
|
import { calibrate } from './calibration-store.js';
|
|
@@ -48,8 +47,6 @@ function createInitialState(rid, prdMarkdown, projectRoot, runners) {
|
|
|
48
47
|
cgStatus: null,
|
|
49
48
|
workUnits: [],
|
|
50
49
|
depEdges: [],
|
|
51
|
-
kg: null,
|
|
52
|
-
understandAvailable: false,
|
|
53
50
|
codegraphAffectedCrossFile: false,
|
|
54
51
|
scc: null,
|
|
55
52
|
criticalPath: null,
|
|
@@ -84,7 +81,6 @@ async function runStage(state, stage) {
|
|
|
84
81
|
function resolveRunners(options) {
|
|
85
82
|
return {
|
|
86
83
|
cg: options.codegraphRunner ?? defaultCodegraphRunner(),
|
|
87
|
-
ur: options.understandRunner ?? defaultUnderstandRunner(),
|
|
88
84
|
ier: options.importEdgeRunner ?? defaultImportEdgeRunner()
|
|
89
85
|
};
|
|
90
86
|
}
|
|
@@ -172,15 +168,13 @@ function matchAcToHit(ac, hits) {
|
|
|
172
168
|
// Stage 2: dependency DAG
|
|
173
169
|
// =====================================================================
|
|
174
170
|
async function buildDepDagStage(state) {
|
|
175
|
-
const kg = await state.runners.ur.read(state.projectRoot);
|
|
176
|
-
const understandAvailable = kg !== null;
|
|
177
171
|
const fileSet = await collectFileSet(state.workUnits, state.runners.ier, state.projectRoot);
|
|
178
172
|
const allFiles = Array.from(fileSet);
|
|
179
173
|
const codegraphAffectedCrossFile = await probeCrossFileAffected(allFiles, state.runners.cg, state.projectRoot);
|
|
180
174
|
const workUnits = applyImplicitWuFallback(state.workUnits, allFiles);
|
|
181
175
|
const importEdges = await state.runners.ier.importsOf(state.projectRoot, allFiles);
|
|
182
|
-
const depEdges = buildDependencyEdges(workUnits, importEdges,
|
|
183
|
-
return { ...state,
|
|
176
|
+
const depEdges = buildDependencyEdges(workUnits, importEdges, state.projectRoot);
|
|
177
|
+
return { ...state, codegraphAffectedCrossFile, workUnits, depEdges };
|
|
184
178
|
}
|
|
185
179
|
async function collectFileSet(workUnits, ier, projectRoot) {
|
|
186
180
|
const explicitFiles = workUnits.flatMap((w) => w.files);
|
|
@@ -362,10 +356,10 @@ function backtrackPath(endNode, prev) {
|
|
|
362
356
|
function minCutStage(state) {
|
|
363
357
|
if (state.criticalPath === null)
|
|
364
358
|
return state;
|
|
365
|
-
const minCut = findMinCut(state.workUnits, state.depEdges, state.criticalPath
|
|
359
|
+
const minCut = findMinCut(state.workUnits, state.depEdges, state.criticalPath);
|
|
366
360
|
return { ...state, minCut };
|
|
367
361
|
}
|
|
368
|
-
function findMinCut(_wus, edges, criticalPath
|
|
362
|
+
function findMinCut(_wus, edges, criticalPath) {
|
|
369
363
|
const cpSet = new Set(criticalPath.nodes);
|
|
370
364
|
const nonCpEdges = edges.filter((e) => !cpSet.has(e.from) || !cpSet.has(e.to));
|
|
371
365
|
const sorted = [...nonCpEdges].sort((a, b) => a.weight - b.weight);
|
|
@@ -405,7 +399,7 @@ function partitionEstimateEmitStage(state) {
|
|
|
405
399
|
...b,
|
|
406
400
|
slices: b.slices.map((s) => ({
|
|
407
401
|
...s,
|
|
408
|
-
estimate: estimateSlice(s
|
|
402
|
+
estimate: estimateSlice(s)
|
|
409
403
|
}))
|
|
410
404
|
}));
|
|
411
405
|
const result = buildDecompositionResult(state, batchesWithEstimates);
|
|
@@ -416,12 +410,10 @@ function buildDecompositionResult(state, batchesWithEstimates) {
|
|
|
416
410
|
const totalSlices = batchesWithEstimates.reduce((sum, b) => sum + b.slices.length, 0);
|
|
417
411
|
const pickHint = buildPickHint(totalSlices);
|
|
418
412
|
const codegraphEnvelope = buildCodegraphEnvelope(state);
|
|
419
|
-
const understandEnvelope = buildUnderstandEnvelope(state);
|
|
420
413
|
return {
|
|
421
414
|
rid: state.rid,
|
|
422
415
|
generatedAt: new Date().toISOString(),
|
|
423
416
|
codegraph: codegraphEnvelope,
|
|
424
|
-
understandAnything: understandEnvelope,
|
|
425
417
|
workUnits: state.workUnits,
|
|
426
418
|
dependencyDAG: { edges: state.depEdges },
|
|
427
419
|
sccAnalysis: state.scc,
|
|
@@ -460,18 +452,7 @@ function buildCodegraphEnvelope(state) {
|
|
|
460
452
|
: 'codegraph.affected returned 0 cross-file dependents (v0.7.10 limitation); used real import edges'
|
|
461
453
|
};
|
|
462
454
|
}
|
|
463
|
-
function
|
|
464
|
-
return {
|
|
465
|
-
kgNodes: state.kg?.nodes.length ?? 0,
|
|
466
|
-
kgEdges: state.kg?.edges.length ?? 0,
|
|
467
|
-
available: state.understandAvailable,
|
|
468
|
-
fallback: state.understandAvailable ? 'semantic' : 'structural-only',
|
|
469
|
-
note: state.understandAvailable
|
|
470
|
-
? 'read from .understand-anything/knowledge-graph.json'
|
|
471
|
-
: '.understand-anything/knowledge-graph.json not found; algorithm falls back to structural cuts'
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
function estimateSlice(slice, _kg) {
|
|
455
|
+
function estimateSlice(slice) {
|
|
475
456
|
const sampleSize = 0;
|
|
476
457
|
const complexitySum = 0;
|
|
477
458
|
return calibrate(complexitySum, slice.estimate.testCount, slice.estimate.locSum, sampleSize);
|
|
@@ -569,5 +550,5 @@ function wuToSlice(wu) {
|
|
|
569
550
|
// `slice-decompose-runners.ts` module — see v2.18.3 file-split for
|
|
570
551
|
// the rationale. Function signatures and behaviour are unchanged
|
|
571
552
|
// (verbatim move).
|
|
572
|
-
import { defaultCodegraphRunner,
|
|
573
|
-
export { defaultCodegraphRunner,
|
|
553
|
+
import { defaultCodegraphRunner, defaultImportEdgeRunner } from './slice-decompose-runners.js';
|
|
554
|
+
export { defaultCodegraphRunner, defaultImportEdgeRunner } from './slice-decompose-runners.js';
|