peaks-loop 4.0.46 → 4.0.47
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 +30 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/dist/cli/commands/code-runtime-commands.d.ts +22 -0
- package/dist/cli/commands/code-runtime-commands.js +99 -16
- package/dist/cli/commands/compact-command.js +129 -1
- package/dist/cli/commands/container-commands.js +3 -3
- package/dist/cli/commands/core/skill-command.js +45 -10
- package/dist/cli/commands/cron-commands.js +2 -1
- package/dist/cli/commands/e2e-verify.js +3 -3
- package/dist/cli/commands/governance-classify-contract-commands.js +1 -0
- package/dist/cli/commands/hooks-commands.js +10 -1
- package/dist/cli/commands/loop-commands.js +1 -0
- package/dist/cli/commands/playwright-commands.js +2 -1
- package/dist/cli/commands/reinject-command.d.ts +72 -0
- package/dist/cli/commands/reinject-command.js +174 -0
- package/dist/cli/commands/request-commands.js +6 -3
- package/dist/cli/commands/shadcn-commands.js +1 -0
- package/dist/cli/commands/test-commands.js +2 -1
- package/dist/cli/commands/vm-commands.js +7 -7
- package/dist/cli/commands/workspace/init-command.js +24 -2
- package/dist/cli/commands/worktree-lease-commands.js +4 -4
- package/dist/cli/index.js +49 -2
- package/dist/cli/program.js +5 -0
- package/dist/hooks/pre-tool-use-sub-agent.js +1 -1
- package/dist/services/adapter/adapter-registry.js +1 -1
- package/dist/services/artifacts/artifact-service.js +1 -1
- package/dist/services/capability-guard-runner/contracts/J01.js +2 -1
- package/dist/services/capability-guard-runner/contracts/J02.js +3 -3
- package/dist/services/capability-guard-runner/contracts/J04.js +4 -2
- package/dist/services/capability-guard-runner/contracts/J07.js +2 -1
- package/dist/services/code/auto-compact-lifecycle.d.ts +11 -1
- package/dist/services/code/auto-compact-lifecycle.js +11 -4
- package/dist/services/code/auto-compact-orchestrator.d.ts +53 -9
- package/dist/services/code/auto-compact-orchestrator.js +153 -32
- package/dist/services/code/orchestrator-can-do.d.ts +4 -2
- package/dist/services/code/orchestrator-can-do.js +37 -5
- package/dist/services/codegraph/codegraph-exclude-reconciler.js +2 -1
- package/dist/services/codegraph/codegraph-process-runner.js +3 -2
- package/dist/services/compact/request-transition-hook.js +5 -2
- package/dist/services/compact-history/compact-history-service.d.ts +61 -0
- package/dist/services/compact-history/compact-history-service.js +49 -0
- package/dist/services/config/config-safety.d.ts +52 -0
- package/dist/services/config/config-safety.js +75 -1
- package/dist/services/context/auto-compact-dispatcher.d.ts +7 -37
- package/dist/services/context/auto-compact-dispatcher.js +113 -40
- package/dist/services/context/auto-compact-reader.d.ts +68 -28
- package/dist/services/context/auto-compact-reader.js +155 -1
- package/dist/services/context/auto-compact-types.d.ts +89 -12
- package/dist/services/context/auto-compact-types.js +16 -32
- package/dist/services/context/harness-window-config.d.ts +412 -0
- package/dist/services/context/harness-window-config.js +607 -0
- package/dist/services/context/main-session-monitor.d.ts +27 -0
- package/dist/services/context/main-session-monitor.js +32 -1
- package/dist/services/context/post-compact-reinjection.d.ts +221 -0
- package/dist/services/context/post-compact-reinjection.js +491 -0
- package/dist/services/dispatch/merge-back-runner.js +5 -5
- package/dist/services/dispatch/service-shutdown.js +3 -3
- package/dist/services/doc/doc-generator.js +2 -1
- package/dist/services/env/shell-probe.js +1 -1
- package/dist/services/fuzzy-matching/fzf-pick-service.js +2 -0
- package/dist/services/hooks/auto-compact-hook-install.d.ts +10 -2
- package/dist/services/hooks/auto-compact-hook-install.js +8 -0
- package/dist/services/ide/adapters/claude-code-adapter.d.ts +107 -3
- package/dist/services/ide/adapters/claude-code-adapter.js +154 -7
- package/dist/services/ide/ide-registry.d.ts +12 -0
- package/dist/services/ide/ide-registry.js +14 -0
- package/dist/services/ide/ide-types.d.ts +59 -0
- package/dist/services/lint/detect-eslint.js +2 -2
- package/dist/services/lint/eslint-runner.js +3 -1
- package/dist/services/loop/evaluator-dispatcher.js +2 -1
- package/dist/services/memory/project-memory-service/index/kind-dispatch.js +1 -1
- package/dist/services/memory/project-memory-service/store/paths.d.ts +9 -1
- package/dist/services/memory/project-memory-service/store/paths.js +15 -6
- package/dist/services/prd/best-practice-auto-trigger.js +1 -0
- package/dist/services/release/version-precheck-service.d.ts +2 -1
- package/dist/services/release/version-precheck-service.js +82 -12
- package/dist/services/runtime/vendor-adapter.d.ts +29 -4
- package/dist/services/runtime/vendors/claude-code.js +1 -1
- package/dist/services/runtime/vendors/codex.js +1 -1
- package/dist/services/runtime/vendors/copilot.js +1 -1
- package/dist/services/sc/sc-service.js +1 -1
- package/dist/services/scan/diff-scope-service.js +2 -2
- package/dist/services/scan/file-size-scan.js +2 -2
- package/dist/services/scan/orphan-service.js +2 -1
- package/dist/services/scan/type-sanity-service.js +2 -2
- package/dist/services/skillhub/tar-runtime.js +1 -0
- package/dist/services/skills/hooks-codegate-superpowers.d.ts +8 -0
- package/dist/services/skills/hooks-codegate-superpowers.js +40 -3
- package/dist/services/skills/hooks-settings-service.d.ts +12 -0
- package/dist/services/skills/hooks-settings-service.js +77 -10
- package/dist/services/skills/session-start-hook-constants.d.ts +41 -0
- package/dist/services/skills/session-start-hook-constants.js +41 -0
- package/dist/services/skills/skill-presence-service.js +9 -0
- package/dist/services/slice/slice-check-service.js +2 -1
- package/dist/services/slice/slice-decompose-runners.js +2 -1
- package/dist/services/upgrade/upgrade-service.js +1 -0
- package/dist/services/workflow/workflow-skip-service.js +2 -1
- package/dist/services/workspace/migrate-service.js +1 -1
- package/dist/services/workspace/workspace-claude-settings-materializer.js +51 -7
- package/dist/services/workspace/workspace-service.js +8 -0
- package/dist/services/worktree/host-worktree-reconciler.js +1 -0
- package/dist/services/worktree/long-path-cleanup.js +3 -2
- package/dist/shared/process.js +1 -1
- package/package.json +5 -5
- package/scripts/install-skills.mjs +1 -0
- package/scripts/watch.mjs +3 -1
- package/skills/bee/peaks-perf-audit/SKILL.md +1 -1
- package/skills/bee/peaks-prd/SKILL.md +1 -1
- package/skills/bee/peaks-qa/SKILL.md +2 -2
- package/skills/bee/peaks-rd/SKILL.md +2 -2
- package/skills/bee/peaks-reviewer/SKILL.md +1 -1
- package/skills/bee/peaks-sc/SKILL.md +1 -1
- package/skills/bee/peaks-security-audit/SKILL.md +1 -1
- package/skills/bee/peaks-txt/SKILL.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 +2 -2
- package/skills/peaks-code/references/sub-agent-dispatch.md +1 -1
- package/skills/peaks-content/SKILL.md +1 -1
- package/skills/peaks-doctor/SKILL.md +1 -1
- package/skills/peaks-final-review/SKILL.md +1 -1
- package/skills/peaks-ide/SKILL.md +1 -1
- package/skills/peaks-issue-fix-orchestrator/SKILL.md +1 -1
- package/skills/peaks-resume/SKILL.md +1 -1
- package/skills/peaks-slice-decompose/SKILL.md +1 -1
- package/skills/peaks-solo/SKILL.md +1 -1
- package/skills/peaks-sop/SKILL.md +1 -1
- package/skills/peaks-status/SKILL.md +1 -1
- package/skills/peaks-test/SKILL.md +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rid `2026-09-13-a2-post-compact-reinject` —
|
|
3
|
+
* `peaks session reinject --project <path>`.
|
|
4
|
+
*
|
|
5
|
+
* The transport for `buildPostCompactReinjectionCard`. A Claude Code
|
|
6
|
+
* `SessionStart` hook (installed by `peaks hooks install`, matcher `compact`)
|
|
7
|
+
* runs this and Claude Code adds its stdout to the context — which is the
|
|
8
|
+
* whole mechanism by which the engineering state comes back after a
|
|
9
|
+
* compaction. See `src/services/context/post-compact-reinjection.ts` for what
|
|
10
|
+
* the card says and why it has a byte budget.
|
|
11
|
+
*
|
|
12
|
+
* THE ONE THING THIS FILE HAS TO GET RIGHT
|
|
13
|
+
*
|
|
14
|
+
* stdout IS context here. Every other peaks command treats stdout as a
|
|
15
|
+
* channel for a human or a JSON consumer; this one's stdout is pasted in
|
|
16
|
+
* front of the model as though it were something peaks-loop meant to say.
|
|
17
|
+
* Two consequences, and both are the reason the code below looks the way it
|
|
18
|
+
* does:
|
|
19
|
+
*
|
|
20
|
+
* 1. The card is printed VERBATIM — no `printResult`, no envelope, no
|
|
21
|
+
* `next:` lines, no JSON on the card path. Anything else would be
|
|
22
|
+
* injected too, and a `{"ok":true,...}` blob in the context is noise the
|
|
23
|
+
* model then has to interpret.
|
|
24
|
+
* 2. A FAILURE PRINTS NOTHING. Not an error, not a warning, not a hint. An
|
|
25
|
+
* error message on stdout would be injected into the context as a fact,
|
|
26
|
+
* and a model that reads `REINJECT_FAILED: project root must be
|
|
27
|
+
* canonical` in its own context has been handed a false problem to solve
|
|
28
|
+
* mid-slice. So every failure path exits 0 with empty stdout, and the
|
|
29
|
+
* reason is reported only through `--json` (which the hook never passes).
|
|
30
|
+
* This is `failure-soft` for a SessionStart hook: the session must start
|
|
31
|
+
* whether or not there is any state to re-inject.
|
|
32
|
+
*
|
|
33
|
+
* `--json` exists for the human/diagnostic path — `peaks session reinject
|
|
34
|
+
* --json` is how you inspect what the hook would inject without reading a
|
|
35
|
+
* transcript. It is deliberately NOT what the hook runs.
|
|
36
|
+
*/
|
|
37
|
+
import type { Command } from 'commander';
|
|
38
|
+
import type { ProgramIO } from '../cli-helpers.js';
|
|
39
|
+
import { buildPostCompactReinjectionCard } from '../../services/context/post-compact-reinjection.js';
|
|
40
|
+
export type ReinjectOptions = {
|
|
41
|
+
project: string;
|
|
42
|
+
json?: boolean;
|
|
43
|
+
budgetBytes?: string;
|
|
44
|
+
};
|
|
45
|
+
/** Why no card was produced. Reported through `--json` only. */
|
|
46
|
+
export type ReinjectSkipReason = 'empty-project' | 'invalid-project-root' | 'unreadable-project-root';
|
|
47
|
+
export type ReinjectResult = {
|
|
48
|
+
readonly ok: true;
|
|
49
|
+
readonly card: ReturnType<typeof buildPostCompactReinjectionCard>;
|
|
50
|
+
} | {
|
|
51
|
+
readonly ok: false;
|
|
52
|
+
readonly reason: ReinjectSkipReason;
|
|
53
|
+
readonly message: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Pure-ish action body, exported so tests can drive it with inputs Commander
|
|
57
|
+
* would mangle (trailing NUL bytes, whitespace-only paths). Never throws and
|
|
58
|
+
* never writes to stdout itself — `runReinjectAction` is the IO edge.
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildReinjectResult(opts: ReinjectOptions): ReinjectResult;
|
|
61
|
+
/**
|
|
62
|
+
* The IO edge. Returns the process exit code so callers (CLI path or test
|
|
63
|
+
* path) apply it uniformly. NEVER calls `process.exit`.
|
|
64
|
+
*
|
|
65
|
+
* Both branches return 0 on the failure path — see this file's header. The
|
|
66
|
+
* only way a caller sees `exitCode: 1` is a bug in the `--json` serializer
|
|
67
|
+
* itself, and even that is caught.
|
|
68
|
+
*/
|
|
69
|
+
export declare function runReinjectAction(opts: ReinjectOptions, io: ProgramIO): {
|
|
70
|
+
exitCode: 0 | 1;
|
|
71
|
+
};
|
|
72
|
+
export declare function registerReinjectCommand(program: Command, io: ProgramIO): void;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rid `2026-09-13-a2-post-compact-reinject` —
|
|
3
|
+
* `peaks session reinject --project <path>`.
|
|
4
|
+
*
|
|
5
|
+
* The transport for `buildPostCompactReinjectionCard`. A Claude Code
|
|
6
|
+
* `SessionStart` hook (installed by `peaks hooks install`, matcher `compact`)
|
|
7
|
+
* runs this and Claude Code adds its stdout to the context — which is the
|
|
8
|
+
* whole mechanism by which the engineering state comes back after a
|
|
9
|
+
* compaction. See `src/services/context/post-compact-reinjection.ts` for what
|
|
10
|
+
* the card says and why it has a byte budget.
|
|
11
|
+
*
|
|
12
|
+
* THE ONE THING THIS FILE HAS TO GET RIGHT
|
|
13
|
+
*
|
|
14
|
+
* stdout IS context here. Every other peaks command treats stdout as a
|
|
15
|
+
* channel for a human or a JSON consumer; this one's stdout is pasted in
|
|
16
|
+
* front of the model as though it were something peaks-loop meant to say.
|
|
17
|
+
* Two consequences, and both are the reason the code below looks the way it
|
|
18
|
+
* does:
|
|
19
|
+
*
|
|
20
|
+
* 1. The card is printed VERBATIM — no `printResult`, no envelope, no
|
|
21
|
+
* `next:` lines, no JSON on the card path. Anything else would be
|
|
22
|
+
* injected too, and a `{"ok":true,...}` blob in the context is noise the
|
|
23
|
+
* model then has to interpret.
|
|
24
|
+
* 2. A FAILURE PRINTS NOTHING. Not an error, not a warning, not a hint. An
|
|
25
|
+
* error message on stdout would be injected into the context as a fact,
|
|
26
|
+
* and a model that reads `REINJECT_FAILED: project root must be
|
|
27
|
+
* canonical` in its own context has been handed a false problem to solve
|
|
28
|
+
* mid-slice. So every failure path exits 0 with empty stdout, and the
|
|
29
|
+
* reason is reported only through `--json` (which the hook never passes).
|
|
30
|
+
* This is `failure-soft` for a SessionStart hook: the session must start
|
|
31
|
+
* whether or not there is any state to re-inject.
|
|
32
|
+
*
|
|
33
|
+
* `--json` exists for the human/diagnostic path — `peaks session reinject
|
|
34
|
+
* --json` is how you inspect what the hook would inject without reading a
|
|
35
|
+
* transcript. It is deliberately NOT what the hook runs.
|
|
36
|
+
*/
|
|
37
|
+
import { printResult } from '../cli-helpers.js';
|
|
38
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
39
|
+
import { resolveCanonicalProjectRootStrict, InvalidProjectRootError } from '../../services/config/config-safety.js';
|
|
40
|
+
import { POST_COMPACT_REINJECTION_BYTE_BUDGET, buildPostCompactReinjectionCard } from '../../services/context/post-compact-reinjection.js';
|
|
41
|
+
/**
|
|
42
|
+
* Parse `--budget-bytes`. Diagnostic only — the production ceiling is the
|
|
43
|
+
* constant. A value that is not a non-negative integer is REFUSED rather than
|
|
44
|
+
* coerced, because a coerced budget would silently change what the card
|
|
45
|
+
* proves (`--budget-bytes 4KiB` becoming `NaN` and then the default would
|
|
46
|
+
* report a full card while the caller believed they had constrained it).
|
|
47
|
+
*/
|
|
48
|
+
function parseBudget(raw) {
|
|
49
|
+
if (raw === undefined)
|
|
50
|
+
return { ok: true, value: null };
|
|
51
|
+
const parsed = Number(raw);
|
|
52
|
+
if (!Number.isInteger(parsed) || parsed < 0)
|
|
53
|
+
return { ok: false };
|
|
54
|
+
return { ok: true, value: parsed };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Pure-ish action body, exported so tests can drive it with inputs Commander
|
|
58
|
+
* would mangle (trailing NUL bytes, whitespace-only paths). Never throws and
|
|
59
|
+
* never writes to stdout itself — `runReinjectAction` is the IO edge.
|
|
60
|
+
*/
|
|
61
|
+
export function buildReinjectResult(opts) {
|
|
62
|
+
if (!opts.project || opts.project.trim() === '') {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
reason: 'empty-project',
|
|
66
|
+
message: '--project <path> is required and must be non-empty'
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
let projectRoot;
|
|
70
|
+
try {
|
|
71
|
+
projectRoot = resolveCanonicalProjectRootStrict(opts.project);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error instanceof InvalidProjectRootError) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
reason: 'invalid-project-root',
|
|
78
|
+
message: `--project is not a usable project root (${error.reason})`
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
reason: 'unreadable-project-root',
|
|
84
|
+
message: error instanceof Error ? error.message : String(error)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const budget = parseBudget(opts.budgetBytes);
|
|
88
|
+
if (!budget.ok) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
reason: 'invalid-project-root',
|
|
92
|
+
message: `--budget-bytes must be a non-negative integer, got ${String(opts.budgetBytes)}`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const card = buildPostCompactReinjectionCard({
|
|
96
|
+
projectRoot,
|
|
97
|
+
...(budget.value !== null ? { budgetBytes: budget.value } : {})
|
|
98
|
+
});
|
|
99
|
+
return { ok: true, card };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The IO edge. Returns the process exit code so callers (CLI path or test
|
|
103
|
+
* path) apply it uniformly. NEVER calls `process.exit`.
|
|
104
|
+
*
|
|
105
|
+
* Both branches return 0 on the failure path — see this file's header. The
|
|
106
|
+
* only way a caller sees `exitCode: 1` is a bug in the `--json` serializer
|
|
107
|
+
* itself, and even that is caught.
|
|
108
|
+
*/
|
|
109
|
+
export function runReinjectAction(opts, io) {
|
|
110
|
+
let result;
|
|
111
|
+
try {
|
|
112
|
+
result = buildReinjectResult(opts);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
result = {
|
|
116
|
+
ok: false,
|
|
117
|
+
reason: 'unreadable-project-root',
|
|
118
|
+
message: error instanceof Error ? error.message : String(error)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (opts.json === true) {
|
|
122
|
+
if (!result.ok) {
|
|
123
|
+
printResult(io, fail('session.reinject', `REINJECT_SKIPPED_${result.reason.replace(/-/g, '_').toUpperCase()}`, result.message, {
|
|
124
|
+
project: opts.project
|
|
125
|
+
}, []), true);
|
|
126
|
+
// A skipped card is a legitimate answer, not a failed command: the hook
|
|
127
|
+
// that runs this must never report a non-zero exit to the harness.
|
|
128
|
+
return { exitCode: 0 };
|
|
129
|
+
}
|
|
130
|
+
const card = result.card;
|
|
131
|
+
printResult(io, ok('session.reinject', {
|
|
132
|
+
sessionId: card.sessionId,
|
|
133
|
+
budgetBytes: card.budgetBytes,
|
|
134
|
+
bytes: card.bytes,
|
|
135
|
+
emittedRanks: card.emittedRanks,
|
|
136
|
+
droppedRanks: card.droppedRanks,
|
|
137
|
+
unresolved: card.unresolved,
|
|
138
|
+
// The payload itself, so a caller can read it without re-running.
|
|
139
|
+
card: card.text
|
|
140
|
+
}), true);
|
|
141
|
+
return { exitCode: 0 };
|
|
142
|
+
}
|
|
143
|
+
// The card path — the one the hook uses. Verbatim, and nothing on failure.
|
|
144
|
+
if (!result.ok)
|
|
145
|
+
return { exitCode: 0 };
|
|
146
|
+
if (result.card.text.length > 0)
|
|
147
|
+
io.stdout(result.card.text);
|
|
148
|
+
return { exitCode: 0 };
|
|
149
|
+
}
|
|
150
|
+
export function registerReinjectCommand(program, io) {
|
|
151
|
+
// Reuse the `session` commander group registered by the auto-registration
|
|
152
|
+
// pass, exactly as `registerPrimerCommand` does — `program.command('session
|
|
153
|
+
// reinject')` would register a single literal command name instead of a
|
|
154
|
+
// child of the group, and creating a second `session` group throws.
|
|
155
|
+
const session = program.commands.find((c) => c.name() === 'session');
|
|
156
|
+
if (!session) {
|
|
157
|
+
throw new Error('registerReinjectCommand: session group not registered; ' +
|
|
158
|
+
'autoRegisterAllCommands must run before registerReinjectCommand');
|
|
159
|
+
}
|
|
160
|
+
session
|
|
161
|
+
.command('reinject')
|
|
162
|
+
.description('Print the post-compact engineering-state card to stdout. ' +
|
|
163
|
+
'Run by the SessionStart hook (matcher: compact) so Claude Code re-injects it ' +
|
|
164
|
+
'as context after a compaction. Read-only; prints nothing (exit 0) when there is ' +
|
|
165
|
+
'no state to re-inject.')
|
|
166
|
+
.requiredOption('--project <path>', 'Project root (must be a non-empty canonical path)')
|
|
167
|
+
.option('--budget-bytes <n>', `Diagnostic override for the card's byte ceiling (default ${POST_COMPACT_REINJECTION_BYTE_BUDGET})`)
|
|
168
|
+
.option('--json', 'emit a JSON envelope { ok, data } to stdout instead of the card')
|
|
169
|
+
.action((opts) => {
|
|
170
|
+
const { exitCode } = runReinjectAction(opts, io);
|
|
171
|
+
if (exitCode !== 0)
|
|
172
|
+
process.exitCode = exitCode;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -277,9 +277,12 @@ export function registerRequestCommands(program, io) {
|
|
|
277
277
|
// 0.85–0.95 pre-compact zone, write a checkpoint BEFORE the
|
|
278
278
|
// transition completes and attach `preCompactCheckpoint: true` to
|
|
279
279
|
// the response envelope. The LLM remains the decision-maker; this
|
|
280
|
-
// hook only surfaces the signal.
|
|
281
|
-
//
|
|
282
|
-
// refuse
|
|
280
|
+
// hook only surfaces the signal. At ratio ≥ 0.95 the auto-compact
|
|
281
|
+
// orchestrator ASKS the harness to compact and reports that it is
|
|
282
|
+
// waiting; it does not refuse anything, because peaks-loop has no
|
|
283
|
+
// executor for a running session and a refusal it cannot enforce only
|
|
284
|
+
// deadlocked the runner (slice
|
|
285
|
+
// 2026-09-13-auto-compact-trigger-ownership).
|
|
283
286
|
let preCompact = null;
|
|
284
287
|
// 2026-09-03-codegraph-autorefresh: auto codegraph re-index on the
|
|
285
288
|
// RD → QA slice-complete boundary. Set only for rd:qa-handoff; null
|
|
@@ -46,6 +46,7 @@ export function runShadcnInit(options) {
|
|
|
46
46
|
const result = spawnSync(invocation.command, [...invocation.args], {
|
|
47
47
|
cwd: options.cwd,
|
|
48
48
|
encoding: 'utf8',
|
|
49
|
+
windowsHide: true,
|
|
49
50
|
timeout: options.timeoutMs ?? 300_000,
|
|
50
51
|
maxBuffer: 32 * 1024 * 1024,
|
|
51
52
|
env: invocation.baseEnv
|
|
@@ -203,7 +203,8 @@ export function runRunner(framework, argv, projectRoot, deps = {}) {
|
|
|
203
203
|
const proc = spawnFn(resolution.command, resolution.args, {
|
|
204
204
|
cwd: projectRoot,
|
|
205
205
|
env: process.env,
|
|
206
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
206
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
207
|
+
windowsHide: true
|
|
207
208
|
});
|
|
208
209
|
let stdout = '';
|
|
209
210
|
let stderr = '';
|
|
@@ -59,7 +59,7 @@ function detectHypervisor(requested) {
|
|
|
59
59
|
break;
|
|
60
60
|
}
|
|
61
61
|
try {
|
|
62
|
-
const v = execSync(`${binary} --version`, { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
|
|
62
|
+
const v = execSync(`${binary} --version`, { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8', windowsHide: true });
|
|
63
63
|
if (requested === 'kvm' && !existsSync('/dev/kvm')) {
|
|
64
64
|
return { ok: false, stderr: 'KVM kernel module not loaded (/dev/kvm absent)' };
|
|
65
65
|
}
|
|
@@ -87,27 +87,27 @@ function spawnVmWithHypervisor(args) {
|
|
|
87
87
|
</domain>`;
|
|
88
88
|
const xmlPath = `${args.workdir}/.peaks-vm-${args.leaseId}.xml`;
|
|
89
89
|
require('node:fs').writeFileSync(xmlPath, xml, 'utf8');
|
|
90
|
-
const out = execSync(`virsh create ${xmlPath}`, { cwd: args.workdir, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
|
|
90
|
+
const out = execSync(`virsh create ${xmlPath}`, { cwd: args.workdir, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8', windowsHide: true });
|
|
91
91
|
return { vmId: out.trim() };
|
|
92
92
|
}
|
|
93
93
|
if (args.hypervisor === 'hyperkit') {
|
|
94
|
-
const out = execSync(`hvftool create --image ${args.image} --mount ${args.mount}:/work --label peaks.leaseId=${args.leaseId} --label peaks.rid=${args.rid} --entrypoint sleep -- infinity`, { cwd: args.workdir, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
|
|
94
|
+
const out = execSync(`hvftool create --image ${args.image} --mount ${args.mount}:/work --label peaks.leaseId=${args.leaseId} --label peaks.rid=${args.rid} --entrypoint sleep -- infinity`, { cwd: args.workdir, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8', windowsHide: true });
|
|
95
95
|
return { vmId: out.trim() };
|
|
96
96
|
}
|
|
97
97
|
// hyperv: the hvc shim writes a vhdx + emits the new VM id.
|
|
98
|
-
const out = execSync(`hvc create --image ${args.image} --mount ${args.mount} --label peaks.leaseId=${args.leaseId} --label peaks.rid=${args.rid}`, { cwd: args.workdir, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
|
|
98
|
+
const out = execSync(`hvc create --image ${args.image} --mount ${args.mount} --label peaks.leaseId=${args.leaseId} --label peaks.rid=${args.rid}`, { cwd: args.workdir, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8', windowsHide: true });
|
|
99
99
|
return { vmId: out.trim() };
|
|
100
100
|
}
|
|
101
101
|
function destroyVmWithHypervisor(args) {
|
|
102
102
|
try {
|
|
103
103
|
if (args.hypervisor === 'kvm') {
|
|
104
|
-
execSync(`virsh destroy ${args.vmId}`, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
104
|
+
execSync(`virsh destroy ${args.vmId}`, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
105
105
|
}
|
|
106
106
|
else if (args.hypervisor === 'hyperkit') {
|
|
107
|
-
execSync(`hvftool stop ${args.vmId}`, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
107
|
+
execSync(`hvftool stop ${args.vmId}`, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
108
108
|
}
|
|
109
109
|
else {
|
|
110
|
-
execSync(`hvc stop ${args.vmId}`, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
110
|
+
execSync(`hvc stop ${args.vmId}`, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
111
111
|
}
|
|
112
112
|
return true;
|
|
113
113
|
}
|
|
@@ -22,7 +22,7 @@ import { initWorkspace, InvalidSessionIdError, ConflictingSessionError, LegacyCh
|
|
|
22
22
|
// session-id-shaped directories at the `.peaks/_runtime/` sibling
|
|
23
23
|
// level.
|
|
24
24
|
import { ensureSessionWithRotation } from '../../../services/session/session-manager.js';
|
|
25
|
-
import {
|
|
25
|
+
import { resolveWritableProjectRoot, UnsafeProjectRootError } from '../../../services/config/config-safety.js';
|
|
26
26
|
import { applyHookInstall, readHookStatus } from '../../../services/skills/hooks-settings-service.js';
|
|
27
27
|
import { clearStalePresenceOnRotation } from '../../../services/skills/skill-presence-service.js';
|
|
28
28
|
import { gcStalePresenceLeases } from '../../../services/skills/presence-lease-service.js';
|
|
@@ -69,7 +69,12 @@ export function registerWorkspaceInitCommand(workspace, io) {
|
|
|
69
69
|
// (the same regression that produced prompt-project/.peaks/ in
|
|
70
70
|
// the 5/27-5/29 sessions). When startPath is not inside any
|
|
71
71
|
// git repo, the helper falls through to the cwd verbatim.
|
|
72
|
-
|
|
72
|
+
// `resolveWritableProjectRoot` = `resolveCanonicalProjectRoot` plus the
|
|
73
|
+
// home-directory refusal. It has to be this one, and it has to run here:
|
|
74
|
+
// `ensureSessionWithRotation` below WRITES (it mints/binds a session), so
|
|
75
|
+
// a guard placed after it would leave a `.peaks/_runtime/` tree in the
|
|
76
|
+
// user's home even though the init was refused.
|
|
77
|
+
const projectRoot = resolveWritableProjectRoot(options.project);
|
|
73
78
|
// Slice 018: outer-session-mismatch auto-rotation. When the
|
|
74
79
|
// user did NOT pass --session-id explicitly, run
|
|
75
80
|
// `ensureSessionWithRotation` so the binding is rotated on
|
|
@@ -408,6 +413,23 @@ export function registerWorkspaceInitCommand(workspace, io) {
|
|
|
408
413
|
}, warningsForEnvelope, nextActions), options.json);
|
|
409
414
|
}
|
|
410
415
|
catch (error) {
|
|
416
|
+
if (error instanceof UnsafeProjectRootError) {
|
|
417
|
+
// A typed refusal, not a crash: "you are standing in your home
|
|
418
|
+
// directory, which is not a project" is a thing the caller can fix in
|
|
419
|
+
// one step, and the envelope has to say which directory was refused and
|
|
420
|
+
// what to pass instead. `WORKSPACE_INIT_FAILED` below would have buried
|
|
421
|
+
// it as an unexplained failure.
|
|
422
|
+
printResult(io, fail('workspace.init', 'UNSAFE_PROJECT_ROOT', error.message, {
|
|
423
|
+
resolvedProjectRoot: error.projectRoot,
|
|
424
|
+
projectOption: options.project ?? null
|
|
425
|
+
}, [
|
|
426
|
+
'Pass --project <path-to-your-project> instead of a path that resolves to your home directory.',
|
|
427
|
+
'This command creates .peaks/, .gitignore, .claude/settings.local.json and a codegraph index; none of those belong in $HOME.',
|
|
428
|
+
'If you meant to initialize the current directory, cd into a project directory first — peaks will not write into the home directory itself.'
|
|
429
|
+
]), options.json);
|
|
430
|
+
process.exitCode = 1;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
411
433
|
if (error instanceof InvalidSessionIdError) {
|
|
412
434
|
printResult(io, fail('workspace.init', error.code, error.message, { sessionId: options.sessionId }, ['Use a date-prefixed kebab slug like 2026-05-25-add-user-auth']), options.json);
|
|
413
435
|
process.exitCode = 1;
|
|
@@ -91,7 +91,7 @@ export function registerWorktreeLeaseCommands(auth, io) {
|
|
|
91
91
|
// and authorize this very `git worktree add`; for Part 1 we still
|
|
92
92
|
// require a current `peaks worktree auth grant` token to remain
|
|
93
93
|
// consistent with the slice-027 hard gate contract.
|
|
94
|
-
execSync(`git worktree add "${wtPath}" -b "${branch}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
|
|
94
|
+
execSync(`git worktree add "${wtPath}" -b "${branch}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8', windowsHide: true });
|
|
95
95
|
atomicWriteJson(leaseFilePath(joinPathSession(projectRoot, sessionId), leaseId), lease);
|
|
96
96
|
// Part 4.A: emit spawn metric (fire-and-forget; never blocks).
|
|
97
97
|
emitLeaseEvent({
|
|
@@ -167,7 +167,7 @@ export function registerWorktreeLeaseCommands(auth, io) {
|
|
|
167
167
|
// released.
|
|
168
168
|
let gitWorktreeRemoveFailed = false;
|
|
169
169
|
try {
|
|
170
|
-
execSync(`git worktree remove --force "${lease.path}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
|
|
170
|
+
execSync(`git worktree remove --force "${lease.path}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8', windowsHide: true });
|
|
171
171
|
}
|
|
172
172
|
catch {
|
|
173
173
|
gitWorktreeRemoveFailed = true;
|
|
@@ -379,14 +379,14 @@ export function registerWorktreeLeaseCommands(auth, io) {
|
|
|
379
379
|
// already gone we still mark the lease gc.
|
|
380
380
|
let gitWorktreeRemoveFailed = false;
|
|
381
381
|
try {
|
|
382
|
-
execSync(`git worktree remove --force "${updated.path}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
|
|
382
|
+
execSync(`git worktree remove --force "${updated.path}"`, { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8', windowsHide: true });
|
|
383
383
|
}
|
|
384
384
|
catch {
|
|
385
385
|
gitWorktreeRemoveFailed = true;
|
|
386
386
|
}
|
|
387
387
|
// `git worktree prune` clears any stale admin entries. Best-effort.
|
|
388
388
|
try {
|
|
389
|
-
execSync('git worktree prune', { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8' });
|
|
389
|
+
execSync('git worktree prune', { cwd: projectRoot, stdio: 'pipe', encoding: 'utf8', windowsHide: true });
|
|
390
390
|
}
|
|
391
391
|
catch {
|
|
392
392
|
// ignore — prune is idempotent
|
package/dist/cli/index.js
CHANGED
|
@@ -29,8 +29,34 @@ const defaultIo = {
|
|
|
29
29
|
const argv = process.argv.slice(2);
|
|
30
30
|
const hasHelp = argv.some((arg) => arg === '--help' || arg === '-h');
|
|
31
31
|
const firstPositional = argv.find((arg) => !arg.startsWith('-'));
|
|
32
|
+
const program = createProgram();
|
|
33
|
+
/**
|
|
34
|
+
* The subcommand path the caller actually invoked, in the same tokens they
|
|
35
|
+
* typed (`release canary`). Derived from the registered command tree by
|
|
36
|
+
* consuming leading non-`-` argv tokens, so it never disagrees with what
|
|
37
|
+
* Commander dispatched on.
|
|
38
|
+
*
|
|
39
|
+
* Commander's `CommanderError` carries a code and a message but no command
|
|
40
|
+
* reference — `missingMandatoryOptionValue` is raised by the leaf command and
|
|
41
|
+
* throws out of `parseAsync` with nothing naming it. Without this walk the
|
|
42
|
+
* missing-option envelope could only say `command: "cli"`, which is the field
|
|
43
|
+
* the caller needs to act on.
|
|
44
|
+
*/
|
|
45
|
+
function resolveInvokedCommandPath() {
|
|
46
|
+
let cmd = program;
|
|
47
|
+
const parts = [];
|
|
48
|
+
for (const token of argv) {
|
|
49
|
+
if (token.startsWith('-'))
|
|
50
|
+
break;
|
|
51
|
+
const next = cmd.commands.find((c) => c.name() === token || c.aliases().includes(token));
|
|
52
|
+
if (next === undefined)
|
|
53
|
+
break;
|
|
54
|
+
parts.push(next.name());
|
|
55
|
+
cmd = next;
|
|
56
|
+
}
|
|
57
|
+
return parts.length > 0 ? parts.join(' ') : program.name();
|
|
58
|
+
}
|
|
32
59
|
if (hasHelp && firstPositional !== undefined) {
|
|
33
|
-
const program = createProgram();
|
|
34
60
|
const registered = new Set(program.commands.map((c) => c.name()));
|
|
35
61
|
if (!registered.has(firstPositional)) {
|
|
36
62
|
// Defer the check by 0ms so Commander's own help handler runs first
|
|
@@ -41,7 +67,7 @@ if (hasHelp && firstPositional !== undefined) {
|
|
|
41
67
|
});
|
|
42
68
|
}
|
|
43
69
|
}
|
|
44
|
-
|
|
70
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
45
71
|
if (error instanceof CommanderError && error.code === 'commander.version') {
|
|
46
72
|
return;
|
|
47
73
|
}
|
|
@@ -59,6 +85,27 @@ createProgram().parseAsync(process.argv).catch((error) => {
|
|
|
59
85
|
if (error.code === 'commander.help' || error.code === 'commander.helpDisplayed') {
|
|
60
86
|
return;
|
|
61
87
|
}
|
|
88
|
+
if (error.code === 'commander.missingMandatoryOptionValue') {
|
|
89
|
+
// A `.requiredOption()` the caller did not supply. Commander raises this
|
|
90
|
+
// as a plain `Error`-shaped `CommanderError`, so the `.catch()` below used
|
|
91
|
+
// to file it under `UNHANDLED_ERROR` — "you left out an argument" reported
|
|
92
|
+
// as a crash, with empty `nextActions` and no way to see WHICH option or
|
|
93
|
+
// what values it takes. The flags string Commander formats carries both
|
|
94
|
+
// (`--percent <10|50>`), so it is worth extracting rather than
|
|
95
|
+
// paraphrasing: it is the option's own declaration.
|
|
96
|
+
const message = getErrorMessage(error);
|
|
97
|
+
const option = /required option '([^']+)'/.exec(message)?.[1];
|
|
98
|
+
const invoked = resolveInvokedCommandPath();
|
|
99
|
+
printErrorEnvelope(defaultIo, invoked, 'MISSING_REQUIRED_OPTION', option === undefined
|
|
100
|
+
? message
|
|
101
|
+
: `Missing required option '${option}' for \`peaks ${invoked}\`.`, { option: option ?? null }, [
|
|
102
|
+
option === undefined
|
|
103
|
+
? `Run \`peaks ${invoked} --help\` to see the options this command requires.`
|
|
104
|
+
: `Supply ${option} — it is required, so the command has no default for it.`,
|
|
105
|
+
`Run \`peaks ${invoked} --help\` for the option's accepted values and its siblings.`
|
|
106
|
+
]);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
62
109
|
if (error.code === 'commander.missingArgument' || error.code === 'commander.unknownCommand' || error.code === 'commander.unknownOption') {
|
|
63
110
|
// Emit a `COMMAND_NOT_FOUND` JSON envelope for the unknown-command
|
|
64
111
|
// path. The error text already went to stderr via Commander's
|
package/dist/cli/program.js
CHANGED
|
@@ -17,6 +17,7 @@ import { registerWorkspaceCommands } from './commands/workspace-commands.js';
|
|
|
17
17
|
import { registerSopCommands } from './commands/sop-commands.js';
|
|
18
18
|
import { registerSkillVisibilityCommand } from './commands/skill-visibility.js';
|
|
19
19
|
import { registerPrimerCommand } from './commands/primer-command.js';
|
|
20
|
+
import { registerReinjectCommand } from './commands/reinject-command.js';
|
|
20
21
|
import { applyRetention, cleanupEccCache } from '../services/log/retention.js';
|
|
21
22
|
import { writeLogEntry, maybeWriteStderr } from '../services/log/logger.js';
|
|
22
23
|
import { printSuperCommandCatalog } from './cli-helpers.js';
|
|
@@ -217,5 +218,9 @@ Run peaks (no arguments) for a quickstart. You likely want one of:
|
|
|
217
218
|
// `program.command('session primer')` (that registers a single
|
|
218
219
|
// literal command name, not a child of the session group).
|
|
219
220
|
registerPrimerCommand(program, io);
|
|
221
|
+
// rid 2026-09-13-a2-post-compact-reinject: register `peaks session reinject`
|
|
222
|
+
// as a sibling child of the same `session` group. It is the transport for
|
|
223
|
+
// the SessionStart (matcher `compact`) hook's post-compaction card.
|
|
224
|
+
registerReinjectCommand(program, io);
|
|
220
225
|
return program;
|
|
221
226
|
}
|
|
@@ -59,7 +59,7 @@ export function runHookGuardSubprocess(prompt) {
|
|
|
59
59
|
'sub-agent-dispatch-guard',
|
|
60
60
|
'--prompt', prompt,
|
|
61
61
|
'--json'
|
|
62
|
-
], { encoding: 'utf8' });
|
|
62
|
+
], { encoding: 'utf8', windowsHide: true });
|
|
63
63
|
if (result.status !== 0) {
|
|
64
64
|
// Fallback: allow (don't block the dispatch on a guard subprocess failure).
|
|
65
65
|
return {
|
|
@@ -53,7 +53,7 @@ class ProcessVendorAdapter {
|
|
|
53
53
|
if (args.force === true)
|
|
54
54
|
argv.push('--force');
|
|
55
55
|
return new Promise((resolveRun) => {
|
|
56
|
-
const proc = spawn(this.binary, argv, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
56
|
+
const proc = spawn(this.binary, argv, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
57
57
|
let stdout = '';
|
|
58
58
|
let stderr = '';
|
|
59
59
|
proc.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8'); });
|
|
@@ -16,7 +16,8 @@ export async function runJ01Contract(ctx) {
|
|
|
16
16
|
try {
|
|
17
17
|
const stdout = execFileSync('node', [bin, command, input], {
|
|
18
18
|
cwd: ctx.projectRoot,
|
|
19
|
-
env: { ...process.env, PEAKS_CALLER_ID: `guard-J01-${ctx.sessionId}` }
|
|
19
|
+
env: { ...process.env, PEAKS_CALLER_ID: `guard-J01-${ctx.sessionId}` },
|
|
20
|
+
windowsHide: true
|
|
20
21
|
}).toString('utf8');
|
|
21
22
|
const env = JSON.parse(stdout);
|
|
22
23
|
if (!env.ok) {
|
|
@@ -6,10 +6,10 @@ const STATES = ['spec-locked', 'implemented', 'qa-handoff', 'handed-off'];
|
|
|
6
6
|
export async function runJ02Contract(ctx, projectRoot = ctx.projectRoot) {
|
|
7
7
|
const bin = join(ctx.projectRoot, 'bin', 'peaks.js');
|
|
8
8
|
const tmp = mkdtempSync(join(tmpdir(), 'cbl-J02-'));
|
|
9
|
-
const ws = execFileSync('node', [bin, 'workspace', 'init', '--project', tmp, '--json'], { cwd: tmp }).toString('utf8');
|
|
9
|
+
const ws = execFileSync('node', [bin, 'workspace', 'init', '--project', tmp, '--json'], { cwd: tmp, windowsHide: true }).toString('utf8');
|
|
10
10
|
const { data: { sessionId } } = JSON.parse(ws);
|
|
11
11
|
const rid = '2026-08-03-j02-fixture';
|
|
12
|
-
const initOut = execFileSync('node', [bin, 'request', 'init', '--role', 'rd', '--id', rid, '--project', tmp, '--session-id', sessionId, '--apply', '--json'], { cwd: tmp }).toString('utf8');
|
|
12
|
+
const initOut = execFileSync('node', [bin, 'request', 'init', '--role', 'rd', '--id', rid, '--project', tmp, '--session-id', sessionId, '--apply', '--json'], { cwd: tmp, windowsHide: true }).toString('utf8');
|
|
13
13
|
const initEnv = JSON.parse(initOut);
|
|
14
14
|
// `request init` writes the file as `NNN-<id-slug>.md`. The transition CLI accepts
|
|
15
15
|
// the file's basename (without .md) as the requestId. Derive it from data.path.
|
|
@@ -19,7 +19,7 @@ export async function runJ02Contract(ctx, projectRoot = ctx.projectRoot) {
|
|
|
19
19
|
for (const s of STATES) {
|
|
20
20
|
const out = execFileSync('node', [bin, 'request', 'transition', requestId, '--role', 'rd', '--state', s,
|
|
21
21
|
'--project', tmp, '--session-id', sessionId, '--confirm', '--allow-incomplete',
|
|
22
|
-
'--reason', 'J02 contract fixture', '--json'], { cwd: tmp }).toString('utf8');
|
|
22
|
+
'--reason', 'J02 contract fixture', '--json'], { cwd: tmp, windowsHide: true }).toString('utf8');
|
|
23
23
|
const env = JSON.parse(out);
|
|
24
24
|
last = env.data.state;
|
|
25
25
|
}
|
|
@@ -10,7 +10,8 @@ export async function runJ04Contract(ctx) {
|
|
|
10
10
|
try {
|
|
11
11
|
const stdout = execFileSync('node', [bin, 'audit', 'goal', '--help'], {
|
|
12
12
|
cwd: ctx.projectRoot,
|
|
13
|
-
env: { ...process.env, PEAKS_CALLER_ID: `guard-J04-${ctx.sessionId}` }
|
|
13
|
+
env: { ...process.env, PEAKS_CALLER_ID: `guard-J04-${ctx.sessionId}` },
|
|
14
|
+
windowsHide: true
|
|
14
15
|
}).toString('utf8');
|
|
15
16
|
helpOk = stdout.length > 0 && !stdout.includes('unknown command');
|
|
16
17
|
}
|
|
@@ -23,7 +24,8 @@ export async function runJ04Contract(ctx) {
|
|
|
23
24
|
execFileSync('node', [bin, 'audit', 'goal', '--project', tmp, '--json'], {
|
|
24
25
|
cwd: ctx.projectRoot,
|
|
25
26
|
env: { ...process.env, PEAKS_CALLER_ID: `guard-J04-${ctx.sessionId}` },
|
|
26
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
27
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
28
|
+
windowsHide: true
|
|
27
29
|
});
|
|
28
30
|
}
|
|
29
31
|
catch (e) {
|
|
@@ -19,7 +19,8 @@ export async function runJ07Contract(ctx) {
|
|
|
19
19
|
const out = execFileSync('node', [bin, 'test', '--help'], {
|
|
20
20
|
cwd: ctx.projectRoot,
|
|
21
21
|
env: { ...process.env, PEAKS_CALLER_ID: `guard-J07-${ctx.sessionId}` },
|
|
22
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
22
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
23
|
+
windowsHide: true
|
|
23
24
|
}).toString('utf8');
|
|
24
25
|
helpOk = out.length > 0 && !out.includes('unknown command');
|
|
25
26
|
}
|
|
@@ -126,6 +126,12 @@ export declare function summarizeLifecycleError(error: unknown): string;
|
|
|
126
126
|
* `afterRatio: 0` there would publish a fabricated number;
|
|
127
127
|
* - the ratio is still at or above the auto-fire threshold — the
|
|
128
128
|
* compact has not landed, so the run stays open.
|
|
129
|
+
*
|
|
130
|
+
* Returns the settled record, or `null` when nothing settled. Slice
|
|
131
|
+
* 2026-09-13-auto-compact-trigger-ownership: the caller needs the settled
|
|
132
|
+
* `afterRatio` to append the "observed compaction point" row that makes the
|
|
133
|
+
* intent-vs-observed gap readable after a real session. The return value is
|
|
134
|
+
* telemetry only — callers that ignore it are unaffected.
|
|
129
135
|
*/
|
|
130
136
|
export declare function settleOpenLifecycleRun(input: {
|
|
131
137
|
readonly projectRoot: string;
|
|
@@ -134,5 +140,9 @@ export declare function settleOpenLifecycleRun(input: {
|
|
|
134
140
|
readonly source: string;
|
|
135
141
|
readonly autoFireThreshold: number;
|
|
136
142
|
readonly onLifecycleStage?: ((stage: CompactLifecycleStage, record: CompactLifecycleRecord) => void) | undefined;
|
|
137
|
-
}):
|
|
143
|
+
}): {
|
|
144
|
+
readonly runId: string;
|
|
145
|
+
readonly triggerRatio: number;
|
|
146
|
+
readonly afterRatio: number;
|
|
147
|
+
} | null;
|
|
138
148
|
export {};
|