peaks-loop 4.0.46 → 4.0.48
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 +54 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/agents/karpathy-reviewer.md +11 -10
- package/dist/cli/cli-helpers.d.ts +34 -0
- package/dist/cli/cli-helpers.js +57 -0
- package/dist/cli/commands/code-job-shape-commands.js +8 -0
- package/dist/cli/commands/code-runtime-commands.d.ts +22 -0
- package/dist/cli/commands/code-runtime-commands.js +139 -16
- package/dist/cli/commands/compact-command.js +241 -1
- package/dist/cli/commands/config-commands.js +15 -9
- 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/dashboard-long-run.js +6 -0
- package/dist/cli/commands/dispatch-commands.js +11 -1
- package/dist/cli/commands/doctor/invoke-from-code.js +6 -0
- 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 +14 -5
- package/dist/cli/commands/job-commands.js +8 -0
- package/dist/cli/commands/loop-commands.js +1 -0
- package/dist/cli/commands/loop-eval-commands.js +15 -0
- package/dist/cli/commands/perf-audit-commands.js +2 -0
- package/dist/cli/commands/playwright-commands.js +14 -1
- package/dist/cli/commands/prd-commands.js +1 -1
- package/dist/cli/commands/qa-commands.js +22 -0
- 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 +14 -3
- package/dist/cli/commands/scan-commands.js +1 -1
- package/dist/cli/commands/security-audit-commands.js +2 -0
- package/dist/cli/commands/shadcn-commands.js +1 -0
- package/dist/cli/commands/slice-integrate-commands.js +5 -0
- package/dist/cli/commands/statusline-commands.js +44 -4
- package/dist/cli/commands/sub-agent/detached.d.ts +14 -1
- package/dist/cli/commands/sub-agent/detached.js +47 -22
- package/dist/cli/commands/sub-agent-shutdown-commands.js +11 -0
- package/dist/cli/commands/test-commands.js +2 -1
- package/dist/cli/commands/verdict-aggregate-command.js +95 -13
- package/dist/cli/commands/vm-commands.js +7 -7
- package/dist/cli/commands/workflow-commands.js +1 -1
- 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 +10 -3
- 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-prerequisites.d.ts +38 -7
- package/dist/services/artifacts/artifact-prerequisites.js +130 -65
- package/dist/services/artifacts/artifact-service.js +1 -1
- package/dist/services/artifacts/request-artifact-service.d.ts +8 -0
- package/dist/services/artifacts/request-artifact-service.js +18 -8
- package/dist/services/artifacts/request-artifact-state-helpers.d.ts +57 -0
- package/dist/services/artifacts/request-artifact-state-helpers.js +91 -10
- package/dist/services/audit-independent/perf-audit-service.d.ts +9 -0
- package/dist/services/audit-independent/perf-audit-service.js +27 -5
- package/dist/services/audit-independent/security-audit-service.d.ts +12 -2
- package/dist/services/audit-independent/security-audit-service.js +28 -6
- 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 +130 -1
- package/dist/services/code/auto-compact-lifecycle.js +180 -4
- package/dist/services/code/auto-compact-orchestrator.d.ts +53 -9
- package/dist/services/code/auto-compact-orchestrator.js +166 -34
- package/dist/services/code/compact-event-settle.d.ts +122 -0
- package/dist/services/code/compact-event-settle.js +219 -0
- 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 +75 -0
- package/dist/services/compact-history/compact-history-service.js +49 -0
- package/dist/services/config/config-restore.d.ts +12 -1
- package/dist/services/config/config-restore.js +35 -4
- package/dist/services/config/config-rollback.js +6 -1
- 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-context-witness.d.ts +310 -0
- package/dist/services/context/harness-context-witness.js +606 -0
- 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/evidence/evidence-generator.js +86 -49
- package/dist/services/final-review/final-review-service.d.ts +9 -0
- package/dist/services/final-review/final-review-service.js +36 -12
- 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 +31 -0
- package/dist/services/ide/ide-registry.js +35 -0
- package/dist/services/ide/ide-types.d.ts +59 -0
- package/dist/services/job/job-state-store.js +7 -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/polyrepo/polyrepo-dispatcher.js +11 -0
- package/dist/services/prd/best-practice-auto-trigger.js +1 -0
- package/dist/services/prd/handoff-auto-regen.js +31 -27
- package/dist/services/prd/handoff-frontmatter.d.ts +44 -0
- package/dist/services/prd/handoff-frontmatter.js +75 -0
- package/dist/services/prd/handoff-service.d.ts +41 -2
- package/dist/services/prd/handoff-service.js +81 -8
- package/dist/services/prd/handoff-types.d.ts +3 -2
- package/dist/services/prd/handoff-types.js +3 -2
- package/dist/services/qa/qa-business-review-state.js +9 -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/karpathy-service.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/session/session-checkpoint-service.js +8 -0
- package/dist/services/skill/resume-detector.js +29 -11
- package/dist/services/skillhub/tar-runtime.js +1 -0
- package/dist/services/skills/hooks-codegate-superpowers.d.ts +14 -0
- package/dist/services/skills/hooks-codegate-superpowers.js +99 -3
- package/dist/services/skills/hooks-settings-service.d.ts +12 -0
- package/dist/services/skills/hooks-settings-service.js +91 -14
- package/dist/services/skills/session-start-hook-constants.d.ts +86 -0
- package/dist/services/skills/session-start-hook-constants.js +86 -0
- package/dist/services/skills/skill-presence-service.js +9 -0
- package/dist/services/skills/skill-statusline-service.d.ts +14 -0
- package/dist/services/slice/slice-check-service.js +31 -12
- package/dist/services/slice/slice-decompose-runners.js +2 -1
- package/dist/services/slice/slice-review-state.js +8 -0
- package/dist/services/upgrade/upgrade-service.js +1 -0
- package/dist/services/workflow/pipeline-verify-gate-support.d.ts +47 -10
- package/dist/services/workflow/pipeline-verify-gate-support.js +212 -93
- package/dist/services/workflow/pipeline-verify-service.js +24 -23
- package/dist/services/workflow/pipeline-verify-types.d.ts +10 -3
- package/dist/services/workflow/workflow-skip-service.js +2 -1
- package/dist/services/workspace/claude-settings-template.d.ts +56 -8
- package/dist/services/workspace/claude-settings-template.js +98 -20
- package/dist/services/workspace/migrate-service.js +1 -1
- package/dist/services/workspace/workspace-claude-settings-materializer.js +124 -9
- 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 +6 -6
- 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 +8 -6
- package/skills/bee/peaks-qa/SKILL.md +7 -7
- package/skills/bee/peaks-qa/references/qa-runbook.md +2 -2
- package/skills/bee/peaks-qa/references/qa-transition-gates.md +7 -7
- package/skills/bee/peaks-rd/SKILL.md +10 -8
- package/skills/bee/peaks-rd/references/artifact-per-request.md +2 -2
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -5
- package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +13 -13
- package/skills/bee/peaks-rd/references/rd-runbook.md +9 -5
- package/skills/bee/peaks-rd/references/rd-transition-gates.md +9 -7
- package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +6 -6
- 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 +3 -3
- package/skills/peaks-code/references/a2a-artifact-mapping.md +3 -3
- package/skills/peaks-code/references/local-artifact-workspace.md +1 -1
- package/skills/peaks-code/references/resume-detection.md +13 -7
- package/skills/peaks-code/references/runbook.md +3 -2
- package/skills/peaks-code/references/session-overload-signal-index.md +2 -1
- package/skills/peaks-code/references/sub-agent-dispatch.md +1 -1
- package/skills/peaks-code/references/workflow-gates-and-types.md +8 -6
- 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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.48 — 2026-09-14 (一个 id 没校验就进了路径 + 自己的消费者读不出的交接胶囊 + 结尾才写、开头就读的状态)
|
|
4
|
+
|
|
5
|
+
**Highlights**:
|
|
6
|
+
|
|
7
|
+
1. **一个 caller 提供的 id 没经校验就进了路径;这次是当作一类来 instrument,不是逐点打补丁。** session / request / job / change 这些 id 会被拼进产物路径,本仓库**对这件事恰好只有一种正确控制**,而**每个拼接点各自重新决定要不要用它** —— 其中一个没决定,CLI 就写到项目根之外,**并照样返回 `ok: true`**。上一轮量到六处这类点位;这一轮**先给探测装上自检**:在逃逸根植一个哨兵、要求猎手找到它,否则每一个 "escaped=no" 都一文不值 —— 关掉守卫同一探测报 **6/6 逃逸**,恢复后 **0/6**(上一轮的第一次扫描往上走了五层、对十四个用例全报"无逃逸",而逃逸目录就在盘上)。修复是**扩展仓库已有的 AST 守卫**而不是新写扫描器:射程从 0 涨到 `src/` 下 **152 个文件**,灵敏度用同样方式证明(去掉一个守卫它就失败并点名站点,恢复即通过),它的盲区被**精确记录而不是放宽**(`_runtime` 之后是钉死字面量时该站点对它不可见;七个这样的拼接点现在全部点名,一个钉死的普查测试让新增的那个失败)。instrument 找到最严重的一处:**`playwright stop --terminal ../../../../X` 返回 `ok: true`,同时 TERMINATE 了它点名的 pid、并 UNLINK 了项目根之外的一个文件** —— 一个 flag 就拿到任意 SIGTERM 加一次删除。它逃逸有两个原因,都值得记:那一行分析的是 `playwrightSessionsDir()`(其 `_runtime` 槽确实是钉死字面量),而 id 是在**另一个不含任何 `_runtime` 字面量的调用**里拼的;以及**两次扫描从未真的执行过 `playwright`** —— 一个否定结论不会留下"背后到底执行过什么"的痕迹。现在在三个会话操作共用的那个调用处设防,并配一个控制组证明**合法 id 仍然会 kill 和 unlink** —— 选择性的,不是一刀切拒绝。
|
|
8
|
+
|
|
9
|
+
2. **交接胶囊自己的消费者读不出来。** `peaks prd handoff init` 发出的是 `schemaVersion: "2"` 与裸 `handoffHash:`,而 `rd:qa-handoff` 闸要字面 `schemaVersion: 2` 加 `sha256:`,两个 audit loader 又要锚定的 `^schemaVersion:\s*(\d+)\s*$` —— **三个消费者、四条要求,而规范写入者一条都不满足**。上一轮会话精确诊断过这一点,却把分析写进了自己的 gitignored `_runtime` 树,于是下一轮从零重新发现了一遍。这次**修写入者而不是放宽闸**:没有任何子串编辑能教会一个锚定正则去读带引号的值,而一个接受 loader 仍然拒绝之物的闸,只是把分歧挪到闸后面、并且更安静。同一版另有**三处"两个组件对同一件事各说各话"**:`verify-pipeline` 索要 `rd/security-review.md`,而它本该镜像的表早已移到 `audit/security.md`,还索要两个 v2.11.0 已退役的 QA findings 文件 —— 于是**通过 `request transition` 的切片会在下一个检查里失败**;现在要求集从表自己的 tier 顺序经 `getPrerequisitesFor()` 派生。**状态读取者与写入者对"哪一行是权威"看法相反**:`request transition` 在**末尾**追加权威 Status 块,三个读取者取**第一个**匹配 —— 在 QA 追加过四次的文件上,检查器报 `qa-block` 而写入者写的是 `verdict-issued`;现在所有读取者共用一个定位器,**69 个在盘产物经新旧两条路径读出的结果全等,而两者只在那个错的上面不同**,顺手扫出的第三个 first-match 读取者曾把一份被追加过的 PRD 判成 `in-flight` 而非 `resume`。**同一会话里的两个切片静默互相覆盖审计证据**:受闸的产物路径不带 rid,第二个切片的复核替换掉第一个的 —— 而闸保持绿,因为它检查存在与形状,**从不检查归属**;一位审计者注意到并保留了文件、另一位没有,**这个差别是运气不是纪律**。路径现在按 rid 划界、legacy 层留作回退,经 **62 组真实 session×rid 对**验证。
|
|
10
|
+
|
|
11
|
+
3. **压缩触发点第一次有了目击者,而不是从"ratio 跌没跌"反推。** harness 每次压缩都会广播(`PreCompact` 带 `trigger: manual|auto`,`PostCompact` 在完成后触发),而 peaks-loop 从没在听 —— 它靠"下一个探针发现 ratio 掉了"来推断"发生过压缩"。于是本仓库存在的理由(标定)最想回答的那个问题,**"是 harness 自己压的,还是有人敲了 `/compact`"**,在这里**根本没有目击者**;实测本机全部 transcript:`compactMetadata.trigger` 读到 `manual x2`、`auto x0`。新增 `PostCompact` 钩子条目,事件到达当刻就结算打开的 run,并把 harness 自己的 trigger 写进观测行(**没有打开的 run 的压缩仍然不留痕迹 —— 归因不是证据**)。三件事是测量逼出来的:事件路径**不能把"lifecycle 写失败"报成"没有 run"**(`null` 现在只表示一个意思;写失败连 `lifecycleWritten: false` 一起返回 facts,观测行照写,因为那行是**关于 harness 说了什么**、不是关于我们写没写进去);payload 的 `session_id` 与自己的外层会话 id 比对,**子代理发起的事件不能结算主会话的 run**,而字段缺失或自己的 id 解析不出时**接受** —— 一个看不见自己名字的守卫不能变成死钩子;被事件结算的压缩**仍要闭合它的标定对**,填空推迟到下一个测到低于该 run 自身触发点的探针,**是推迟不是封死**。matcher 用空串而不是文档写的 `auto|manual`:两族钩子的 matcher 语义不同(`PreToolUse` 是工具名正则,`SessionStart` 的 `'compact'` 是精确来源标签),证据支持的是精确相等,空串是唯一不可能"永不触发"的形式。姊妹项把"两个窗口是不是同一个数"**从推断变成比对**:`used_percentage` 在 `src/` 里此前**零消费者**,现在默认渲染器把它捕获进会话级 ledger、`context-now` 拿自己的 ratio 与 harness 的百分比比对。三个设计点承担了风险:两个"窗口"**不是一个字段**(比的是 ratio 对 `used_percentage`,**绝不是窗口对窗口** —— 直接比会给出一个自信的错答案);**容差是论证出来的不是选出来的**(贡献项是 harness 的整数百分比取整、分子口径、以及两个在不同时刻算出的数之间的采样偏斜,偏斜被**减掉**而不是预算 —— 预算会让预算涨 x 而偏差涨 x(1+g),真实 gap 被自己的偏斜抵消);**锐度闸测的是目击者的 ratio 而不是 peaks-loop 的**(旧版闸错了对象,于是真实 3.3% 的 gap 在 r=0.2 处读成 `agree`,恰好落在实测采样偏斜的位置)。验收是一次 **274,188 次比对**的扫描、其最坏被掩蔽的 `agree` 是**空集**,同一扫描对旧闸失败。判决有三个而不是两个:`agree` / `disagree` / `unverifiable` —— 两答案的守卫分不清"正确地沉默"和"坏了"。
|
|
12
|
+
|
|
13
|
+
4. **上一版自己点名的"尚未处理"被兑现;其中两件的前提被实测证伪,而纠正前提比修复本身更重要。**
|
|
14
|
+
|
|
15
|
+
- **"模板只拥有它声明的条目。"** 4.0.47 把 settings materializer 的整键 `hooks` 所有权记为**潜伏**问题("今天没有东西走那条路")—— **这是错的,而且有一条活路径**:`installAutoCompactHook` 往那个文件里写 `Bash|Task` 条目,而 `peaks workspace init` 拥有整个键。实测:init → 装好 auto-compact(4 条)→ 再 init → **REFRESHED,3 条,`Bash|Task survived? false`** —— 而**没有任何东西会把它装回去,于是 auto-compact 静默停止**,刷新只报成 `refreshed`。现在模板只拥有它**声明**的条目(按 matcher 键控 + 每 matcher 配额),自己的一律排在最前,其余在盘条目**逐字保留**(未声明的 matcher、超出配额的条目、未声明的事件、不合规的条目);会被漏掉的那一半也修了 —— `templateContentMatches` 必须一起改成**多重集包含**,否则合并之后它会永远拿 3 条模板去比 4 条在盘、于是每次 init 都报 `refreshed`,正是整键所有权存在时要防的那个状态。五条验收条件全部**实测而非推理**。
|
|
16
|
+
- **`config restore` 与 `rollback` 对"没有备份"这件事说两种话**:`--list` 像失败一样 exit 1,`rollback` 像事实一样 exit 0 带 `available: false` —— **它们是同一件事实**。现在两者都 exit 0,且 `available` 出现在**每一个**信封上(`false` = 没有备份可恢复,`true` = 有备份、失败的是操作本身),区分从退出码搬进了一个 JSON 键。
|
|
17
|
+
- **`test:integration` 漏了 `--config`**,于是继承基础 config 的 `include`、**选中零个文件**:"No test files found, exiting with code 0" —— 一个**什么都没跑**的绿。脚本现在带上该 flag,`ci.yml` / `vitest.config.integration.ts` / 一个把旧绕行说成永久的测试注释也一并改正;**没有任何 CI 步骤会跑两遍该套件**(那个 job 直接调 vitest,从不走脚本)。
|
|
18
|
+
- **`detached.ts` 挂了一个可证明永远不会运行的 `child.on('error')`** —— Node 把 spawn 失败派发在 nextTick 队列上,该队列在 await 的调用方恢复之前就排空,所以 handler 总是挂得太晚。删掉它,启动失败路径改读 `spawnError`,而不是返回 `ok: true` 带 `pid: -1`;在一个真实 ENOENT 上端到端验证:`ok: false`、`spawnError {ENOENT}`、派发记录 `status=failed`。另外 `ProcessSupervisor.spawn` 曾在失败启动时写零字节 `pid` 文件,而 `Number('') === 0` —— 一次 `kill(Number(read(pid)))` 的清理会**给 pid 0 发信号,也就是整个进程组**;现在什么都不写,因为"不存在"才是真正的不变量。
|
|
19
|
+
- 以及 `--ide` help 列了两个适配器而注册表里有九个(六处不是三处,改为从注册表派生,从此不会漂移)、`tests/integration/_cli-helper.ts` 镜像了一个生产已不再发出的信封形状(爆炸半径是**一个** importer 不是十一个;改为让真实 CLI 与测试 helper 调用同一份 `cli-helpers.ts`,此后不可能各自漂移)。
|
|
20
|
+
|
|
21
|
+
**验证**:三个版本常量一致(**4.0.48**);`pnpm build` 的 `build-integrity: OK`;`tsc -p tsconfig.json` 保持 **140** 基线、**0 在 `src/`**、且 **TS6053 = 0**(孤立的 TS6053 是一次 abort 而不是一棵干净的树 —— 它报"找不到文件"并且什么都不编译,所以 `140 → 1` 会被读成"大幅改善"、`0 → 1` 被读成"新的小问题",两种读法都错);`tests/unit` **248 files / 2627 passed / 3 skipped / 0 failed**。套件的 `write-gate-decision-table` 与 `final-review-service` 用例是**负载敏感**的(并行负载下超时、单独跑通过)—— **点名,不两边计数**。
|
|
22
|
+
|
|
23
|
+
**明确未验证的(不当作已完成)**:**没有人在真实 Claude Code 上观察过一次 `PostCompact`** —— 测试证明的是"命令会结算",**从不是"事件会到达"**;同理,本仓库**从未观察过任何真实的 Claude Code statusline 载荷**(此仓没有 `statusLine` 键,装一个属于需要用户确认的 harness settings 变更),被验证的只是"真实载荷会被正确捕获并比对"。
|
|
24
|
+
|
|
25
|
+
**本轮顺带发现、尚未处理(不属本版修复)**:13 个守卫点位里 10 个没有实测控制组;17 个带 id 的文件既未分类也未点名;约 60 处 binding/env 拼接;31 个未追踪的 CLI 文件上仍有同一种路径逃逸形状;以及最刺眼的一条 —— **扫描清单自己的后续指令仍然排除了这一轮查出最严重缺陷的那个文件**。
|
|
26
|
+
|
|
27
|
+
## 4.0.47 — 2026-09-13 (90 个文件第一次进 CI 就绿了 + 弹窗的根在守卫射程 + 说"能做"而做不到的地方)
|
|
28
|
+
|
|
29
|
+
**Highlights**:
|
|
30
|
+
|
|
31
|
+
1. **`tests/integration/**`(90 个文件 / 463 个测试)第一次进了 CI,也第一次在 ubuntu 上绿了。** 它此前**从未在任何地方跑过** —— `ci.yml` 的测试步骤用默认 config,其 `include` 只含 `tests/unit/**`;所以"CI 绿"与"全量绿"都不覆盖它。接入后实测 **20 条失败**,逐条分诊后:**2 条是真产品 bug**(`memory extract` 把"文件不存在"报成沙箱逃逸;`release canary` 缺 `package.json` 时抛 `UNHANDLED_ERROR`)、**1 条分诊误判被拒绝**(`sop registry` 看着像读不到磁盘清单,实际是仓库从没跑过 `sop register` —— 内容缺口,不是代码缺口)、其余为陈旧期望与契约选择。
|
|
32
|
+
|
|
33
|
+
**那个 CI job 一开始是"有意变红"的,现在是普通闸门** —— 它的名字与注释都改成了如实描述:**红即真回归**,不再是"已知初始状态"。已验证:**ubuntu 与 windows 双双全绿**。
|
|
34
|
+
|
|
35
|
+
2. **空白 `cmd.exe` 弹窗:根在守卫的射程,不在扫描的深度。** 上一版给 14 个文件加了 `windowsHide` 并加了守卫,弹窗却没停 —— 因为守卫的 `GUARDED_FILES` 是**那 14 个文件的硬编码棘轮**,其它文件**按构造就在射程外**,于是守卫报零而症状继续。扩成**发现式**(遍历 `src|tests|scripts|packages`,**先收集完整列表再逐个解析**,不依赖单文件结果)后,查出 **214 个未隐藏调用点 / 105 个文件**,**全部修复,债务棘轮为空**。
|
|
36
|
+
|
|
37
|
+
扩围还暴露了守卫**自身 AST 遍历的 4 处缺陷** —— 那些缺陷正是这些站点长期隐形的原因:`execFile(cmd,args,OPTIONS,cb)` 的 options 读错位置(放过 `src/shared/process.ts`,**每次子代理派发都在跑**)、改名 import 跟不上、**`promisify(execFile)` 完全跟不上**(隐藏了 `orchestrator-can-do.ts` 上**每轮** `code context-now` 路径的两个活站点)、动态 import 命名空间漏。
|
|
38
|
+
|
|
39
|
+
3. **auto-compact:从"谁来决定何时压"到"压完之后还在不在"。** 两件事都做了:
|
|
40
|
+
|
|
41
|
+
- **尺度对齐。** peaks-loop 与 harness **各自独立解析"上下文窗口"**(前者走 env → config → 模型名启发式 → 硬编码 200K),**没有任何机制保证两者相同**。后果:启发式认不出模型时,peaks-loop 的"95%"落在 **190K token**、而 harness 要到 **~967K** 才压 —— **红线早触发约 5 倍,然后死锁**(没有任何东西能在 190K 处强制压缩)。现在两边**是同一个数**;红线**不再封死派发**,只请求并报告等待;阈值写入 `.claude/settings.local.json` 的 `env` 块,**带人类可读的告知与一键回滚**(harness 默认对被覆盖是静默的)。
|
|
42
|
+
- **压缩后回注。** 压缩由 harness 执行、摘要是**泛化的** —— 它不知道什么是 slice / request / gate / AC,所以丢掉的恰恰是本项目自己的状态,而**没有任何东西把它装回去**。新增 `SessionStart` + `matcher:"compact"` 钩子(官方支持的回注通道),**预算 3072 字节**:按**存活排名**保留块(身份 → **指针** → 运行期规则 → 下一步 → 当前工作),**指针优先于规则**(让人去读产物,而不是把产物搬进来);降级是 **greedy by rank(跳过并继续)**,不是一个超大块把后面的全挤掉。**单位是字节而非"窗口的百分之几"** —— 百分比预算会**随窗口变大而变大**,正好重新制造它要防的失效。
|
|
43
|
+
|
|
44
|
+
4. **一批"代码在说它做不到的事"** —— 本版最反复的一条主线:
|
|
45
|
+
|
|
46
|
+
- **一个谁都接不住的 spawn 失败。** 集成套件测试全过、进程却 exit 1。根因不是平台:`ProcessSupervisor.spawn` 调 `nodeSpawn` 时**没挂 `'error'` 监听器**,而 **Node 把 ENOENT 派发在 nextTick 上,那个队列在 `await` 恢复之前就排空了** —— 所以 `await spawn(); child.on('error', …)` **无论谁写都输掉竞速**,`DispatchResult.child`(注释写明"给调用方挂 handler 用")是个**无法兑现的承诺**,CLI 里自称 "canonical pattern" 的 handler **是死代码**。改为把结果做成**类型化的值**(`settled` 承诺 + `spawnError`),失败不再被记成 `running`。
|
|
47
|
+
- **缺必填选项报成 `UNHANDLED_ERROR`**(`command: "cli"`、`nextActions` 为空)—— 改为类型化的 `MISSING_REQUIRED_OPTION`,点名命令与选项。
|
|
48
|
+
- **`config restore` 读的是 `~/.peaks/*.bak` 而不是项目目录** —— 所以它在一台**恰好迁移过配置的开发机**上绿、在干净 runner 上红;而 `rollback` 因为优雅降级反而一直绿。
|
|
49
|
+
- **CI 里 `request init` 要解析 caller id**,无 IDE env 时落到 D2/EX_USAGE(exit 64),而 `workspace init` 从不解析它 —— 这就是"上一行成功、这一行失败"的不对称。
|
|
50
|
+
|
|
51
|
+
**验证**:三个版本常量一致(**4.0.47**);`tsc -p tsconfig.json` 保持 **142** 基线;`tests/unit` **228 files / 2397 passed / 3 skipped / 0 failed**;`tests/integration` **88 files / 2 skipped / 0 failed**,且 **CI 的 ubuntu 与 windows 两个平台都绿**。
|
|
52
|
+
|
|
53
|
+
**明确未验证的(不当作已完成)**:压缩后回注**没有人在一次*真实*压缩之后观察过** —— 单测无法让 harness 压缩、也无法观测它怎么捕获钩子 stdout,所以 `matcher:"compact"` 是当作已知条件接受的,**它的失效方式是静默的**;**下一次真实压缩才是第一次真检验**。以及"由 peaks-loop 决定**触发点**"仍未达成:写入的窗口等于真实窗口,只让两边共享尺度,**没有让 harness 提前压** —— 标定需要真实会话数据,`compact-history.jsonl` 已记录"意图 vs 实际"待读。
|
|
54
|
+
|
|
55
|
+
**本轮顺带发现、尚未处理(不属本版修复)**:三条 `SessionStart` 条目假设 Windows 上 shell 形式的 SessionStart 命令不会开控制台窗口 —— `shell: powershell` 那个钉子只加在了 per-tool-call 钩子上,**这是 windowsHide 家族还没扫到的一处**;`materializeClaudeSettingsLocal` 拥有本地 settings 文件的整个 `hooks` 键,落在那里的一条 SessionStart 条目会被模板刷新**静默删掉**(今天没有东西走那条路);`config restore --list` 在缺 `.bak` 时 exit 1 而 `rollback` exit 0 并返回 `{available:false}` —— 一处真实的不对称,需要产品决定;`test:integration` 脚本仍漏 `--config`,**选中的是零个文件**。
|
|
56
|
+
|
|
3
57
|
## 4.0.46 — 2026-09-12 (85% 该压不压 + 把"派发"记成了"正在压缩")
|
|
4
58
|
|
|
5
59
|
**Highlights**:
|
package/README-en.md
CHANGED
|
@@ -140,7 +140,7 @@ Every lane opens with **one slash command**.
|
|
|
140
140
|
|
|
141
141
|
| | |
|
|
142
142
|
| --- | --- |
|
|
143
|
-
| **Latest** | [](https://www.npmjs.com/package/peaks-loop) — 4.0.
|
|
143
|
+
| **Latest** | [](https://www.npmjs.com/package/peaks-loop) — 4.0.48 (2026-09-14) |
|
|
144
144
|
| **Domains** | Code (`peaks-code`) · Content (`peaks-content`) · Project health (`peaks-doctor`) · Issue sweep (`peaks-issue-fix-orchestrator`) · Custom SOP (`peaks-sop`) · Cross-domain primitives (`peaks-solo` dispatcher · `peaks-resume` · `peaks-status` · `peaks-test` · `peaks-slice-decompose`) |
|
|
145
145
|
| **Sediment pool** | `~/.peaks/` local pool · twice-clean runs auto-promote to a bee · broken runs come back for you to redefine · the bee grows with your taste |
|
|
146
146
|
| **Test suite** | 285+ cases · 4 packages (peaks-loop / peaks-loop-mut / peaks-loop-shared-channel / peaks-loop-shared) · **0 timeouts** · 14 BDD caller-binding edge cases |
|
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ npm i -g peaks-loop
|
|
|
140
140
|
|
|
141
141
|
| | |
|
|
142
142
|
| --- | --- |
|
|
143
|
-
| **最新版本** | [](https://www.npmjs.com/package/peaks-loop) — 4.0.
|
|
143
|
+
| **最新版本** | [](https://www.npmjs.com/package/peaks-loop) — 4.0.48(2026-09-14) |
|
|
144
144
|
| **覆盖域** | 代码(`peaks-code`) · 内容(`peaks-content`) · 项目健康(`peaks-doctor`) · 批量修 issue(`peaks-issue-fix-orchestrator`) · 自定义 SOP(`peaks-sop`) · 通用原语(`peaks-solo` 分诊 / `peaks-resume` 续 / `peaks-status` 看 / `peaks-test` 测 / `peaks-slice-decompose` 切片) |
|
|
145
145
|
| **沉淀池** | `~/.peaks/` 本地池 · 跑两次自动晋升成 bee · 跑翻车让你重定义 · bee 跟着你的口味长 |
|
|
146
146
|
| **测试套件** | 1096 cases · 4 packages (peaks-loop 1015 / runtime 39 / mut 22 / shared-channel 20) · **CI 首次全绿**(ubuntu + windows) · 14 BDD caller-binding coverage |
|
|
@@ -29,7 +29,8 @@ You are a **Karpathy-guidelines enforcement reviewer** for peaks-rd. You inspect
|
|
|
29
29
|
|
|
30
30
|
1. **The verbatim 4-section Karpathy-guidelines block** (injected by the parent RD loop via `rd-sub-agent-dispatch.md` §"Karpathy-guidelines context"). If this block is missing from the prompt, return `gateAction: 'block'` with a single `think-before-coding` violation.
|
|
31
31
|
2. **The slice's `git diff` against the base branch** (use `git diff <base>...HEAD` to get the full RD-side change set; fall back to `git diff` if no base is given, then `git status --short` to confirm scope).
|
|
32
|
-
3. **The slice's `rd/tech-doc.md
|
|
32
|
+
3. **The slice's `prd/handoff.md`** (the immutable peaks-prd design / scope record — v2.11.0: it replaces `rd/tech-doc.md`, which no gate requires any more). Read it at `.peaks/_runtime/<sessionId>/prd/handoff.md`; verify the handoff hash matches the value the parent dispatched before relying on it.
|
|
33
|
+
> `rd/tech-doc-<rid>.md` was evaluated as the alternative and rejected: nothing in the pipeline writes a rid-scoped tech-doc (`peaks evidence generate` writes the RIDLESS `rd/tech-doc.md` on purpose, since the `TECH_DOC` prereq was dropped in v2.11.0), so requiring one would BLOCK every slice. The ridless path was worse still — one file shared by every slice in a session, so a stale architecture summary from an earlier slice read as this slice's design doc. `prd/handoff.md` is the record the contract itself requires at `rd:qa-handoff` (`AUDIT_REQUIRES_HANDOFF`), and the fan-out prose already names it (`references/parallel-review-fanout.md`).
|
|
33
34
|
4. **The slice's PRD body / acceptance criteria** (look at `.peaks/_runtime/<sessionId>/prd/requests/<rid>.md`).
|
|
34
35
|
5. **Optional: the slice's `rd/code-review.md` / `rd/security-review.md` / `rd/perf-baseline.md`** for cross-context only — do not duplicate their findings.
|
|
35
36
|
|
|
@@ -47,7 +48,7 @@ If any of inputs 1-4 is unreadable / missing, return `gateAction: 'block'` with
|
|
|
47
48
|
- The diff adds a regex / parser / serializer without naming the input shape (language, locale, edge cases).
|
|
48
49
|
- The diff introduces a CLI option whose default value is not justified (why this default? what changed if a user picks a different value?).
|
|
49
50
|
- The diff contains the phrase "we'll handle that later" / "TODO: think about" / "TBD" in a code path.
|
|
50
|
-
- The slice's `
|
|
51
|
+
- The slice's `prd/handoff.md` body — the architecture / trade-offs record — is missing or empty.
|
|
51
52
|
|
|
52
53
|
### 3.2 Simplicity First
|
|
53
54
|
|
|
@@ -85,7 +86,7 @@ If any of inputs 1-4 is unreadable / missing, return `gateAction: 'block'` with
|
|
|
85
86
|
- The diff adds a new public function / exported type / CLI subcommand with NO test in the same diff.
|
|
86
87
|
- The slice's `qa/test-cases/<rid>.md` is empty or has < 3 test rows.
|
|
87
88
|
- The diff claims a behavior change in a commit message / PR body / docstring but no test exercises the change.
|
|
88
|
-
- The
|
|
89
|
+
- The slice's `prd/handoff.md` frontmatter carries no `acceptanceCriteria` entries — the handoff's equivalent of the old "Acceptance checks" section.
|
|
89
90
|
- The diff adds a new regex / parser / runtime check whose failure path is unreachable from any test.
|
|
90
91
|
|
|
91
92
|
## 4. Output (compact JSON envelope)
|
|
@@ -163,8 +164,8 @@ When `evaluationCost` is reported, the envelope is:
|
|
|
163
164
|
|---|---|
|
|
164
165
|
| 0 violations | `'pass'` |
|
|
165
166
|
| 1+ violations of any kind, none of which is a HARD-blocker | `'warn'` |
|
|
166
|
-
| `## Karpathy-Gate` header missing from `rd/karpathy-review
|
|
167
|
-
| Inputs 1-4 (guidelines verbatim / diff /
|
|
167
|
+
| `## Karpathy-Gate` header missing from `rd/karpathy-review-<rid>.md` file write | `'block'` |
|
|
168
|
+
| Inputs 1-4 (guidelines verbatim / diff / prd-handoff / PRD-AC) missing or unreadable | `'block'` |
|
|
168
169
|
| Diff is empty (no changed files) but the slice claims a feature change | `'block'` |
|
|
169
170
|
| The slice introduces secrets, executes arbitrary code, or modifies `~/.claude/settings.json` / `~/.claude/agents/` / `~/.claude/hooks/` | `'block'` |
|
|
170
171
|
| 3+ violations across 3+ different `kind` values | `'block'` (the slice is structurally misaligned, not just imperfect) |
|
|
@@ -181,7 +182,7 @@ If the `evaluationCost` field is absent (older reviewer, or the orchestrator did
|
|
|
181
182
|
|
|
182
183
|
## 5. File write contract
|
|
183
184
|
|
|
184
|
-
You MUST also write `rd/karpathy-review
|
|
185
|
+
You MUST also write `rd/karpathy-review-<rid>.md` (relative to the slice's project root), replacing `<rid>` with this slice's request id — the same id that names `.peaks/_runtime/<sessionId>/rd/requests/<rid>.md`. The rid is part of the filename because every slice in a session shares `rd/`; writing the ridless `rd/karpathy-review.md` silently destroys the previous slice's review (it did on 2026-09-13). The file must contain EXACTLY these section headers, in this order, with title-case capitalization (the existing `KARPATHY_REVIEW` prereq in `src/services/artifacts/artifact-prerequisites.ts` enforces this). The 5 literal lines below are indented by 4 spaces to keep them out of the sibling-reference heading inventory; copy them verbatim into the file you write.
|
|
185
186
|
|
|
186
187
|
```md
|
|
187
188
|
# Karpathy review — <rid>
|
|
@@ -209,14 +210,14 @@ You MUST also write `rd/karpathy-review.md` (relative to the slice's project roo
|
|
|
209
210
|
<bullet-list of evidence, one per finding, or "No violations" if clean>
|
|
210
211
|
```
|
|
211
212
|
|
|
212
|
-
If you cannot write this file (read-only filesystem, path collision, permission denied), include a `writeError: '<reason>'` field in your JSON envelope and set `gateAction: 'block'`. The transition CLI gate will refuse the `qa-handoff` transition if `rd/karpathy-review
|
|
213
|
+
If you cannot write this file (read-only filesystem, path collision, permission denied), include a `writeError: '<reason>'` field in your JSON envelope and set `gateAction: 'block'`. The transition CLI gate will refuse the `qa-handoff` transition if `rd/karpathy-review-<rid>.md` is missing.
|
|
213
214
|
|
|
214
215
|
## 6. Hard prohibitions
|
|
215
216
|
|
|
216
217
|
In addition to the 4-sub-agent block:
|
|
217
218
|
|
|
218
219
|
- **MUST NOT write code** — you review, you do not implement. The RD main loop owns code edits.
|
|
219
|
-
- **MUST NOT modify the request artifact** (`.peaks/_runtime/<sessionId>/rd/requests/<rid>.md` or the PRD body). Your only write target is `rd/karpathy-review
|
|
220
|
+
- **MUST NOT modify the request artifact** (`.peaks/_runtime/<sessionId>/rd/requests/<rid>.md` or the PRD body). Your only write target is `rd/karpathy-review-<rid>.md`.
|
|
220
221
|
- **MUST NOT call `peaks request transition`** — only the parent RD loop owns the transition state machine.
|
|
221
222
|
- **MUST NOT install hooks, agents, MCP servers, or modify settings** (this is the global peaks-rd red line; it applies to sub-agents too).
|
|
222
223
|
- **MUST NOT touch Slice 1+2+3+4+5 products** (zero regression). If the diff includes changes to `karpathy-service.ts` / `scan-commands.ts` / `artifact-prerequisites.ts` / `peaks-rd/SKILL.md` / `rd-fanout-contracts.md` / `rd-sub-agent-dispatch.md` / `karpathy-5way-fanout.test.ts` / `rd/karpathy-review.md`, flag them as `surgical-changes` violations unless the slice's PRD explicitly authorizes the touch.
|
|
@@ -234,11 +235,11 @@ In addition to the 4-sub-agent block:
|
|
|
234
235
|
|
|
235
236
|
## 8. Review process
|
|
236
237
|
|
|
237
|
-
1. Read the 4 inputs (guidelines / diff /
|
|
238
|
+
1. Read the 4 inputs (guidelines / diff / prd-handoff / PRD-AC).
|
|
238
239
|
2. For each of the 4 guidelines, walk the diff and apply the detection rules in §3.
|
|
239
240
|
3. Collect violations into a list, capped at 5 per kind.
|
|
240
241
|
4. Decide `gateAction` per the §4 decision table.
|
|
241
|
-
5. Write `rd/karpathy-review
|
|
242
|
+
5. Write `rd/karpathy-review-<rid>.md` with the 4 title-case section headers.
|
|
242
243
|
6. Emit the compact JSON envelope on the last line of your response.
|
|
243
244
|
|
|
244
245
|
## 9. Review summary format (informational, not part of envelope)
|
|
@@ -58,6 +58,40 @@ export declare function printCliEnvelope(io: ProgramIO, r: CliEnvelope): void;
|
|
|
58
58
|
* `src/cli/program.ts:162` is hereby resolved by this helper.
|
|
59
59
|
*/
|
|
60
60
|
export declare function printErrorEnvelope(io: ProgramIO, command: string, code: string, message: string, data: Record<string, unknown>, nextActions: string[]): void;
|
|
61
|
+
/**
|
|
62
|
+
* The subcommand path the caller actually invoked, in the same tokens they
|
|
63
|
+
* typed (`release canary`). Derived from the registered command tree by
|
|
64
|
+
* consuming leading non-`-` argv tokens, so it never disagrees with what
|
|
65
|
+
* Commander dispatched on.
|
|
66
|
+
*
|
|
67
|
+
* Commander's `CommanderError` carries a code and a message but no command
|
|
68
|
+
* reference — `missingMandatoryOptionValue` is raised by the leaf command and
|
|
69
|
+
* throws out of `parseAsync` with nothing naming it. Without this walk the
|
|
70
|
+
* missing-option envelope could only say `command: "cli"`, which is the field
|
|
71
|
+
* the caller needs to act on.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveInvokedCommandPath(program: Command, argv: readonly string[]): string;
|
|
74
|
+
/**
|
|
75
|
+
* Render the `MISSING_REQUIRED_OPTION` envelope for a Commander
|
|
76
|
+
* `commander.missingMandatoryOptionValue`.
|
|
77
|
+
*
|
|
78
|
+
* A `.requiredOption()` the caller did not supply is raised as a plain
|
|
79
|
+
* `Error`-shaped `CommanderError`, so an unhandled `.catch()` used to file it
|
|
80
|
+
* under `UNHANDLED_ERROR` — "you left out an argument" reported as a crash,
|
|
81
|
+
* with empty `nextActions`, `command: "cli"` and no way to see WHICH option.
|
|
82
|
+
* The flags string Commander formats carries both the name and the accepted
|
|
83
|
+
* values (`--percent <10|50>`), so it is worth extracting rather than
|
|
84
|
+
* paraphrasing: it is the option's own declaration.
|
|
85
|
+
*
|
|
86
|
+
* Exported because it has TWO callers that must not drift: the real wrapper
|
|
87
|
+
* (`src/cli/index.ts`) and the in-process test runner
|
|
88
|
+
* (`tests/integration/_cli-helper.ts`), which re-creates the wrapper's
|
|
89
|
+
* behaviour for tests that call the program directly. That file used to
|
|
90
|
+
* hand-mirror the pre-fix shape, so every integration test that hit a missing
|
|
91
|
+
* required option asserted an envelope production no longer emits. Sharing the
|
|
92
|
+
* builder is what makes the mirror incapable of lying.
|
|
93
|
+
*/
|
|
94
|
+
export declare function printMissingRequiredOptionEnvelope(io: ProgramIO, invokedCommand: string, message: string): void;
|
|
61
95
|
export declare function isRecommendationWorkflow(value: string): value is RecommendationWorkflow;
|
|
62
96
|
export declare function isArtifactProvider(value: string): value is ArtifactProvider;
|
|
63
97
|
export declare function isArtifactSetupStep(value: string): value is GuidedArtifactSetup['step'];
|
package/dist/cli/cli-helpers.js
CHANGED
|
@@ -88,6 +88,63 @@ export function printErrorEnvelope(io, command, code, message, data, nextActions
|
|
|
88
88
|
io.stderr(JSON.stringify(envelope, null, 2));
|
|
89
89
|
process.exitCode = 1;
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The subcommand path the caller actually invoked, in the same tokens they
|
|
93
|
+
* typed (`release canary`). Derived from the registered command tree by
|
|
94
|
+
* consuming leading non-`-` argv tokens, so it never disagrees with what
|
|
95
|
+
* Commander dispatched on.
|
|
96
|
+
*
|
|
97
|
+
* Commander's `CommanderError` carries a code and a message but no command
|
|
98
|
+
* reference — `missingMandatoryOptionValue` is raised by the leaf command and
|
|
99
|
+
* throws out of `parseAsync` with nothing naming it. Without this walk the
|
|
100
|
+
* missing-option envelope could only say `command: "cli"`, which is the field
|
|
101
|
+
* the caller needs to act on.
|
|
102
|
+
*/
|
|
103
|
+
export function resolveInvokedCommandPath(program, argv) {
|
|
104
|
+
let cmd = program;
|
|
105
|
+
const parts = [];
|
|
106
|
+
for (const token of argv) {
|
|
107
|
+
if (token.startsWith('-'))
|
|
108
|
+
break;
|
|
109
|
+
const next = cmd.commands.find((c) => c.name() === token || c.aliases().includes(token));
|
|
110
|
+
if (next === undefined)
|
|
111
|
+
break;
|
|
112
|
+
parts.push(next.name());
|
|
113
|
+
cmd = next;
|
|
114
|
+
}
|
|
115
|
+
return parts.length > 0 ? parts.join(' ') : program.name();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Render the `MISSING_REQUIRED_OPTION` envelope for a Commander
|
|
119
|
+
* `commander.missingMandatoryOptionValue`.
|
|
120
|
+
*
|
|
121
|
+
* A `.requiredOption()` the caller did not supply is raised as a plain
|
|
122
|
+
* `Error`-shaped `CommanderError`, so an unhandled `.catch()` used to file it
|
|
123
|
+
* under `UNHANDLED_ERROR` — "you left out an argument" reported as a crash,
|
|
124
|
+
* with empty `nextActions`, `command: "cli"` and no way to see WHICH option.
|
|
125
|
+
* The flags string Commander formats carries both the name and the accepted
|
|
126
|
+
* values (`--percent <10|50>`), so it is worth extracting rather than
|
|
127
|
+
* paraphrasing: it is the option's own declaration.
|
|
128
|
+
*
|
|
129
|
+
* Exported because it has TWO callers that must not drift: the real wrapper
|
|
130
|
+
* (`src/cli/index.ts`) and the in-process test runner
|
|
131
|
+
* (`tests/integration/_cli-helper.ts`), which re-creates the wrapper's
|
|
132
|
+
* behaviour for tests that call the program directly. That file used to
|
|
133
|
+
* hand-mirror the pre-fix shape, so every integration test that hit a missing
|
|
134
|
+
* required option asserted an envelope production no longer emits. Sharing the
|
|
135
|
+
* builder is what makes the mirror incapable of lying.
|
|
136
|
+
*/
|
|
137
|
+
export function printMissingRequiredOptionEnvelope(io, invokedCommand, message) {
|
|
138
|
+
const option = /required option '([^']+)'/.exec(message)?.[1];
|
|
139
|
+
printErrorEnvelope(io, invokedCommand, 'MISSING_REQUIRED_OPTION', option === undefined
|
|
140
|
+
? message
|
|
141
|
+
: `Missing required option '${option}' for \`peaks ${invokedCommand}\`.`, { option: option ?? null }, [
|
|
142
|
+
option === undefined
|
|
143
|
+
? `Run \`peaks ${invokedCommand} --help\` to see the options this command requires.`
|
|
144
|
+
: `Supply ${option} — it is required, so the command has no default for it.`,
|
|
145
|
+
`Run \`peaks ${invokedCommand} --help\` for the option's accepted values and its siblings.`
|
|
146
|
+
]);
|
|
147
|
+
}
|
|
91
148
|
export function isRecommendationWorkflow(value) {
|
|
92
149
|
return value === 'code-refactor' || value === 'product-refactor' || value === 'frontend-design';
|
|
93
150
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { existsSync, readFileSync } from 'node:fs';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
10
|
+
import { isUnsafePathInput } from '../../shared/path-safety.js';
|
|
10
11
|
import { fail, ok } from 'peaks-loop-shared/result';
|
|
11
12
|
import { readJobShapeDecision, writeJobShapeDecision, JobShapeDecisionError, JOB_SHAPE_NOT_DECIDED, JOB_SHAPE_ALREADY_DECIDED } from '../../services/code/job-shape-decision.js';
|
|
12
13
|
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
@@ -60,6 +61,13 @@ export function registerCodeJobShapeCommands(code, io) {
|
|
|
60
61
|
process.exitCode = 1;
|
|
61
62
|
return;
|
|
62
63
|
}
|
|
64
|
+
// Sid axis. `--session-id` reaches both the `last-prompt.txt` read and
|
|
65
|
+
// `writeJobShapeDecision`, so one guard at resolution covers the file.
|
|
66
|
+
if (isUnsafePathInput(sessionId)) {
|
|
67
|
+
printResult(io, fail('code.detect-job', 'INVALID_SESSION_ID', `Invalid session id: ${sessionId} (must be a single path segment)`, { provided: sessionId }, ['Pass a session id that is a single path segment']), opts.json);
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
63
71
|
// Prompt source: explicit --prompt > last-prompt.txt > empty.
|
|
64
72
|
let promptText = opts.prompt ?? '';
|
|
65
73
|
if (opts.prompt === undefined) {
|
|
@@ -11,4 +11,26 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Command } from 'commander';
|
|
13
13
|
import { type ProgramIO } from '../cli-helpers.js';
|
|
14
|
+
import { type ResultEnvelope } from 'peaks-loop-shared/result';
|
|
15
|
+
import { type AutoCompactResult } from '../../services/code/auto-compact-orchestrator.js';
|
|
16
|
+
/**
|
|
17
|
+
* Adapt `runAutoCompact`'s domain result to a `ResultEnvelope`.
|
|
18
|
+
*
|
|
19
|
+
* Extracted and exported so the CHANNEL each fact chooses can be asserted
|
|
20
|
+
* directly, without a real red-line probe.
|
|
21
|
+
*
|
|
22
|
+
* The harness-window CONFLICT ("peaks-loop divided this ratio by N but the
|
|
23
|
+
* settings file pins M") is a fact about the STATE, not an instruction, so it
|
|
24
|
+
* rides `warnings` — the very channel `peaks code context-now` already puts it
|
|
25
|
+
* on, via the same `harnessWindowSyncWarning`. Before this, the shim hard-coded
|
|
26
|
+
* `warnings: []`, so the identical fact reached a consumer as a sentence inside
|
|
27
|
+
* `nextActions` on this command and as a `warnings` entry on the other one. One
|
|
28
|
+
* fact, two shapes, depending on which of the two syncing commands a consumer
|
|
29
|
+
* happened to read: two parsers for one mechanism.
|
|
30
|
+
*
|
|
31
|
+
* The prose description (`describeHarnessWindowSync`) stays in `nextActions` on
|
|
32
|
+
* BOTH commands — that half is advice ("what to do about it") and both already
|
|
33
|
+
* agree on it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildAutoCompactEnvelope(result: AutoCompactResult): ResultEnvelope<unknown>;
|
|
14
36
|
export declare function registerCodeRuntimeCommands(code: Command, io: ProgramIO): void;
|
|
@@ -14,6 +14,10 @@ import { fail, ok } from 'peaks-loop-shared/result';
|
|
|
14
14
|
import { detectPostCompactResume, formatPostCompactResumeLogLine } from '../../services/code/post-compact-detector.js';
|
|
15
15
|
import { runAutoCompact } from '../../services/code/auto-compact-orchestrator.js';
|
|
16
16
|
import { auditContext } from '../../services/context/context-audit.js';
|
|
17
|
+
import { syncHarnessWindowForProject } from '../../services/context/auto-compact-reader.js';
|
|
18
|
+
import { describeHarnessWindowSync, harnessWindowSyncWarning } from '../../services/context/harness-window-config.js';
|
|
19
|
+
import { describeHarnessWitness, readAndCompareHarnessWitness } from '../../services/context/harness-context-witness.js';
|
|
20
|
+
import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
|
|
17
21
|
import { buildContextAuditHint } from '../../services/context/context-audit-hint.js';
|
|
18
22
|
import { evaluateStep08, STEP_08_BACKUP_REGEX } from '../../services/code/step-08-gate.js';
|
|
19
23
|
import { evaluateEmitHandoff, JOB_NOT_INITIALIZED, JOB_REMAINING_BLOCKED } from '../../services/code/emit-handoff.js';
|
|
@@ -21,6 +25,38 @@ import { readJobShapeDecision, JobShapeDecisionError } from '../../services/code
|
|
|
21
25
|
import { getSkillPresence } from '../../services/skills/skill-presence-service.js';
|
|
22
26
|
import { probeInFlightBatch } from '../../services/workflow/workflow-inflight-probe.js';
|
|
23
27
|
import { resolveOuterSessionId } from '../../services/session/binding-status-service.js';
|
|
28
|
+
/**
|
|
29
|
+
* Adapt `runAutoCompact`'s domain result to a `ResultEnvelope`.
|
|
30
|
+
*
|
|
31
|
+
* Extracted and exported so the CHANNEL each fact chooses can be asserted
|
|
32
|
+
* directly, without a real red-line probe.
|
|
33
|
+
*
|
|
34
|
+
* The harness-window CONFLICT ("peaks-loop divided this ratio by N but the
|
|
35
|
+
* settings file pins M") is a fact about the STATE, not an instruction, so it
|
|
36
|
+
* rides `warnings` — the very channel `peaks code context-now` already puts it
|
|
37
|
+
* on, via the same `harnessWindowSyncWarning`. Before this, the shim hard-coded
|
|
38
|
+
* `warnings: []`, so the identical fact reached a consumer as a sentence inside
|
|
39
|
+
* `nextActions` on this command and as a `warnings` entry on the other one. One
|
|
40
|
+
* fact, two shapes, depending on which of the two syncing commands a consumer
|
|
41
|
+
* happened to read: two parsers for one mechanism.
|
|
42
|
+
*
|
|
43
|
+
* The prose description (`describeHarnessWindowSync`) stays in `nextActions` on
|
|
44
|
+
* BOTH commands — that half is advice ("what to do about it") and both already
|
|
45
|
+
* agree on it.
|
|
46
|
+
*/
|
|
47
|
+
export function buildAutoCompactEnvelope(result) {
|
|
48
|
+
const data = 'data' in result ? result.data : null;
|
|
49
|
+
const nextActions = 'nextActions' in result ? result.nextActions : [];
|
|
50
|
+
const warning = harnessWindowSyncWarning(data?.harnessWindow ?? null);
|
|
51
|
+
const warnings = warning === null ? [] : [warning];
|
|
52
|
+
if (result.ok) {
|
|
53
|
+
return ok('code.auto-compact', data ?? {}, warnings, [result.message, ...nextActions]);
|
|
54
|
+
}
|
|
55
|
+
// `fail()` hard-codes `warnings: []`, so the warning is spread back over it —
|
|
56
|
+
// a refused write can disagree with the ratio just as easily on the failure
|
|
57
|
+
// path, and the state does not become less true because the dispatch failed.
|
|
58
|
+
return { ...fail('code.auto-compact', result.code, result.message, data, [...nextActions]), warnings };
|
|
59
|
+
}
|
|
24
60
|
export function registerCodeRuntimeCommands(code, io) {
|
|
25
61
|
addJsonOption(code
|
|
26
62
|
.command('post-compact-detect')
|
|
@@ -58,9 +94,11 @@ export function registerCodeRuntimeCommands(code, io) {
|
|
|
58
94
|
.command('auto-compact')
|
|
59
95
|
.description('v2.13.0 AC-4: zero-human-intervention auto-compact. Probes current ' +
|
|
60
96
|
'context-fill % via the active IDE adapter; ≥ 0.85 writes a pre-compact ' +
|
|
61
|
-
'checkpoint + convergence plan + auto-decisions log; ≥ 0.95
|
|
62
|
-
'
|
|
63
|
-
'
|
|
97
|
+
'checkpoint + convergence plan + auto-decisions log; ≥ 0.95 ASKS the ' +
|
|
98
|
+
'harness to compact and reports that it is waiting for it — no ratio ' +
|
|
99
|
+
'blocks sub-agent dispatch, because peaks-loop has no way to compact a ' +
|
|
100
|
+
'running session and a gate nobody can satisfy gates nothing. The LLM / ' +
|
|
101
|
+
'runner keeps working at any ratio without human intervention. Pair with `peaks code ' +
|
|
64
102
|
'context-now` (AC-1), the read-only probe that reports the ratio this ' +
|
|
65
103
|
'command acts on. This command is also fired by the installed ' +
|
|
66
104
|
'PreToolUse hook, which passes `--project .` — without that argument ' +
|
|
@@ -71,7 +109,15 @@ export function registerCodeRuntimeCommands(code, io) {
|
|
|
71
109
|
.option('--session-id <sid>', 'override session id (default: read from active presence)')
|
|
72
110
|
.option('--in-flight-batch', 'defer if a sub-agent batch is in flight (D6.e)')
|
|
73
111
|
.option('--force', 'force compact at any ratio (test seam)')
|
|
74
|
-
.
|
|
112
|
+
// Accepted and inert. There is no longer a 95% gate to skip: peaks-loop
|
|
113
|
+
// cannot compact a running session, so the red line never blocked
|
|
114
|
+
// dispatch and `bypassRedLine` is read by nothing (slice
|
|
115
|
+
// 2026-09-13-auto-compact-trigger-ownership, T3/A1). The flag is KEPT
|
|
116
|
+
// rather than deleted because it is a published CLI surface — deleting it
|
|
117
|
+
// would make an existing caller fail on an unknown option, which is a
|
|
118
|
+
// harder break than a no-op — and because the honest fix here is to stop
|
|
119
|
+
// advertising it, not to change its meaning.
|
|
120
|
+
.option('--bypass-red-line', 'no-op: the 95% red line no longer gates dispatch, so there is nothing to bypass (accepted for backward compatibility)')
|
|
75
121
|
// No commander default here, deliberately. A declared default makes
|
|
76
122
|
// `opts.mode` permanently defined, which defeats the orchestrator's
|
|
77
123
|
// `input.mode ?? resolveAutoCompactMode(projectRoot)` fallback and
|
|
@@ -132,12 +178,7 @@ export function registerCodeRuntimeCommands(code, io) {
|
|
|
132
178
|
// generic accepts it. The orchestrator envelope carries
|
|
133
179
|
// `data` on success-path and `nextActions` on the error
|
|
134
180
|
// path; surface both directly to the user.
|
|
135
|
-
|
|
136
|
-
const nextActions = 'nextActions' in result ? result.nextActions : [];
|
|
137
|
-
const envelope = result.ok
|
|
138
|
-
? ok(`code.auto-compact`, data ?? {}, [], [result.message, ...nextActions])
|
|
139
|
-
: fail(`code.auto-compact`, code, result.message, data, [...nextActions]);
|
|
140
|
-
printResult(io, envelope, opts.json);
|
|
181
|
+
printResult(io, buildAutoCompactEnvelope(result), opts.json);
|
|
141
182
|
if (!exitOk)
|
|
142
183
|
process.exitCode = 1;
|
|
143
184
|
}
|
|
@@ -157,7 +198,8 @@ export function registerCodeRuntimeCommands(code, io) {
|
|
|
157
198
|
'hermes / openclaw register their own env-var via IdeAdapter.compact. ' +
|
|
158
199
|
'v3.1.2 / 2026-09-12: ≥0.85 emits action=auto-compact-now ' +
|
|
159
200
|
'(MANDATORY in every mode — single-rid included) and ' +
|
|
160
|
-
'≥0.95 emits action=red-line (
|
|
201
|
+
'≥0.95 emits action=red-line (the installed PreToolUse hook re-runs ' +
|
|
202
|
+
'this command on the next Bash/Task tool call; nothing is blocked). ' +
|
|
161
203
|
'--enforce-job-mode only changes the reported `jobMode` label; ' +
|
|
162
204
|
'the thresholds are identical. ' +
|
|
163
205
|
'Context-window override: set env PEAKS_CONTEXT_WINDOW_TOKENS=<positive int> ' +
|
|
@@ -208,6 +250,51 @@ export function registerCodeRuntimeCommands(code, io) {
|
|
|
208
250
|
env: process.env,
|
|
209
251
|
promptSizeBytes
|
|
210
252
|
});
|
|
253
|
+
// Promote `--project .` (what the PreToolUse hook passes) to the git
|
|
254
|
+
// root ONCE, for both of the consumers below: the settings path the
|
|
255
|
+
// harness window is written to, and the session directory the witness
|
|
256
|
+
// is read from, must not depend on the caller's cwd — and an absolute
|
|
257
|
+
// path is what the envelope then reports back to the operator. This
|
|
258
|
+
// resolves through `git rev-parse --show-toplevel`, i.e. a whole
|
|
259
|
+
// process spawn (measured 2026-09-14: med 111.8 ms per call on this
|
|
260
|
+
// host), so calling it twice with the same argument charged the witness
|
|
261
|
+
// read it precedes — 0.027 ms — roughly 4,000x its own cost.
|
|
262
|
+
const canonicalProjectRoot = resolveCanonicalProjectRoot(opts.project);
|
|
263
|
+
// Slice 2026-09-13-auto-compact-trigger-ownership (T1 + T2): materialize
|
|
264
|
+
// the window this probe just divided by into the harness's own settings,
|
|
265
|
+
// so "85%" here and the harness's own trigger are one point on one
|
|
266
|
+
// scale. Idempotent (no write when the value is already in force) and a
|
|
267
|
+
// no-op when the probe carried no token window — peaks-loop never
|
|
268
|
+
// invents a number it did not measure.
|
|
269
|
+
const harnessWindow = syncHarnessWindowForProject({
|
|
270
|
+
projectRoot: canonicalProjectRoot,
|
|
271
|
+
env: process.env,
|
|
272
|
+
tokens: probe.capacityTokens ?? null
|
|
273
|
+
});
|
|
274
|
+
// The machine half of 要告知. A refused write is not automatically a
|
|
275
|
+
// non-event: the file may pin a window that disagrees with the one this
|
|
276
|
+
// probe just divided by, and then the ratio above describes a trigger
|
|
277
|
+
// the harness is not going to fire. `nextActions` carries the full
|
|
278
|
+
// sentence; this one line rides `warnings` so a JSON consumer cannot
|
|
279
|
+
// miss it either.
|
|
280
|
+
const harnessWindowWarning = harnessWindowSyncWarning(harnessWindow);
|
|
281
|
+
// Slice 2026-09-13-statusline-window-witness (AC2/AC3): the harness's
|
|
282
|
+
// own number for the same quantity, captured by the statusline. This is
|
|
283
|
+
// OBSERVATION ONLY — it never feeds `verdict` / `action` / any threshold
|
|
284
|
+
// below. A wrong reading here must not be able to fire a compact.
|
|
285
|
+
const harnessWitness = readAndCompareHarnessWitness({
|
|
286
|
+
// The same promoted root as the harness-window write above, for the
|
|
287
|
+
// same reason: the statusline resolves its root from the harness
|
|
288
|
+
// payload (absolute), so a `--project .` from a hook would otherwise
|
|
289
|
+
// look for the witness somewhere else and report `absent` forever.
|
|
290
|
+
projectRoot: canonicalProjectRoot,
|
|
291
|
+
sessionId,
|
|
292
|
+
peaksRatio: probe.ratio,
|
|
293
|
+
peaksTokens: probe.rawTokens ?? null,
|
|
294
|
+
peaksWindowTokens: probe.capacityTokens ?? null,
|
|
295
|
+
outerSessionId: outerSessionId ?? null
|
|
296
|
+
});
|
|
297
|
+
const witnessNotice = describeHarnessWitness(harnessWitness);
|
|
211
298
|
const ratioPct = (probe.ratio * 100).toFixed(1);
|
|
212
299
|
let action = 'ok';
|
|
213
300
|
let next = null;
|
|
@@ -263,17 +350,53 @@ export function registerCodeRuntimeCommands(code, io) {
|
|
|
263
350
|
// default) — null for byte/percent sources, which have no window.
|
|
264
351
|
capacitySource: probe.capacitySource ?? null,
|
|
265
352
|
bytesPrompt: promptSizeBytes ?? null,
|
|
266
|
-
capturedAt: probe.capturedAt
|
|
267
|
-
|
|
353
|
+
capturedAt: probe.capturedAt,
|
|
354
|
+
// Slice 2026-09-13-auto-compact-trigger-ownership: what the harness
|
|
355
|
+
// window sync did on this probe. Reported rather than silent — the
|
|
356
|
+
// harness tells a user who overrides the window only via
|
|
357
|
+
// `/autocompact`, so peaks-loop must be the one that says it.
|
|
358
|
+
harnessWindow,
|
|
359
|
+
// Slice 2026-09-13-statusline-window-witness: the second scale.
|
|
360
|
+
harnessWitness
|
|
361
|
+
}, [
|
|
362
|
+
...(harnessWindowWarning === null ? [] : [harnessWindowWarning]),
|
|
363
|
+
// AC3: the disagreement rides `warnings` — it is a fact about the
|
|
364
|
+
// state, not an instruction — and it is one-way. Never an
|
|
365
|
+
// AskUserQuestion (see .peaks/memory/auto-compact-threshold-policy.md).
|
|
366
|
+
...(witnessNotice === null ? [] : [witnessNotice])
|
|
367
|
+
], [
|
|
268
368
|
action === 'red-line'
|
|
269
|
-
? `RED LINE: ≥ 95%. Next: \`${next}\` (
|
|
369
|
+
? `RED LINE: ≥ 95%. Next: \`${next}\` — peaks-loop asks the harness to compact and KEEPS WORKING (dispatch is not blocked); re-probe to confirm it landed.`
|
|
270
370
|
: action === 'auto-compact-now'
|
|
271
371
|
? `MANDATORY auto-compact (≥85%, every mode). Code MUST call \`${next}\` WITHOUT confirmation.`
|
|
272
372
|
: action === 'soft-warn'
|
|
273
373
|
? `Soft warn (50–85%). Continue working; the next \`peaks code auto-compact\` will re-check.`
|
|
274
374
|
: `Below 50%. No action required.`,
|
|
275
|
-
gateModeNotice
|
|
276
|
-
|
|
375
|
+
gateModeNotice,
|
|
376
|
+
// Single wording, shared with `peaks code auto-compact` — see
|
|
377
|
+
// `describeHarnessWindowSync`.
|
|
378
|
+
describeHarnessWindowSync(harnessWindow),
|
|
379
|
+
// AC3: the one-way hint that accompanies the `warnings` entry. Both
|
|
380
|
+
// channels carry the SAME fact in the shape each is read for
|
|
381
|
+
// (machine-readable warning vs human/LLM advice) — see
|
|
382
|
+
// `harnessWindowSyncWarning` / `describeHarnessWindowSync` above for
|
|
383
|
+
// the same split, and note this one never asks a question.
|
|
384
|
+
...(witnessNotice === null
|
|
385
|
+
? []
|
|
386
|
+
: ['Re-probe with `peaks code context-now` to confirm; this is reported, not blocking.'])
|
|
387
|
+
]),
|
|
388
|
+
// Slice 2026-09-13-auto-compact-trigger-ownership: was hard-coded
|
|
389
|
+
// `true`, which made the declared `--json` flag a no-op and left the
|
|
390
|
+
// human with raw JSON and no `next:` lines — so a person running this
|
|
391
|
+
// command could not see that peaks-loop had just written their
|
|
392
|
+
// harness settings. The notices above (including the key, the value
|
|
393
|
+
// and the rollback command) are `nextActions`, which `printResult`
|
|
394
|
+
// prints as `next: …` lines ONLY in the non-JSON path. Honoring the
|
|
395
|
+
// flag is what puts them in front of a human. See the consumer audit
|
|
396
|
+
// in the slice's RD artifact: every in-repo caller (the PreToolUse
|
|
397
|
+
// hooks, `orchestrator-can-do`, the skill runbooks) passes `--json`
|
|
398
|
+
// explicitly, so their byte-for-byte output is unchanged.
|
|
399
|
+
opts.json === true);
|
|
277
400
|
}
|
|
278
401
|
catch (err) {
|
|
279
402
|
printResult(io, fail('code.context-now', 'CONTEXT_NOW_FAILED', getErrorMessage(err), null, [
|