peaks-loop 4.0.5 → 4.0.7
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 +320 -0
- package/dist/cli/commands/code-job-shape-commands.js +11 -8
- package/dist/cli/commands/code-mode-gate-commands.js +43 -1
- package/dist/cli/commands/code-runtime-commands.js +11 -6
- package/dist/cli/commands/core/standards-command.js +50 -5
- package/dist/cli/commands/dispatch-commands.js +39 -3
- package/dist/cli/commands/hooks-commands.js +26 -9
- package/dist/cli/commands/sub-agent-commands.js +2 -0
- package/dist/cli/commands/sub-agent-exec-command.d.ts +25 -0
- package/dist/cli/commands/sub-agent-exec-command.js +132 -0
- package/dist/cli/commands/sub-agent-shared.d.ts +11 -0
- package/dist/services/audit/backing-detector.d.ts +19 -2
- package/dist/services/audit/backing-detector.js +66 -7
- package/dist/services/audit/enforcers/lint-audit-regression.js +10 -1
- package/dist/services/context/build-dispatch-system-prompt.js +18 -0
- package/dist/services/dispatch/test-tool-detection.d.ts +14 -0
- package/dist/services/dispatch/test-tool-detection.js +22 -0
- package/dist/services/scan/archetype-service.js +52 -2
- package/dist/services/session/index.d.ts +1 -1
- package/dist/services/session/index.js +1 -1
- package/dist/services/session/session-manager.d.ts +29 -0
- package/dist/services/session/session-manager.js +33 -0
- package/dist/services/skills/hooks-settings-service.d.ts +41 -0
- package/dist/services/skills/hooks-settings-service.js +61 -5
- package/dist/services/slice/slice-check-service.js +59 -0
- package/dist/services/standards/find-skills-integration.d.ts +59 -0
- package/dist/services/standards/find-skills-integration.js +225 -0
- package/dist/services/standards/project-standards-service.js +44 -0
- package/package.json +4 -4
- package/scripts/install-skills.mjs +171 -0
- package/skills/peaks-code/SKILL.md +43 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,325 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.7 — 2026-08-02 (dogfood surface repairs on `ice-cola`)
|
|
4
|
+
|
|
5
|
+
A 24h-mode dogfood pass on the downstream `ice-cola` monorepo (4
|
|
6
|
+
sub-packages: admin / server / client / hermes-agent) surfaced 25 real
|
|
7
|
+
CLI / runtime / documentation defects across peaks-loop 4.0.6, plus a
|
|
8
|
+
meta-level flow bug (the LLM bypassed the peaks-code / peaks-rd /
|
|
9
|
+
peaks-qa flow and routed 22 files / 632 lines of `Edit` calls
|
|
10
|
+
directly). This release closes all 4 P0/P1 ship-blockers, the 5
|
|
11
|
+
P2 batch fixes, the find-skills integration, and 4 PR-meta
|
|
12
|
+
enforcement-layer fixes.
|
|
13
|
+
release closes the 5 P0/P1 ship-blockers and reshapes SKILL.md's
|
|
14
|
+
Drift Index. 4 P2 changes were documented in the changeset but only
|
|
15
|
+
the documentation half (Drift Index refresh) ships in 4.0.7; the
|
|
16
|
+
implementation half (monorepo-aware language detection, monorepo
|
|
17
|
+
sub-agent prompt, slice check monorepo typecheck) is queued for 4.0.8.
|
|
18
|
+
|
|
19
|
+
### P0 — `peaks audit red-lines` no longer reports false-positive enforcerRef orphans on consumer projects
|
|
20
|
+
|
|
21
|
+
- `src/services/audit/backing-detector.ts` and
|
|
22
|
+
`src/services/audit/enforcers/lint-audit-regression.ts` now resolve
|
|
23
|
+
`enforcerRef` paths against the peaks-loop source root (resolved via
|
|
24
|
+
`import.meta.url` + upward walk to `package.json#name === 'peaks-loop'`),
|
|
25
|
+
with an explicit `peaksLoopRoot` override for tests. Pre-rid every
|
|
26
|
+
downstream audit reported all 26 enforcer files as "missing on disk"
|
|
27
|
+
(88 enforcerFindings on ice-cola). Post-rid: 0.
|
|
28
|
+
- New `resolvePeaksLoopRoot()` exported helper.
|
|
29
|
+
- Tests: `tests/unit/audit/backing-detector-resolve-root.test.ts` (8
|
|
30
|
+
cases including the ice-cola repro).
|
|
31
|
+
|
|
32
|
+
### P0 — `peaks code {detect-job,read-job-shape}`, `peaks workflow plan read`, and `peaks route` now resolve the canonical session id
|
|
33
|
+
|
|
34
|
+
- New shared resolver `resolveActiveSessionId(projectRoot, override?)`
|
|
35
|
+
in `src/services/session/session-manager.ts`, exported via
|
|
36
|
+
`src/services/session/index.ts`. Composes the existing
|
|
37
|
+
`getSessionIdCanonical` + `getSessionId` fan-out so the same
|
|
38
|
+
`sessionId` flows through all 4 CLIs.
|
|
39
|
+
- `src/cli/commands/code-job-shape-commands.ts` and
|
|
40
|
+
`src/cli/commands/code-runtime-commands.ts` now use the shared
|
|
41
|
+
resolver instead of the per-file `getSkillPresence` helper (which
|
|
42
|
+
read `.peaks/.active-skill.json` — a different store from
|
|
43
|
+
`.peaks/_runtime/session.json`).
|
|
44
|
+
- Pre-rid the 4 CLIs reported `NO_ACTIVE_SESSION` for any session
|
|
45
|
+
created via `peaks workspace init` that never had
|
|
46
|
+
`peaks skill presence:set peaks-code` called on it (the common
|
|
47
|
+
case for downstream consumer projects and 24h-mode sessions).
|
|
48
|
+
- Tests: `tests/unit/session/resolve-active-session.test.ts` (7 cases).
|
|
49
|
+
|
|
50
|
+
### P1 — `peaks code should-pause` no longer false-positives in 24h-mode
|
|
51
|
+
|
|
52
|
+
- `src/cli/commands/code-mode-gate-commands.ts` short-circuits the
|
|
53
|
+
stale-presence check when `.peaks/_runtime/<sid>/24h-state.json`
|
|
54
|
+
reads `state === '24H_ACTIVE'`. Without this, every 24h-mode
|
|
55
|
+
session hit a `shouldPause: true` wall at Step 1 even though
|
|
56
|
+
24h-mode is explicitly designed to be a no-AskUserQuestion mode.
|
|
57
|
+
- New local helper `is24hActive(projectRoot, sessionId)` reuses the
|
|
58
|
+
existing `read24hState` from `services/24h-mode/store.ts`.
|
|
59
|
+
- Tests: `tests/unit/code/should-pause-24h-bypass.test.ts` (9 cases).
|
|
60
|
+
|
|
61
|
+
### P2 #6 (enhancement) — `peaks standards {init,update} --suggest-skills` integrates find-skills
|
|
62
|
+
|
|
63
|
+
- New `--suggest-skills` flag on `peaks standards init` and
|
|
64
|
+
`peaks standards update` (off by default). When set, peaks-loop
|
|
65
|
+
runs `npx skills find <query>` for each query derived from the
|
|
66
|
+
detected ProjectContext (component library, build tool, CSS
|
|
67
|
+
framework, notable deps, language) and emits the top-5 results
|
|
68
|
+
in `nextActions` as copy-paste `npx skills add` commands.
|
|
69
|
+
|
|
70
|
+
- Per user direction 2026-08-02: **no hardcoded verified-owner
|
|
71
|
+
whitelist**. The service runs `npx skills find` with no
|
|
72
|
+
`--owner` filter so the registry / leaderboard decides the
|
|
73
|
+
ranking (this is exactly the anti-pattern find-skills
|
|
74
|
+
SKILL.md Step 4 warns against — "do not recommend based solely
|
|
75
|
+
on a hardcoded list; verify with install count + source
|
|
76
|
+
reputation"). Recommendations are sorted by install count desc
|
|
77
|
+
so the user sees the same top results they would see running
|
|
78
|
+
`npx skills find` manually.
|
|
79
|
+
|
|
80
|
+
- The service NEVER calls `npx skills add` itself. Installation
|
|
81
|
+
is gated on the user passing `--suggest-skills` (off by
|
|
82
|
+
default) and the install command is emitted as a single shell
|
|
83
|
+
snippet the user reviews before pasting. The CLI caller owns
|
|
84
|
+
the gating.
|
|
85
|
+
|
|
86
|
+
- Failure mode: if `npx skills find` fails (network / registry
|
|
87
|
+
down), the service silently degrades — init/update still
|
|
88
|
+
completes, `nextActions` shows a "no matching skills found"
|
|
89
|
+
hint, no error. The user can re-run with `--suggest-skills`
|
|
90
|
+
later.
|
|
91
|
+
|
|
92
|
+
- New file: `src/services/standards/find-skills-integration.ts`.
|
|
93
|
+
Tests: `tests/unit/standards/find-skills-integration.test.ts`
|
|
94
|
+
(12 cases covering queriesForContext, parseFindOutput,
|
|
95
|
+
findSkillsForContext — all using a custom `SkillFindRunner`
|
|
96
|
+
injection so the test never hits the network).
|
|
97
|
+
|
|
98
|
+
### P2 batch — 5 monorepo / sub-agent prompt fixes (all in this release)
|
|
99
|
+
|
|
100
|
+
- **`peaks standards` / `detectLanguage` is monorepo-aware** (`project-standards-service.ts`).
|
|
101
|
+
A monorepo root that has no `tsconfig.json` (only `tsconfig.base.json` + per-package
|
|
102
|
+
`tsconfig.json`) is now classified as `typescript` when any sub-package in
|
|
103
|
+
`packages/<name>/` has its own `tsconfig.json`. Pre-rid ice-cola (4-package
|
|
104
|
+
TypeScript monorepo) was misclassified as `javascript` because the root scan
|
|
105
|
+
saw no `tsconfig.json` and the root `package.json#devDependencies` had
|
|
106
|
+
`peaks-loop` only (a `javascript` standards pack).
|
|
107
|
+
|
|
108
|
+
- **`peaks scan archetype` is monorepo-aware** (`archetype-service.ts`).
|
|
109
|
+
A 1-level walk into `packages/<name>/` merges each sub-package's
|
|
110
|
+
`dependencies` / `devDependencies` / `peerDependencies` /
|
|
111
|
+
`optionalDependencies` into the framework-detection map. Ice-cola
|
|
112
|
+
now correctly reports `hasBackendFramework: true, backendFrameworks:
|
|
113
|
+
["@nestjs/core", "@nestjs/common"]` instead of
|
|
114
|
+
`hasBackendFramework: false`.
|
|
115
|
+
|
|
116
|
+
- **`peaks sub-agent dispatch` prompt includes a monorepo branch**
|
|
117
|
+
when `<projectRoot>/pnpm-workspace.yaml` is present. The pre-rid
|
|
118
|
+
static prompt had no monorepo handling, so sub-agents ran
|
|
119
|
+
`pnpm test` at the root which fanned out to every sub-package
|
|
120
|
+
(36-minute wall clock for a single-slice change). The new block
|
|
121
|
+
tells the sub-agent to scope to one sub-package via
|
|
122
|
+
`pnpm --filter @<scope>/<name> test <pattern>`.
|
|
123
|
+
|
|
124
|
+
- **`peaks slice check` typecheck stage is monorepo-aware**
|
|
125
|
+
(`slice-check-service.ts`). When `pnpm-workspace.yaml` is
|
|
126
|
+
present, the typecheck stage runs `pnpm -r typecheck` first and
|
|
127
|
+
falls back to `pnpm -r --parallel exec tsc --noEmit` if no
|
|
128
|
+
per-package `typecheck` script exists. Per-package failures
|
|
129
|
+
are captured in `data.failedPackages` so the operator can see
|
|
130
|
+
which sub-package broke (the pre-rid "tsc exited with code 1"
|
|
131
|
+
was a 2ms fake-fail with no actionable detail).
|
|
132
|
+
|
|
133
|
+
- **`peaks sub-agent dispatch` context window block no longer
|
|
134
|
+
misleads on fallback probes** (`build-dispatch-system-prompt.ts`).
|
|
135
|
+
When the probe source is `conservative-fallback` (or any other
|
|
136
|
+
untrusted value), the block short-circuits to "no probe
|
|
137
|
+
available — treat the displayed ratio as unverified" instead
|
|
138
|
+
of rendering "0.0% used (100.0% free)" as if it were a real
|
|
139
|
+
measurement. Trusted sources are `token-counted`,
|
|
140
|
+
`transcript-estimate`, and `statusline`; anything else triggers
|
|
141
|
+
the fallback path.
|
|
142
|
+
|
|
143
|
+
### P1 — SKILL.md Drift Index refreshed for 4.0.6+
|
|
144
|
+
|
|
145
|
+
- D-001 rewritten (the pre-4.0.7 entry was the inverse of 4.0.6's
|
|
146
|
+
actual behavior — `peaks code detect-job` requires `--is-job`).
|
|
147
|
+
- New D-013: `peaks code context-now --json` requires `--project`.
|
|
148
|
+
- New D-014: documents the 4-CLI `NO_ACTIVE_SESSION` regression fixed
|
|
149
|
+
by P0 above.
|
|
150
|
+
- New D-015: documents the 24h-mode ↔ stale-presence interaction fixed
|
|
151
|
+
by P1 above.
|
|
152
|
+
- Step N+2 probe primitive example now includes `--project <path>`.
|
|
153
|
+
|
|
154
|
+
### Compatibility
|
|
155
|
+
|
|
156
|
+
- All command names, options, and exit codes are preserved.
|
|
157
|
+
- Auto-compact 0.85 / 0.95 contract unchanged.
|
|
158
|
+
- `peaks audit red-lines` on peaks-loop itself shows 0 orphan findings
|
|
159
|
+
(it had 88+ before the fix; now also on every consumer project).
|
|
160
|
+
- `peaks code detect-job` / `read-job-shape` accept the same flags
|
|
161
|
+
as 4.0.6.
|
|
162
|
+
|
|
163
|
+
### How to verify
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Inside peaks-loop source root (after `pnpm build`):
|
|
167
|
+
peaks audit red-lines --project . --json
|
|
168
|
+
# expect: enforcerFindings: 0
|
|
169
|
+
|
|
170
|
+
# Inside a downstream project that has peaks-loop as a devDep:
|
|
171
|
+
peaks audit red-lines --project . --json
|
|
172
|
+
# expect: enforcerFindings: 0 (was 88+ before)
|
|
173
|
+
|
|
174
|
+
# 24h-mode + should-pause:
|
|
175
|
+
peaks session 24h-mode transition --state 24H_ACTIVE
|
|
176
|
+
peaks code should-pause --step step-1-mode-select --json
|
|
177
|
+
# expect: shouldPause decision is NOT "stale-presence"
|
|
178
|
+
|
|
179
|
+
# detect-job CLI:
|
|
180
|
+
peaks code detect-job --is-job false --rationale "x" --suggested-job-id n-a
|
|
181
|
+
# expect: ok, recorded to .peaks/_runtime/<sid>/job-shape.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Source: `.peaks/memory/2026-08-02-ice-cola-dogfood-surface-probe.md`.
|
|
185
|
+
|
|
186
|
+
### PR-meta batch — flow-level enforcement (4 fixes addressing why the LLM bypassed peaks-code in this same session)
|
|
187
|
+
|
|
188
|
+
The 4.0.7 dogfood pass surfaced a flow-level bug that the 25
|
|
189
|
+
P0/P1/P2 fixes alone could not close: the LLM routed 22 files /
|
|
190
|
+
632 lines of `Edit` calls directly without a single
|
|
191
|
+
`peaks sub-agent dispatch rd`. Six root causes were identified
|
|
192
|
+
(internal-only debug report, 2026-08-02); the four below close
|
|
193
|
+
the four most actionable layers with surgical changes.
|
|
194
|
+
|
|
195
|
+
- **PR-meta-1: Edit-enforcement hook** (new
|
|
196
|
+
`src/services/hooks/pre-tool-edit-enforcement.sh`).
|
|
197
|
+
Opt-in via `peaks hooks install --with-edit-enforcement` (off by
|
|
198
|
+
default to preserve the existing peaks-code behavior; the gate
|
|
199
|
+
is a HARD layer, not a soft warning). When the hook fires on
|
|
200
|
+
an `Edit` / `Write` / `MultiEdit` / `NotebookEdit` call
|
|
201
|
+
targeting `src/**`, it checks for an active RD artifact under
|
|
202
|
+
`.peaks/_runtime/<sid>/rd/requests/` and either allows the
|
|
203
|
+
call (RD exists) or denies with a structured
|
|
204
|
+
`permissionDecision: deny` envelope that points the LLM at the
|
|
205
|
+
right next step (`peaks request init` + `peaks sub-agent dispatch
|
|
206
|
+
rd`). Bypass conditions: tests / docs / RD artifacts / the
|
|
207
|
+
hook scripts themselves, a `.peaks/standards/edit-bypass` file
|
|
208
|
+
(gitignored escape hatch for hot-fixes), or the env var
|
|
209
|
+
`PEAKS_EDIT_BYPASS_24H=1` (24h-mode opt-in).
|
|
210
|
+
|
|
211
|
+
- **PR-meta-2: 24h mode scope explicit** (SKILL.md §"24h mode
|
|
212
|
+
explicit scope"). 24h mode is `peaks session 24h-mode
|
|
213
|
+
transition --state 24H_ACTIVE`. The pre-PR-meta-2 prose was
|
|
214
|
+
implicit ("bypass AskUserQuestion") and the LLM over-extended
|
|
215
|
+
it to **all** user instructions, including the Code-Change
|
|
216
|
+
Red Line. The new section enumerates exactly which 11 steps
|
|
217
|
+
24h mode auto-proceeds at (mode select / step confirm / swarm
|
|
218
|
+
pick / QA verdict / etc.) and which 6 rules it does **not**
|
|
219
|
+
bypass (Code-Change Red Line, sub-agent dispatch, worktree
|
|
220
|
+
authorization, request state machine, external side effects,
|
|
221
|
+
2.x → 1.x reverse migration). The single-sentence rule is
|
|
222
|
+
reproducible at the top of the section.
|
|
223
|
+
|
|
224
|
+
- **PR-meta-3: scope-counter fail-LOUD** (new
|
|
225
|
+
`src/services/hooks/pre-tool-scope-counter.sh`). The
|
|
226
|
+
harness-side `SCOPE WARNING: 27 files modified` was
|
|
227
|
+
informational noise that the LLM learned to ignore
|
|
228
|
+
(`.peaks/memory/2026-07-28-sub-agent-visibility-issue.md`
|
|
229
|
+
documents the same friction). PR-meta-3 turns the warning
|
|
230
|
+
into a checkpoint: the hook counts cumulative
|
|
231
|
+
application-source edits per session in
|
|
232
|
+
`.peaks/_runtime/<sid>/edit-counter.json` and emits a
|
|
233
|
+
fail-LOUD reflection reminder at thresholds 5, 10, 20. The
|
|
234
|
+
LLM cannot continue without writing to
|
|
235
|
+
`.peaks/_runtime/<sid>/scope-reflection.md` once a
|
|
236
|
+
threshold is hit. Off by default; opt in via
|
|
237
|
+
`peaks hooks install --with-scope-counter`.
|
|
238
|
+
|
|
239
|
+
- **PR-meta-4: dispatch `--emit-bash-script`** (new flag on
|
|
240
|
+
`peaks sub-agent dispatch`). The dry-run architecture is
|
|
241
|
+
preserved (the CLI is still the canonical dispatch surface),
|
|
242
|
+
but the LLM no longer has to parse `toolCall` args from the
|
|
243
|
+
JSON envelope and re-execute them via the Agent tool. The
|
|
244
|
+
flag emits a ready-to-exec bash script alongside the
|
|
245
|
+
envelope; the LLM runs the script via the Bash tool and
|
|
246
|
+
the dispatch fires immediately. The script invokes the
|
|
247
|
+
canonical `peaks sub-agent exec` surface, not a side-step.
|
|
248
|
+
- **PR-meta-5: `peaks hooks install --with-edit-enforcement /
|
|
249
|
+
--with-scope-counter`** (slice 4.0.7). The two opt-in hook
|
|
250
|
+
entries added in PR-meta-1 / PR-meta-3 are now reachable via
|
|
251
|
+
the standard install flow. Default behavior is unchanged
|
|
252
|
+
(gate-enforce only); the flags activate the meta-layer
|
|
253
|
+
hooks. The summary envelope's `entries` array now reflects
|
|
254
|
+
the actual installed shape (gate-enforce + opt-in entries)
|
|
255
|
+
so operators can dry-run + verify before applying.
|
|
256
|
+
- **PR-meta-6: `peaks sub-agent exec`** (new sub-command).
|
|
257
|
+
Companion to PR-meta-4's `--emit-bash-script`. The script
|
|
258
|
+
emitted by the dispatch CLI invokes this surface; `exec`
|
|
259
|
+
reads the dispatch record from disk and re-emits the
|
|
260
|
+
canonical `toolCall` envelope (envelopeVersion 2.4.0 with
|
|
261
|
+
`viaExec: true`). Idempotent: the same exec call can be
|
|
262
|
+
replayed any number of times. Mismatch detection: when the
|
|
263
|
+
caller-passed session-id / request-id / batch-id / role do
|
|
264
|
+
not match the record, `exec` emits a warning but still uses
|
|
265
|
+
the record as the canonical source of truth. Failure modes:
|
|
266
|
+
`DISPATCH_RECORD_MISSING`, `DISPATCH_RECORD_INVALID`,
|
|
267
|
+
`DISPATCH_RECORD_NO_TOOL_CALL`. Tests: 3 cases in
|
|
268
|
+
`tests/unit/commands/sub-agent-exec.test.ts`.
|
|
269
|
+
- **PR-meta-7: scope-counter fail-CLOSED**. The PR-meta-3
|
|
270
|
+
scope counter emitted `additionalContext` (soft signal);
|
|
271
|
+
the LLM continued editing without writing the reflection
|
|
272
|
+
file. PR-meta-7 hardens the checkpoint: at thresholds 5 /
|
|
273
|
+
10 / 20, the hook now emits `permissionDecision: deny`
|
|
274
|
+
until the LLM writes a non-empty reflection file at
|
|
275
|
+
`.peaks/_runtime/<sid>/scope-reflection.md`. The next
|
|
276
|
+
threshold requires a fresh reflection. Bypass: empty file
|
|
277
|
+
at `.peaks/standards/scope-counter-bypass` (gitignored
|
|
278
|
+
escape hatch).
|
|
279
|
+
|
|
280
|
+
### Compatibility
|
|
281
|
+
|
|
282
|
+
- `peaks hooks install` default behavior is unchanged. The
|
|
283
|
+
meta-1 / meta-3 hooks are opt-in (`--with-edit-enforcement`,
|
|
284
|
+
`--with-scope-counter`); the meta-2 SKILL.md section is
|
|
285
|
+
documentation-only; the meta-4 flag is opt-in
|
|
286
|
+
(`--emit-bash-script`).
|
|
287
|
+
- All command names, options, and exit codes are preserved.
|
|
288
|
+
- Auto-compact 0.85 / 0.95 contract unchanged.
|
|
289
|
+
- 24h mode scope is **narrowed**, not widened: same 11
|
|
290
|
+
auto-proceed steps, but the previously-implicit bypass is
|
|
291
|
+
now explicitly bounded to "AskUserQuestion gates" only.
|
|
292
|
+
|
|
293
|
+
## 4.0.6 — 2026-08-02 (postinstall auto-register outputStyle)
|
|
294
|
+
|
|
295
|
+
- **postinstall auto-registers `outputStyle: peaks-skill-swarm` into `~/.claude/settings.json`.**
|
|
296
|
+
|
|
297
|
+
Real user feedback 2026-08-02 (fresh 0-1 project on `Desktop\ticket-cross`): `npm i -g peaks-loop@latest` copied `peaks-skill-swarm.md` into `~/.claude/output-styles/` but never wrote `outputStyle` into `~/.claude/settings.json`, so Claude Code loaded the default style in new sessions.
|
|
298
|
+
|
|
299
|
+
The new `installBundledOutputStyleDefault()` helper in `scripts/install-skills.mjs`:
|
|
300
|
+
|
|
301
|
+
- reads `~/.claude/settings.json` (creates it if missing)
|
|
302
|
+
- merges `outputStyle: 'peaks-skill-swarm'` when not yet set
|
|
303
|
+
- preserves every other key (theme, env, plugins, hooks, permissions, ...)
|
|
304
|
+
- user-defined `outputStyle` wins — never overwritten
|
|
305
|
+
- malformed JSON / non-regular file / missing bundled style at the dispatched target → soft skip, never throw
|
|
306
|
+
|
|
307
|
+
Resolution precedence (settings file path):
|
|
308
|
+
|
|
309
|
+
1. `options.settingsFile` (test hook)
|
|
310
|
+
2. `PEAKS_CLAUDE_SETTINGS_FILE` env var (CI override)
|
|
311
|
+
3. `<homedir>/.claude/settings.json` (default)
|
|
312
|
+
|
|
313
|
+
Opt-out: `PEAKS_SKIP_OUTPUT_STYLE_DEFAULT=1`.
|
|
314
|
+
|
|
315
|
+
Tests: 6 new cases (4 integration + 2 direct unit) in `tests/integration/ide/install-skills-dispatch.test.ts`. `tests/integration/ide` 12/12 green; `pnpm build` → `build-integrity: OK`.
|
|
316
|
+
|
|
317
|
+
Compatibility:
|
|
318
|
+
|
|
319
|
+
- Existing user-defined `outputStyle` is preserved.
|
|
320
|
+
- Opt-out via `PEAKS_SKIP_OUTPUT_STYLE_DEFAULT=1` for CI / scripting.
|
|
321
|
+
- `~/.claude/settings.json` writes are atomic (`writeFileAtomically` + `O_NOFOLLOW`).
|
|
322
|
+
|
|
3
323
|
## 4.0.5 — 2026-08-02 (statusline polish + sub-agent merge bundle)
|
|
4
324
|
|
|
5
325
|
- Release 4.0.5 — 2026-08-01 bundle.
|
|
@@ -10,7 +10,6 @@ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
|
10
10
|
import { fail, ok } from 'peaks-loop-shared/result';
|
|
11
11
|
import { readJobShapeDecision, writeJobShapeDecision, JobShapeDecisionError, JOB_SHAPE_NOT_DECIDED, JOB_SHAPE_ALREADY_DECIDED } from '../../services/code/job-shape-decision.js';
|
|
12
12
|
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
13
|
-
import { getSkillPresence } from '../../services/skills/skill-presence-service.js';
|
|
14
13
|
export function registerCodeJobShapeCommands(code, io) {
|
|
15
14
|
// v3.1.1 Step 0.8 — Job-shape decision recorder.
|
|
16
15
|
// The CLI is a *recorder*, not a judge. The LLM supplies the verdict
|
|
@@ -148,15 +147,19 @@ export function registerCodeJobShapeCommands(code, io) {
|
|
|
148
147
|
});
|
|
149
148
|
}
|
|
150
149
|
// Local helper (was `readActiveSid` in code-commands.ts before rid-024 split).
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
150
|
+
// Slice 4.0.7-dogfood-PR-2: now uses the shared canonical resolver
|
|
151
|
+
// (`resolveActiveSessionId` from services/session) so detect-job and
|
|
152
|
+
// read-job-shape see the same session id that `peaks session info --active`
|
|
153
|
+
// returns. The pre-rid helper read `getSkillPresence` (the
|
|
154
|
+
// `.peaks/.active-skill.json` file), which was a different store from
|
|
155
|
+
// `.peaks/_runtime/session.json` — any session created via
|
|
156
|
+
// `peaks workspace init` that never had `peaks skill presence:set peaks-code`
|
|
157
|
+
// called on it (the common case for downstream consumer projects and
|
|
158
|
+
// 24h-mode sessions) reported `NO_ACTIVE_SESSION` from this file.
|
|
159
|
+
import { resolveActiveSessionId } from '../../services/session/index.js';
|
|
154
160
|
function readActiveSidForJobShape(projectRoot) {
|
|
155
161
|
try {
|
|
156
|
-
|
|
157
|
-
if (presence === null || presence === undefined)
|
|
158
|
-
return null;
|
|
159
|
-
return presence.sessionId ?? null;
|
|
162
|
+
return resolveActiveSessionId(projectRoot);
|
|
160
163
|
}
|
|
161
164
|
catch {
|
|
162
165
|
return null;
|
|
@@ -100,10 +100,22 @@ export function registerCodeModeGateCommands(code, io) {
|
|
|
100
100
|
// this only adds the structured `stale` reason + an
|
|
101
101
|
// envelope-level `stalePresence` field so downstream tooling
|
|
102
102
|
// (statusline, sub-agent dispatch) can act on it.
|
|
103
|
+
//
|
|
104
|
+
// Slice 4.0.7-dogfood-PR-3 (ice-cola surface probe 2026-08-02):
|
|
105
|
+
// 24h-mode sessions explicitly opt out of AskUserQuestion
|
|
106
|
+
// (the whole point of 24h is no-pause operation), so the
|
|
107
|
+
// stale-presence gate short-circuits to "not stale" when the
|
|
108
|
+
// session is in `24H_ACTIVE`. Without this bypass, every
|
|
109
|
+
// 24h-mode session hit a `shouldPause: true` wall at Step 1
|
|
110
|
+
// even though 24h-mode is designed to be a no-AskUserQuestion
|
|
111
|
+
// mode.
|
|
103
112
|
let stalePresence = null;
|
|
104
113
|
if (opts.step === 'step-1-mode-select' && opts.ignoreStalePresence !== true) {
|
|
105
114
|
const projectRoot = opts.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
106
|
-
|
|
115
|
+
const sessionIdForGate = readActiveSidForModeGate(projectRoot);
|
|
116
|
+
if (!is24hActive(projectRoot, sessionIdForGate)) {
|
|
117
|
+
stalePresence = checkStalePresence({ projectRootOverride: projectRoot });
|
|
118
|
+
}
|
|
107
119
|
}
|
|
108
120
|
if (stalePresence !== null && stalePresence.stale) {
|
|
109
121
|
// Build the envelope manually so we can attach the extra
|
|
@@ -209,9 +221,22 @@ export function registerCodeModeGateCommands(code, io) {
|
|
|
209
221
|
// The mode-gate stale-presence check needs the active sid for the
|
|
210
222
|
// observability event; we re-import getSkillPresence here to avoid the
|
|
211
223
|
// cross-file helper import.
|
|
224
|
+
// Slice 4.0.7-dogfood-PR-2: also uses the shared canonical resolver
|
|
225
|
+
// (resolveActiveSessionId) so the same id flows through detect-job,
|
|
226
|
+
// read-job-shape, and should-pause. The pre-rid helper read
|
|
227
|
+
// `getSkillPresence` (the `.peaks/.active-skill.json` file) which
|
|
228
|
+
// was a different store from `.peaks/_runtime/session.json`.
|
|
212
229
|
import { getSkillPresence } from '../../services/skills/skill-presence-service.js';
|
|
230
|
+
import { resolveActiveSessionId } from '../../services/session/index.js';
|
|
231
|
+
import { read24hState } from '../../services/24h-mode/store.js';
|
|
213
232
|
function readActiveSidForModeGate(projectRoot) {
|
|
233
|
+
// Prefer the canonical binding (works for any session created via
|
|
234
|
+
// peaks workspace init); fall back to skill presence for sub-skill
|
|
235
|
+
// activations that never wrote a binding (legacy edge case).
|
|
214
236
|
try {
|
|
237
|
+
const canonical = resolveActiveSessionId(projectRoot);
|
|
238
|
+
if (canonical !== null)
|
|
239
|
+
return canonical;
|
|
215
240
|
const presence = getSkillPresence(projectRoot);
|
|
216
241
|
if (presence === null || presence === undefined)
|
|
217
242
|
return null;
|
|
@@ -221,3 +246,20 @@ function readActiveSidForModeGate(projectRoot) {
|
|
|
221
246
|
return null;
|
|
222
247
|
}
|
|
223
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Slice 4.0.7-dogfood-PR-3: returns true when the active session is
|
|
251
|
+
* in 24H_ACTIVE state (per `.peaks/_runtime/<sid>/24h-state.json`).
|
|
252
|
+
* In that state, the mode-gate's stale-presence check is a false
|
|
253
|
+
* positive — 24h-mode explicitly opts out of AskUserQuestion.
|
|
254
|
+
*/
|
|
255
|
+
function is24hActive(projectRoot, sessionId) {
|
|
256
|
+
if (sessionId === null || sessionId.length === 0)
|
|
257
|
+
return false;
|
|
258
|
+
try {
|
|
259
|
+
const snap = read24hState(projectRoot, sessionId);
|
|
260
|
+
return snap.state === '24H_ACTIVE';
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -13,7 +13,6 @@ import { runAutoCompact } from '../../services/code/auto-compact-orchestrator.js
|
|
|
13
13
|
import { evaluateStep08, STEP_08_BACKUP_REGEX } from '../../services/code/step-08-gate.js';
|
|
14
14
|
import { evaluateEmitHandoff, JOB_NOT_INITIALIZED, JOB_REMAINING_BLOCKED } from '../../services/code/emit-handoff.js';
|
|
15
15
|
import { readJobShapeDecision, JobShapeDecisionError } from '../../services/code/job-shape-decision.js';
|
|
16
|
-
import { getSkillPresence } from '../../services/skills/skill-presence-service.js';
|
|
17
16
|
export function registerCodeRuntimeCommands(code, io) {
|
|
18
17
|
addJsonOption(code
|
|
19
18
|
.command('post-compact-detect')
|
|
@@ -391,14 +390,20 @@ export function registerCodeRuntimeCommands(code, io) {
|
|
|
391
390
|
// Local helper (was `readActiveSid` in code-commands.ts before rid-024 split).
|
|
392
391
|
// Only the 5 runtime probes above use it; keeping it local avoids the
|
|
393
392
|
// cross-file helper import.
|
|
393
|
+
// Slice 4.0.7-dogfood-PR-2: now uses the shared canonical resolver
|
|
394
|
+
// (`resolveActiveSessionId` from services/session) so context-now /
|
|
395
|
+
// post-compact-detect / auto-compact / gate-step-08 / emit-handoff see
|
|
396
|
+
// the same session id that `peaks session info --active` returns.
|
|
397
|
+
// The pre-rid helper read `getSkillPresence` (the
|
|
398
|
+
// `.peaks/.active-skill.json` file), which was a different store from
|
|
399
|
+
// `.peaks/_runtime/session.json` — the two were kept in sync only when
|
|
400
|
+
// `peaks skill presence:set` was called explicitly.
|
|
401
|
+
import { resolveActiveSessionId } from '../../services/session/index.js';
|
|
394
402
|
function readActiveSid(projectRoot) {
|
|
395
403
|
try {
|
|
396
|
-
|
|
397
|
-
if (presence === null || presence === undefined)
|
|
398
|
-
return null;
|
|
399
|
-
return presence.sessionId ?? null;
|
|
404
|
+
return resolveActiveSessionId(projectRoot);
|
|
400
405
|
}
|
|
401
|
-
catch {
|
|
406
|
+
catch {
|
|
402
407
|
return null;
|
|
403
408
|
}
|
|
404
409
|
}
|
|
@@ -2,8 +2,37 @@ import { summarizeProjectStandardsInitResult, summarizeProjectStandardsUpdateRes
|
|
|
2
2
|
import { executeProjectStandardsInitIdeAware, executeProjectStandardsUpdateIdeAware } from '../../../services/standards/ide-aware-standards-service.js';
|
|
3
3
|
import { migrateStandards } from '../../../services/standards/migrate-service.js';
|
|
4
4
|
import { migrateClaudeRules } from '../../../services/standards/migrate-claude-rules-service.js';
|
|
5
|
+
import { detectProjectContext } from '../../../services/standards/project-context.js';
|
|
6
|
+
import { findSkillsForContext } from '../../../services/standards/find-skills-integration.js';
|
|
5
7
|
import { fail, ok } from 'peaks-loop-shared/result';
|
|
6
8
|
import { addJsonOption, getErrorMessage, printResult } from '../../cli-helpers.js';
|
|
9
|
+
/**
|
|
10
|
+
* Slice 4.0.7-PR-12 helper: render `npx skills find` results as
|
|
11
|
+
* next-action strings the user can paste. Never auto-installs.
|
|
12
|
+
* Returns an empty array when the network call fails (so the
|
|
13
|
+
* init/update flow never blocks on find-skills).
|
|
14
|
+
*/
|
|
15
|
+
async function buildFindSkillsNextActions(projectRoot) {
|
|
16
|
+
try {
|
|
17
|
+
const ctx = detectProjectContext(projectRoot);
|
|
18
|
+
const result = await findSkillsForContext(ctx, { topN: 5 });
|
|
19
|
+
if (result.recommendations.length === 0) {
|
|
20
|
+
return [`peaks-loop (PR-12): no matching skills found for this project stack; try \`npx skills find <query>\` manually.`];
|
|
21
|
+
}
|
|
22
|
+
const lines = ['peaks-loop (PR-12) skill recommendations (verified-owner first; install commands are copy-paste, never auto-run):'];
|
|
23
|
+
for (const r of result.recommendations) {
|
|
24
|
+
const count = r.installCount === null ? '?' : `${r.installCount.toLocaleString()} installs`;
|
|
25
|
+
lines.push(` ${r.displayName} (${r.owner}, ${count}) — ${r.installCommand}`);
|
|
26
|
+
}
|
|
27
|
+
if (result.failedRuns.length > 0) {
|
|
28
|
+
lines.push(`peaks-loop (PR-12): ${result.failedRuns.length} find-skills run(s) failed (network or registry). Re-run \`npx skills find <query>\` to retry.`);
|
|
29
|
+
}
|
|
30
|
+
return lines;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
7
36
|
export function registerStandardsCommand(program, io) {
|
|
8
37
|
const standards = program.command('standards').description('Manage project-local coding standards');
|
|
9
38
|
addJsonOption(standards
|
|
@@ -13,7 +42,8 @@ export function registerStandardsCommand(program, io) {
|
|
|
13
42
|
.option('--language <language>', 'standards language pack')
|
|
14
43
|
.option('--ide <id>', 'override IDE detection (e.g. claude-code, trae)')
|
|
15
44
|
.option('--dry-run', 'preview writes without changing files')
|
|
16
|
-
.option('--apply', 'write missing standards into the target project')
|
|
45
|
+
.option('--apply', 'write missing standards into the target project')
|
|
46
|
+
.option('--suggest-skills', 'Slice 4.0.7-PR-12: after init, query the open agent-skills ecosystem for matching skills and emit install commands. Off by default. Never auto-installs — user pastes the command.')).action(async (options) => {
|
|
17
47
|
if (options.dryRun === true && options.apply === true) {
|
|
18
48
|
printResult(io, fail('standards.init', 'INVALID_STANDARDS_INIT_FLAGS', 'Use either --dry-run or --apply, not both', {}, ['Run without --apply to preview writes, or omit --dry-run when applying standards']), options.json);
|
|
19
49
|
process.exitCode = 1;
|
|
@@ -21,7 +51,17 @@ export function registerStandardsCommand(program, io) {
|
|
|
21
51
|
}
|
|
22
52
|
try {
|
|
23
53
|
const result = executeProjectStandardsInitIdeAware({ projectRoot: options.project, ...(options.language !== undefined ? { language: options.language } : {}), ...(options.ide !== undefined ? { ideId: options.ide } : {}), apply: options.apply === true });
|
|
24
|
-
|
|
54
|
+
const summary = summarizeProjectStandardsInitResult(result);
|
|
55
|
+
// Slice 4.0.7-PR-12: optional find-skills hint. Always
|
|
56
|
+
// human-gated (we never invoke `npx skills add`); the user
|
|
57
|
+
// pastes the install command. The slice is geared at
|
|
58
|
+
// peaks-loop downstream projects — every project that runs
|
|
59
|
+
// `peaks standards init` gets the same hint when the flag is
|
|
60
|
+
// set, so consumers do not need to remember find-skills.
|
|
61
|
+
const suggestNextActions = options.suggestSkills === true
|
|
62
|
+
? await buildFindSkillsNextActions(options.project)
|
|
63
|
+
: [];
|
|
64
|
+
printResult(io, ok('standards.init', summary, [], suggestNextActions), options.json);
|
|
25
65
|
}
|
|
26
66
|
catch (error) {
|
|
27
67
|
printResult(io, fail('standards.init', 'STANDARDS_INIT_FAILED', getErrorMessage(error), {}, ['Check the project path and existing .claude/rules directory before retrying']), options.json);
|
|
@@ -35,7 +75,8 @@ export function registerStandardsCommand(program, io) {
|
|
|
35
75
|
.option('--language <language>', 'standards language pack')
|
|
36
76
|
.option('--ide <id>', 'override IDE detection (e.g. claude-code, trae)')
|
|
37
77
|
.option('--dry-run', 'preview writes without changing files')
|
|
38
|
-
.option('--apply', 'append managed metadata to the target project')
|
|
78
|
+
.option('--apply', 'append managed metadata to the target project')
|
|
79
|
+
.option('--suggest-skills', 'Slice 4.0.7-PR-12: after update, refresh skill recommendations for the project stack. Off by default.')).action(async (options) => {
|
|
39
80
|
if (options.dryRun === true && options.apply === true) {
|
|
40
81
|
printResult(io, fail('standards.update', 'INVALID_STANDARDS_UPDATE_FLAGS', 'Use either --dry-run or --apply, not both', {}, ['Run without --apply to preview writes, or omit --dry-run when applying standards updates']), options.json);
|
|
41
82
|
process.exitCode = 1;
|
|
@@ -44,9 +85,13 @@ export function registerStandardsCommand(program, io) {
|
|
|
44
85
|
try {
|
|
45
86
|
const result = executeProjectStandardsUpdateIdeAware({ projectRoot: options.project, ...(options.language !== undefined ? { language: options.language } : {}), ...(options.ide !== undefined ? { ideId: options.ide } : {}), apply: options.apply === true });
|
|
46
87
|
const summary = summarizeProjectStandardsUpdateResult(result);
|
|
88
|
+
const suggestNextActions = options.suggestSkills === true
|
|
89
|
+
? await buildFindSkillsNextActions(options.project)
|
|
90
|
+
: [];
|
|
91
|
+
const allNextActions = [...summary.reviewSuggestions, ...suggestNextActions];
|
|
47
92
|
const response = summary.reviewSuggestions.length > 0
|
|
48
|
-
? fail('standards.update', 'STANDARDS_UPDATE_REVIEW_REQUIRED', 'Standards update requires manual review', summary,
|
|
49
|
-
: ok('standards.update', summary);
|
|
93
|
+
? fail('standards.update', 'STANDARDS_UPDATE_REVIEW_REQUIRED', 'Standards update requires manual review', summary, allNextActions)
|
|
94
|
+
: ok('standards.update', summary, [], suggestNextActions);
|
|
50
95
|
printResult(io, response, options.json);
|
|
51
96
|
if (summary.reviewSuggestions.length > 0) {
|
|
52
97
|
process.exitCode = 1;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* envelope's `nextActions` reinforce the point.
|
|
16
16
|
*/
|
|
17
17
|
import { existsSync } from 'node:fs';
|
|
18
|
+
import { join } from 'node:path';
|
|
18
19
|
import { randomUUID } from 'node:crypto';
|
|
19
20
|
import { spawn as childProcessSpawn } from 'node:child_process';
|
|
20
21
|
import { fail, getErrorMessage, ok } from 'peaks-loop-shared/result';
|
|
@@ -37,7 +38,7 @@ import { DEFAULT_PREFERENCES } from '../../services/preferences/preferences-type
|
|
|
37
38
|
import { writeLogEntry } from '../../services/log/logger.js';
|
|
38
39
|
import { HEADROOM_MODES, PROMPT_LIMIT_BYTES, RECOMMENDED_ROLES, validateRole } from './sub-agent-shared.js';
|
|
39
40
|
import { runDispatchFromDag } from './dispatch-from-dag.js';
|
|
40
|
-
import { TEST_TOOL_DETECTION_BLOCK, formatTestToolDetection } from '../../services/dispatch/test-tool-detection.js';
|
|
41
|
+
import { TEST_TOOL_DETECTION_BLOCK, MONOREPO_TEST_TOOL_BLOCK, formatTestToolDetection } from '../../services/dispatch/test-tool-detection.js';
|
|
41
42
|
import { MemoryPreflightService } from '../../services/context/memory-preflight-service.js';
|
|
42
43
|
import { buildDispatchSystemPrompt } from '../../services/context/build-dispatch-system-prompt.js';
|
|
43
44
|
import { createDispatchProvenanceToken, DISPATCH_PROVENANCE_ENV, writeDispatchProvenance, } from '../../services/worktree/dispatch-provenance.js';
|
|
@@ -67,7 +68,8 @@ export function registerDispatchCommand(parent, io) {
|
|
|
67
68
|
.option('--headroom-mode <mode>', `G7.7: headroom mode (${HEADROOM_MODES.join(' | ')}); default balanced`)
|
|
68
69
|
.option('--force', 'G9: override the 80% hard reject threshold at CLI (NOT allowed at hook layer per RL-30 strict)')
|
|
69
70
|
.option('--from-dag <file>', '2.7.0 slice-dag-dispatcher MVP: read a SliceDag JSON file, dispatch one sub-agent per node in topological order; --batch-id overrides the auto-generated batch id (mutually exclusive with <role>)')
|
|
70
|
-
.option('--isolation <mode>', 'slice 2026-07-29-worktree-l2-extended Part 2.C: isolation mode for the sub-agent. Accepts "worktree" (Part 2.C + Part 12 L2 surface), "container" (Part 8 contract + Part 12 L4 docker runtime), or "vm" (Part 25 contract; the VM runtime is a follow-up rid and fail-fasts with ISOLATION_VM_NOT_YET_IMPLEMENTED). Auto-spawns a lease + injects PEAKS_<MODE>_LEASE_ID into the dispatch envelope so the sub-agent can write to the isolated surface without a separate auth grant.')
|
|
71
|
+
.option('--isolation <mode>', 'slice 2026-07-29-worktree-l2-extended Part 2.C: isolation mode for the sub-agent. Accepts "worktree" (Part 2.C + Part 12 L2 surface), "container" (Part 8 contract + Part 12 L4 docker runtime), or "vm" (Part 25 contract; the VM runtime is a follow-up rid and fail-fasts with ISOLATION_VM_NOT_YET_IMPLEMENTED). Auto-spawns a lease + injects PEAKS_<MODE>_LEASE_ID into the dispatch envelope so the sub-agent can write to the isolated surface without a separate auth grant.')
|
|
72
|
+
.option('--emit-bash-script', 'Slice 4.0.7-PR-meta-4: in addition to the JSON envelope, emit a ready-to-exec bash script that the LLM (or the user) can run via the Bash tool. The script invokes the Task tool\'s underlying sub-agent runtime. Lowers the dispatch friction documented in `.peaks/memory/2026-07-28-sub-agent-visibility-issue.md` (the LLM no longer has to manually extract toolCall args from the envelope). The dry-run architecture is preserved: the script is a one-shot executor, not a side-step.')).action(async (role, options) => {
|
|
71
73
|
const asJson = options.json === true;
|
|
72
74
|
// 2.7.0 slice-dag-dispatcher MVP: --from-dag short-circuits the single
|
|
73
75
|
// sub-agent path and runs the full DAG plan via `dag-orchestrator`.
|
|
@@ -365,7 +367,14 @@ export function registerDispatchCommand(parent, io) {
|
|
|
365
367
|
`branch: ${worktreeBranch}\n` +
|
|
366
368
|
`You MAY ` + '`git worktree add` ' + `and ` + '`git worktree remove` ' + `against this lease without a separate ` + '`peaks worktree auth grant` ' + `— the PreToolUse gate reads the lease file. Run ` + '`peaks worktree release --lease-id ${leaseId}` ' + `when done.\n`
|
|
367
369
|
: '';
|
|
368
|
-
|
|
370
|
+
// Slice 4.0.7-dogfood-PR-9: when pnpm-workspace.yaml is present,
|
|
371
|
+
// append the monorepo test-tool block so the sub-agent scopes
|
|
372
|
+
// to a single sub-package instead of fanning out to the whole
|
|
373
|
+
// monorepo. The block is empty/cheap when the project is a
|
|
374
|
+
// single package (no pnpm-workspace.yaml).
|
|
375
|
+
const isMonorepo = existsSync(join(projectRoot, 'pnpm-workspace.yaml'));
|
|
376
|
+
const monorepoBlock = isMonorepo ? `\n\n${MONOREPO_TEST_TOOL_BLOCK}` : '';
|
|
377
|
+
let effectivePrompt = `${formatTestToolDetection()}${monorepoBlock}\n\n${memoryAugmentedBody}${isolationBlock}`;
|
|
369
378
|
let headroomCompressed = false;
|
|
370
379
|
let headroomResult = null;
|
|
371
380
|
const warnings = [...decision.warnings];
|
|
@@ -584,6 +593,33 @@ export function registerDispatchCommand(parent, io) {
|
|
|
584
593
|
worktreePath,
|
|
585
594
|
worktreeBranch
|
|
586
595
|
}, warnings, nextActions), asJson);
|
|
596
|
+
// Slice 4.0.7-PR-meta-4: --emit-bash-script prints a one-shot
|
|
597
|
+
// bash script to stdout that the LLM (or the user) can run
|
|
598
|
+
// directly via the Bash tool. The script invokes the
|
|
599
|
+
// canonical `peaks sub-agent exec` surface (a new CLI in
|
|
600
|
+
// this slice) that re-runs the dispatch with the same
|
|
601
|
+
// dispatchRecordPath, sid, role, batch-id, rid, and
|
|
602
|
+
// prompt-size, so the LLM does not have to parse toolCall
|
|
603
|
+
// args from the JSON envelope and re-execute them. The
|
|
604
|
+
// dry-run architecture is preserved: the CLI is the
|
|
605
|
+
// canonical dispatch surface; the script is a one-shot
|
|
606
|
+
// executor, not a side-step.
|
|
607
|
+
if (options.emitBashScript === true) {
|
|
608
|
+
const script = [
|
|
609
|
+
'#!/usr/bin/env bash',
|
|
610
|
+
`# Slice 4.0.7-PR-meta-4: dispatch executor for rid=${rid} role=${role} batch=${batchId}`,
|
|
611
|
+
`# Generated by \`peaks sub-agent dispatch --emit-bash-script\`. Re-invokes the`,
|
|
612
|
+
'# canonical peaks sub-agent exec surface; safe to run via Bash tool.',
|
|
613
|
+
`peaks sub-agent exec \\`,
|
|
614
|
+
` --session-id "${sid}" \\`,
|
|
615
|
+
` --request-id "${rid}" \\`,
|
|
616
|
+
` --role "${role}" \\`,
|
|
617
|
+
` --batch-id "${batchId}" \\`,
|
|
618
|
+
` --dispatch-record "${dispatchRecordPath}" \\`,
|
|
619
|
+
` --project "${projectRoot}"`
|
|
620
|
+
].join('\n');
|
|
621
|
+
process.stdout.write(`\n# === peaks sub-agent dispatch — ready-to-exec script (slice 4.0.7-PR-meta-4) ===\n${script}\n# === end script ===\n`);
|
|
622
|
+
}
|
|
587
623
|
// Slice 2026-06-23-audit-4th #B1: structured log on success path.
|
|
588
624
|
// Best-effort: writeLogEntry swallows its own errors (logger.ts:155-159),
|
|
589
625
|
// so a full disk or missing ~/.peaks/logs/ dir never blocks the dispatch.
|