peaks-loop 4.1.0 → 4.1.1
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 +25 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/dist/cli/commands/_register.js +2 -2
- package/dist/cli/commands/code-mode-gate-should-pause-command.js +1 -1
- package/dist/cli/commands/comments-commands.d.ts +14 -0
- package/dist/cli/commands/comments-commands.js +96 -0
- package/dist/cli/commands/core/memory-command.js +10 -0
- package/dist/cli/commands/core/skill-command.js +1 -1
- package/dist/cli/commands/core/standards-command.js +1 -1
- package/dist/cli/commands/ecc-commands.d.ts +17 -22
- package/dist/cli/commands/ecc-commands.js +38 -26
- package/dist/cli/commands/prd-commands.js +8 -2
- package/dist/cli/program.js +4 -9
- package/dist/services/code/mode-gate-types.d.ts +1 -1
- package/dist/services/code/mode-gate-types.js +0 -1
- package/dist/services/code/mode-gate.js +1 -9
- package/dist/services/code/user-touchpoint-classifier.js +0 -7
- package/dist/services/code-review/ecc-bridge.d.ts +6 -6
- package/dist/services/comments/citation-rules.d.ts +154 -0
- package/dist/services/comments/citation-rules.js +241 -0
- package/dist/services/comments/comment-audit.d.ts +57 -0
- package/dist/services/comments/comment-audit.js +100 -0
- package/dist/services/comments/comment-citations.d.ts +60 -0
- package/dist/services/comments/comment-citations.js +186 -0
- package/dist/services/comments/comment-hygiene.d.ts +79 -0
- package/dist/services/comments/comment-hygiene.js +133 -0
- package/dist/services/comments/comment-prune.d.ts +88 -0
- package/dist/services/comments/comment-prune.js +148 -0
- package/dist/services/comments/prune-apply.d.ts +60 -0
- package/dist/services/comments/prune-apply.js +150 -0
- package/dist/services/comments/repo-path-probe.d.ts +43 -0
- package/dist/services/comments/repo-path-probe.js +78 -0
- package/dist/services/log/retention.d.ts +0 -16
- package/dist/services/log/retention.js +0 -17
- package/dist/services/memory/project-memory-service/index.d.ts +1 -1
- package/dist/services/memory/project-memory-service/index.js +1 -1
- package/dist/services/memory/project-memory-service/store/atomic-write.d.ts +19 -7
- package/dist/services/memory/project-memory-service/store/atomic-write.js +120 -26
- package/dist/services/prd/handoff-frontmatter.js +61 -0
- package/dist/services/prd/handoff-gate-evidence.js +14 -10
- package/dist/services/prd/handoff-service.d.ts +11 -1
- package/dist/services/prd/handoff-service.js +11 -1
- package/dist/services/prd/handoff-types.d.ts +33 -1
- package/dist/services/recommendations/installed-capability-detector.d.ts +5 -5
- package/dist/services/recommendations/installed-capability-detector.js +11 -10
- package/dist/services/scan/archetype-detection.d.ts +37 -0
- package/dist/services/scan/archetype-detection.js +175 -2
- package/dist/services/scan/archetype-service.js +36 -22
- package/dist/services/scan/scan-types.d.ts +9 -0
- package/dist/services/workspace/generated-artifacts-stamp.d.ts +2 -2
- package/dist/services/workspace/generated-artifacts-stamp.js +2 -2
- package/dist/services/workspace/workspace-service.js +1 -1
- package/package.json +5 -5
- package/scripts/install-skills.mjs +0 -177
- package/skills/bee/peaks-qa/references/reading-handoff-frontmatter.md +3 -1
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +1 -1
- package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +3 -2
- package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +29 -7
- package/skills/peaks-code/references/frontend-only-mode.md +2 -2
- package/skills/peaks-code/references/startup-sequence.md +0 -4
- package/dist/cli/commands/upgrade-commands.d.ts +0 -25
- package/dist/cli/commands/upgrade-commands.js +0 -154
- package/dist/services/upgrade/1x-detector-service.d.ts +0 -7
- package/dist/services/upgrade/1x-detector-service.js +0 -96
- package/dist/services/upgrade/gitignore-migrate-service.d.ts +0 -56
- package/dist/services/upgrade/gitignore-migrate-service.js +0 -170
- package/dist/services/upgrade/upgrade-service.d.ts +0 -81
- package/dist/services/upgrade/upgrade-service.js +0 -428
- package/skills/peaks-code/references/step-0-55-1x-detection.md +0 -83
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.1.1 — 2026-10-05 (一次"闸自己坏了整整一个版本"、注释债第一次进棘轮、以及五个从真实使用里捞出来的缺陷)
|
|
4
|
+
|
|
5
|
+
**版本级别**: 未发布区间 `v4.1.0..HEAD` 共 **14 个 commit**(6 fix / 4 feat / 1 test / 1 refactor / 1 docs / 1 chore)。**取 patch,而不是按 4.1.0 自己写下的判据取 minor** —— 这条判据不是被推翻,而是被有意收窄:本版新增的两条命令(`peaks comments audit` / `peaks comments prune`)是**仓库自己的维护表面**,只读或只动本仓注释,不改变任何既有命令的输入输出,也不要求下游做任何事;对外承诺的内容全是修复(其中一条是门禁崩溃)。`^4.1.0` 的下游直接匹配本版。4 个 workspace 包各 +1 patch(internal-runtime 0.0.40→0.0.41、mut 0.1.54→0.1.55、shared 0.0.89→0.0.90、shared-channel 0.0.57→0.0.58),顺带把"本地 manifest 比 registry 新"(mut 的 0.1.54 从未发布)这个坑一次清掉:`scripts/release-pack.mjs` 按拓扑序把 5 个包在同一次 CI 运行里一起发出去,`pnpm pack` 把 `workspace:*` 重写成精确 pin,实测 tarball 内 `CLI_VERSION` 为 4.1.1。
|
|
6
|
+
|
|
7
|
+
### 1. 门禁自己坏了一个版本(`78cb982f`,用户可见)
|
|
8
|
+
|
|
9
|
+
`node .husky/peaks-gate.mjs file-size` 从 `28b5145f` 起**每次都崩**:那次提交把 comment 腿从 `.husky/gate/legs.mjs` 搬出去时,连带删掉了紧贴它的两段 import(`./context.mjs` 与 `peaks-gate-file-size.mjs`),而 file-size 腿的函数体留在原地 —— `ReferenceError: refuseScopedSubset is not defined`。没有一条臂跑过整条 file-size 模式,所以它活着进了主干;恢复 import 后 `file-size-gate-leg` / `-excess-` / `-hooks-` 三家 21 条臂当场转绿。同一批 78 条臂红的第二个成因是工装:`.husky/file-size/measure.mjs` 与 `.husky/peaks-gate-comment-hygiene.mjs` 都通过 `node_modules/tsx/dist/cli.mjs` spawn 工具,而四个 fixture builder 把这个 tsx 替身写死成"任何调用都回 census 信封"。census 曾是唯一的 tsx 调用方,所以这写法无害了三个版本;comment 腿上线后它收到 census JSON、在里面找不到 `deadReferences`,于是拒绝 seed —— 现在是一份按请求工具分派的替身,顺带把四份逐字复制的 census 信封合成一份。
|
|
10
|
+
|
|
11
|
+
### 2. 注释债第一次进棘轮(4 feat)
|
|
12
|
+
|
|
13
|
+
`peaks comments audit` 出只读清单(dead-reference / narrative 两类,带归属文件),`peaks comments prune` 是执行器,它的**交付物是证明而不是删除**:在真树副本上计划 1677 处、覆盖 484 文件、拒绝 7 处,注释-only 证明 0 问题,残余计数 4+3 恰好等于拒绝数 —— 证明方式是重读改前改后文本、逐行比对非注释行与代码 token,任何一条代码文本变动都拒绝写入。两行 ceiling `commentDeadReferences` / `commentNarrativeLines` 入闸,seed 于**强制范围内实测**的 25 / 1515(952 个 tracked 文件,与 eslint / prettier / tsc / silent-warning / file-size 读同一份清单)。分类器是**先校准再入闸**的:188 → 65(把 standards 守卫的 5 条候选排除规则搬过来)→ 28(邻居解析改为 sibling-only,实测每一次祖先上溯带来 16 次误报)→ 25(再加"句子在推理路径"与包内相对根两条规则)。分类器与守卫的排除规则由一条 parity 臂钉住(读守卫源码里的字面量比对,不抄表)。
|
|
14
|
+
|
|
15
|
+
### 3. 五个从真实使用里捞出来的缺陷(`c08bf736` / `23af9c1d` / `2e323344`)
|
|
16
|
+
|
|
17
|
+
1.x→2.0 的升级路径整条删除(含 gate step),不再让一个已经退役的迁移分支继续决定今天的状态;`frontendOnly` 对隐藏目录 / 非 Node 后端的**漏判**修掉(有后端却报 true,用户据此关掉了一整条后端保护);ECC 从 GitHub tarball 拉取改为读 `ecc-universal` npm 依赖, acquisition 层删除,`ecc status` 从此只报告它能不吞异常说出的东西;`peaks memory extract` 的凭证拒绝变成**可诊断**(原先只说"有凭证",且误杀形状过宽),并收紧了两类假阳性;PRD 交接的 frontmatter / `gateEvidence` 字段由文档断言变成代码真产 —— 这是本仓最主的一类缺陷:prose 声称的字段实现里没有。
|
|
18
|
+
|
|
19
|
+
### 4. 一个钉在历史 commit 上的等价守卫,收窄而不是退役(owner 决定 2026-10-05)
|
|
20
|
+
|
|
21
|
+
`baseline-split-equivalence.test.ts` 把拆分前的 799 行 monolith 钉在 `77b711ff`,与今天的 11 模块版逐字节对比。两行新 ceiling 落在**两侧共享**的 `keys.mjs` 里,于是旧程序被要求核准它自己没有腿去量的行 —— 它诚实地拒绝了(`NOT THE CANONICAL CEILING SET`)。逐字节等价在"行集会增长"的棘轮上不可能再次成立,可选的只有收窄与退役,选了前者:参考侧现在读**自己那个时代**的 `keys.mjs`(一个被交给今天的清单的钉死程序,已经不是那个程序了),投影只允许吞掉"锚点之后 `CEILING_KEYS` 新增的行",而这个集合由两份真实清单求差得出(`ceilingKeysInText` 读 git blob 与工作树),没有任何字面量;`_split-anchor-era.ts` 持有唯一的 sha,`baseline-split-anchor.test.ts` 证明 harness 不留第二份。`rescope-projection-bounded.test.ts` 加 4 条臂:种一条**不属于 era** 的 moved-row,它的 bullet、header、计数与差异全部保留;note 下的 bullet 若全是 era 行则整条随计数一同消退;comment 腿的两行 stderr 必须由其**自己的 emitter** 或源码字面量证明真的会打印;`projectArtifact` 只掉它被交出去的键,其余照旧逐字节比。
|
|
22
|
+
|
|
23
|
+
### 5. 本版验证
|
|
24
|
+
|
|
25
|
+
tsc 0;`pnpm build` 0;全量 unit **388 files / 4082 passed / 3 skipped**;`node .husky/peaks-gate.mjs repo` exit 0,19 行 ceiling 全部 held(`eslint 2098/803`、`comment dead-ref 25 / narrative 1515`、`silent-warn 44/62`、`file-size 127 / 40137`、`hooks 0/0`);`peaks release precheck --strict` 0;版本 parity `shared-dist` / `runtime-src` 双 OK;`PEAKS_DRY_RUN=1 scripts/release-pack.mjs` 5 个 tarball 全部通过 registry 校验(无 `workspace:*` 残留)。artifacts 侧:baseline 只重写了**报告用**的 shadow 块(588 files / 643 findings / 160 errors / 36 over-cap / 13349 excess,增量是那个新进的 era helper 文件),没有任何 ceiling 移动。
|
|
26
|
+
|
|
27
|
+
|
|
3
28
|
## 4.1.0 — 2026-10-03 (strict-remediation 战役收口: 门禁第一次开始看自己、判据换成"哪些代码是产品"、以及一次把自己推翻的探针)
|
|
4
29
|
|
|
5
30
|
**版本级别**: 未发布区间 `v4.0.54..HEAD` 共 **167 个 commit**(49 refactor / 42 docs / 35 fix / 8 feat / 6 test / 15 chore / 1 style)。取 **minor** 而不是继续 patch,理由是这区间有**新增的用户可见 CLI 表面**(`peaks job add-slice`)以及若干会改变用户所见的修复(cron 超时、`pnpm lint` 的口径、空测试集从"绿"变"红");`^4.0.54` 的下游仍能匹配到本版。
|
package/README-en.md
CHANGED
|
@@ -155,7 +155,7 @@ Every lane opens with **one slash command**.
|
|
|
155
155
|
|
|
156
156
|
| | |
|
|
157
157
|
| --- | --- |
|
|
158
|
-
| **Latest** | [](https://www.npmjs.com/package/peaks-loop) — 4.1.
|
|
158
|
+
| **Latest** | [](https://www.npmjs.com/package/peaks-loop) — 4.1.1 (2026-10-05) |
|
|
159
159
|
| **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`) |
|
|
160
160
|
| **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 |
|
|
161
161
|
| **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
|
@@ -154,7 +154,7 @@ npm i -g peaks-loop
|
|
|
154
154
|
|
|
155
155
|
| | |
|
|
156
156
|
| --- | --- |
|
|
157
|
-
| **最新版本** | [](https://www.npmjs.com/package/peaks-loop) — 4.1.
|
|
157
|
+
| **最新版本** | [](https://www.npmjs.com/package/peaks-loop) — 4.1.1(2026-10-05) |
|
|
158
158
|
| **覆盖域** | 代码(`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` 切片) |
|
|
159
159
|
| **沉淀池** | `~/.peaks/` 本地池 · 跑两次自动晋升成 bee · 跑翻车让你重定义 · bee 跟着你的口味长 |
|
|
160
160
|
| **测试套件** | 1096 cases · 4 packages (peaks-loop 1015 / runtime 39 / mut 22 / shared-channel 20) · **CI 首次全绿**(ubuntu + windows) · 14 BDD caller-binding coverage |
|
|
@@ -10,6 +10,7 @@ import { registerCodeCommands } from './code-commands.js';
|
|
|
10
10
|
import { registerCodeGateCommand } from './code-gate-command.js';
|
|
11
11
|
import { registerDashboardCommands } from './dashboard-commands.js';
|
|
12
12
|
import { registerCodeReviewCommands } from './code-review-commands.js';
|
|
13
|
+
import { registerCommentsCommands } from './comments-commands.js';
|
|
13
14
|
import { registerCodegraphCommands } from './codegraph-commands.js';
|
|
14
15
|
import { registerCompactCommands } from './compact-command.js';
|
|
15
16
|
import { registerComplexityCommands } from './complexity-commands.js';
|
|
@@ -79,7 +80,6 @@ import { registerSubAgentShutdownCommands } from './sub-agent-shutdown-commands.
|
|
|
79
80
|
import { registerSubAgentDispatchGuard } from './sub-agent-dispatch-guard.js';
|
|
80
81
|
import { registerTestCommands } from './test-commands.js';
|
|
81
82
|
import { registerVendorDetectCommand } from './vendor-detect.js';
|
|
82
|
-
import { registerUpgradeCommands } from './upgrade-commands.js';
|
|
83
83
|
import { registerUserTouchpointCommands } from './user-touchpoint-commands.js';
|
|
84
84
|
import { registerVerdictAggregateCommands } from './verdict-aggregate-command.js';
|
|
85
85
|
import { registerWebCommands } from './web-commands.js';
|
|
@@ -158,8 +158,8 @@ const REGISTRATIONS = [
|
|
|
158
158
|
['bee-commands', registerBeeCommands],
|
|
159
159
|
['ecc-commands', registerEccCommands],
|
|
160
160
|
['baseline-commands', registerBaselineCommands],
|
|
161
|
-
['upgrade-commands', registerUpgradeCommands],
|
|
162
161
|
['code-review-commands', registerCodeReviewCommands],
|
|
162
|
+
['comments-commands', registerCommentsCommands],
|
|
163
163
|
['security-audit-commands', registerSecurityAuditCommands],
|
|
164
164
|
['perf-audit-commands', registerPerfAuditCommands],
|
|
165
165
|
['verdict-aggregate-command', registerVerdictAggregateCommands],
|
|
@@ -67,7 +67,7 @@ function validateModeAndStep(opts) {
|
|
|
67
67
|
if (!GATED_STEPS.includes(opts.step)) {
|
|
68
68
|
return {
|
|
69
69
|
ok: false,
|
|
70
|
-
envelope: fail('code.should-pause', 'INVALID_STEP', `step must be one of the
|
|
70
|
+
envelope: fail('code.should-pause', 'INVALID_STEP', `step must be one of the ${GATED_STEPS.length} GATED_STEPS (got "${opts.step}")`, { provided: opts.step, allowed: [...GATED_STEPS] }, [`Pass --step <one of the ${GATED_STEPS.length} GATED_STEPS>`])
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
return { ok: true, mode, step: opts.step };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks comments` — the read side of comment hygiene.
|
|
3
|
+
*
|
|
4
|
+
* `peaks comments audit` reports which comments in the product scope assert
|
|
5
|
+
* something the repository no longer supports, and which are development
|
|
6
|
+
* narrative rather than documentation. It is read-only on purpose: the number it
|
|
7
|
+
* prints is the number a later ratchet would be seeded from, and a ceiling is
|
|
8
|
+
* only trustworthy once a human has read a list of what the rule flags.
|
|
9
|
+
*
|
|
10
|
+
* Remediation is the next slice's job, not this command's.
|
|
11
|
+
*/
|
|
12
|
+
import type { Command } from 'commander';
|
|
13
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
14
|
+
export declare function registerCommentsCommands(program: Command, io: ProgramIO): void;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks comments` — the read side of comment hygiene.
|
|
3
|
+
*
|
|
4
|
+
* `peaks comments audit` reports which comments in the product scope assert
|
|
5
|
+
* something the repository no longer supports, and which are development
|
|
6
|
+
* narrative rather than documentation. It is read-only on purpose: the number it
|
|
7
|
+
* prints is the number a later ratchet would be seeded from, and a ceiling is
|
|
8
|
+
* only trustworthy once a human has read a list of what the rule flags.
|
|
9
|
+
*
|
|
10
|
+
* Remediation is the next slice's job, not this command's.
|
|
11
|
+
*/
|
|
12
|
+
import { auditComments } from '../../services/comments/comment-audit.js';
|
|
13
|
+
import { pruneComments } from '../../services/comments/comment-prune.js';
|
|
14
|
+
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
15
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
16
|
+
const KINDS = ['dead-reference', 'narrative'];
|
|
17
|
+
function parseKind(value) {
|
|
18
|
+
return KINDS.find((kind) => kind === value);
|
|
19
|
+
}
|
|
20
|
+
function nextActions(result) {
|
|
21
|
+
const lines = [
|
|
22
|
+
`dead-reference: ${result.deadReferences} comment line(s) cite a path that resolves nowhere.`,
|
|
23
|
+
`narrative: ${result.narrative} comment line(s) are about the work, not the code.`
|
|
24
|
+
];
|
|
25
|
+
if (result.deadReferences > 0) {
|
|
26
|
+
lines.push('Fix the citation or delete the claim — a comment that names a deleted file is an instruction to go look for it.');
|
|
27
|
+
}
|
|
28
|
+
lines.push('Worst files first: --json carries per-line records with the matched rule.');
|
|
29
|
+
return lines;
|
|
30
|
+
}
|
|
31
|
+
/** The `prune` action: plan, prove, and only then write. */
|
|
32
|
+
function runPrune(io, options) {
|
|
33
|
+
const kind = parseKind(options.kind);
|
|
34
|
+
if (options.kind !== undefined && kind === undefined) {
|
|
35
|
+
printResult(io, fail('comments.prune', 'INVALID_KIND', `--kind must be one of: ${KINDS.join(', ')}`, { provided: options.kind }, ['Re-run with a supported --kind, or omit it for both categories.']), options.json === true);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const result = pruneComments({
|
|
40
|
+
projectRoot: options.project ?? process.cwd(),
|
|
41
|
+
...(kind === undefined ? {} : { kinds: [kind] }),
|
|
42
|
+
...(options.file === undefined ? {} : { onlyFile: options.file }),
|
|
43
|
+
...(options.apply === true ? { apply: true } : {})
|
|
44
|
+
});
|
|
45
|
+
const actions = [
|
|
46
|
+
result.applied
|
|
47
|
+
? `Ledger: ${result.ledgerPath} records every line removed and every refusal.`
|
|
48
|
+
: 'Dry run: nothing was written. Pass --apply to write, after reading the plan.',
|
|
49
|
+
result.skipped > 0 ? `${result.skipped} finding(s) refused — see skipped reasons.` : undefined,
|
|
50
|
+
result.notWritten.length > 0 ? `Files not written: ${result.notWritten.join(', ')}` : undefined
|
|
51
|
+
].filter((line) => line !== undefined);
|
|
52
|
+
printResult(io, ok('comments.prune', result, [], actions), options.json === true);
|
|
53
|
+
}
|
|
54
|
+
/** The `audit` action, named so the registration above stays a registration. */
|
|
55
|
+
function runAudit(io, options) {
|
|
56
|
+
const projectRoot = options.project ?? process.cwd();
|
|
57
|
+
const kind = parseKind(options.kind);
|
|
58
|
+
if (options.kind !== undefined && kind === undefined) {
|
|
59
|
+
printResult(io, fail('comments.audit', 'INVALID_KIND', `--kind must be one of: ${KINDS.join(', ')}`, { provided: options.kind }, ['Re-run with a supported --kind, or omit it to scan both.']), options.json === true);
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const limit = options.limit === undefined ? undefined : Number.parseInt(options.limit, 10);
|
|
65
|
+
const result = auditComments({
|
|
66
|
+
projectRoot,
|
|
67
|
+
...(kind === undefined ? {} : { kind }),
|
|
68
|
+
...(limit === undefined || Number.isNaN(limit) ? {} : { limit })
|
|
69
|
+
});
|
|
70
|
+
printResult(io, ok('comments.audit', result, [], nextActions(result)), options.json === true);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
printResult(io, fail('comments.audit', 'COMMENTS_AUDIT_FAILED', getErrorMessage(error), {}, [
|
|
74
|
+
'Check --project points at a repository root.'
|
|
75
|
+
]), options.json === true);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function registerCommentsCommands(program, io) {
|
|
80
|
+
const comments = program
|
|
81
|
+
.command('comments')
|
|
82
|
+
.description('Measure comment debt in product code: stale references and process narrative.');
|
|
83
|
+
addJsonOption(comments
|
|
84
|
+
.command('audit')
|
|
85
|
+
.description('Read-only scan of src/** and packages/*/src/** comments.')
|
|
86
|
+
.option('--project <path>', 'project root to scan (default: cwd)')
|
|
87
|
+
.option('--kind <kind>', `one of: ${KINDS.join(', ')}`)
|
|
88
|
+
.option('--limit <n>', 'cap the per-line findings printed (totals are never capped)')).action((options) => runAudit(io, options));
|
|
89
|
+
addJsonOption(comments
|
|
90
|
+
.command('prune')
|
|
91
|
+
.description('Remove the comment lines the scan named, and prove the diff is comment-only first. Dry run unless --apply.')
|
|
92
|
+
.option('--project <path>', 'project root to prune (default: cwd)')
|
|
93
|
+
.option('--kind <kind>', `one of: ${KINDS.join(', ')}`)
|
|
94
|
+
.option('--file <path>', 'one repo-relative file, for a narrow first run')
|
|
95
|
+
.option('--apply', 'write the files (default: plan and prove only)')).action((options) => runPrune(io, options));
|
|
96
|
+
}
|
|
@@ -217,5 +217,15 @@ function memoryExtractNextActions(refusal) {
|
|
|
217
217
|
'Retitle the memory so it is not named after a credential term, then re-run memory extract'
|
|
218
218
|
];
|
|
219
219
|
}
|
|
220
|
+
if (refusal.check === SENSITIVE_MEMORY_CHECKS.content) {
|
|
221
|
+
// Name the rule, not just the family. `matchedTerm` is a closed id
|
|
222
|
+
// (`credential-assignment`, `bearer-value`, `pem-private-key`, …), so the
|
|
223
|
+
// reader can tell a credential in the text from a sentence that merely has
|
|
224
|
+
// the shape of one — which is the difference between removing a value and
|
|
225
|
+
// rewording prose.
|
|
226
|
+
return [
|
|
227
|
+
`Remove the credential value the "${refusal.matchedTerm}" pattern matched, then re-run memory extract`
|
|
228
|
+
];
|
|
229
|
+
}
|
|
220
230
|
return ['Remove the credential value from the memory content, then re-run memory extract'];
|
|
221
231
|
}
|
|
@@ -136,7 +136,7 @@ function generatedConfigNotice(projectRoot) {
|
|
|
136
136
|
warnings: [
|
|
137
137
|
`Generated config at '${projectRoot}' was produced by peaks-loop ${onDiskVersion} ` +
|
|
138
138
|
`but ${staleness.expected.packageVersion} is installed (${staleness.reasons.join(', ')}). ` +
|
|
139
|
-
`Re-run \`peaks workspace init\`
|
|
139
|
+
`Re-run \`peaks workspace init\` ` +
|
|
140
140
|
`to regenerate .claude/settings.local.json and the offline template copy.`
|
|
141
141
|
]
|
|
142
142
|
};
|
|
@@ -145,7 +145,7 @@ export function registerStandardsCommand(program, io) {
|
|
|
145
145
|
.description('Rewrite a consumer project CLAUDE.md to drop the legacy heartbeat block (slice 028). Dry-run by default; pass --apply to write. With --from-claude-rules, thins the 1.x .claude/rules/ tree to 2-line pointers and scaffolds .peaks/standards/ (slice 2026-06-12-standards-migrate-claude-rules).')
|
|
146
146
|
.option('--project <path>', 'target project root')
|
|
147
147
|
.option('--apply', 'rewrite the legacy block in place; default is dry-run')
|
|
148
|
-
.option('--from-claude-rules', 'thin .claude/rules/ to pointers and scaffold .peaks/standards/
|
|
148
|
+
.option('--from-claude-rules', 'thin .claude/rules/ to pointers and scaffold .peaks/standards/')).action((options) => {
|
|
149
149
|
const projectRoot = options.project ?? process.cwd();
|
|
150
150
|
if (options.fromClaudeRules === true) {
|
|
151
151
|
try {
|
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `peaks ecc install|status|ls|show` —
|
|
2
|
+
* `peaks ecc install|status|ls|show` — the ECC read layer.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* ECC's agent definitions come from the `ecc-universal` npm dependency of
|
|
5
|
+
* `peaks-loop-mut`. `install` copies `<package>/agents/*.md` into the
|
|
6
|
+
* plugin-free dir the LLM reads (`~/.peaks/agents/ecc/`); `status`, `ls` and
|
|
7
|
+
* `show` read that copy. There is no download anywhere in this file: the
|
|
8
|
+
* previous design fetched a GitHub release tarball on the premise that the
|
|
9
|
+
* project was not on npm, and that premise was false (`ecc-universal`
|
|
10
|
+
* `repository.url` is `git+https://github.com/affaan-m/ECC.git`).
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - `peaks ecc
|
|
16
|
-
*
|
|
17
|
-
* first body line when frontmatter is malformed).
|
|
18
|
-
* - `peaks ecc show <name> [--section H] [--max-lines N]` prints
|
|
19
|
-
* the SKILL.md body to stdout — this is the Skill-first path
|
|
20
|
-
* the LLM consumes directly.
|
|
12
|
+
* - `peaks ecc install` — land/refresh the copy. Idempotent; prunes agents the
|
|
13
|
+
* installed package no longer ships.
|
|
14
|
+
* - `peaks ecc status` — package version + what landed and when.
|
|
15
|
+
* - `peaks ecc ls` — the roster with declared name + description (D-009
|
|
16
|
+
* fallback to filename + first body line when frontmatter is malformed).
|
|
17
|
+
* - `peaks ecc show <name> [--section H] [--max-lines N]` — the agent body;
|
|
18
|
+
* this is the Skill-first path the LLM consumes directly.
|
|
21
19
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Per the "Enhancement, not new AI CLI" tenet: this command is a
|
|
26
|
-
* download + read-only access layer. There is no `peaks ecc run`.
|
|
20
|
+
* Per the "Enhancement, not new AI CLI" tenet: read-only access plus a local
|
|
21
|
+
* copy. There is no `peaks ecc run`, and no ECC subprocess is spawned.
|
|
27
22
|
*/
|
|
28
23
|
import type { Command } from 'commander';
|
|
29
24
|
import { type ProgramIO } from '../cli-helpers.js';
|
|
@@ -1,53 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EccSourceError, installEccAgents, listEccAgents, readEccMaterializeManifest, readMaterializedAgent } from 'peaks-loop-mut';
|
|
2
2
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
3
3
|
import { fail, ok } from 'peaks-loop-shared/result';
|
|
4
4
|
export function registerEccCommands(program, io) {
|
|
5
5
|
const ecc = program
|
|
6
6
|
.command('ecc')
|
|
7
|
-
.description('
|
|
7
|
+
.description('ECC agents from the bundled `ecc-universal` dependency: copy + read-only access for the LLM (no subprocess; no peaks agent run).');
|
|
8
8
|
addJsonOption(ecc
|
|
9
9
|
.command('install')
|
|
10
|
-
.description('
|
|
11
|
-
|
|
12
|
-
const asJson = options.json === true;
|
|
10
|
+
.description('Copy <node_modules>/ecc-universal/agents/*.md into ~/.peaks/agents/ecc/ (idempotent).')).action((_options) => {
|
|
11
|
+
const asJson = _options.json === true;
|
|
13
12
|
try {
|
|
14
|
-
const result =
|
|
13
|
+
const result = installEccAgents();
|
|
15
14
|
const envelope = ok('ecc.install', result, [], [
|
|
16
|
-
`
|
|
17
|
-
`
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
`Materialized ${result.materialized.length} ECC agent(s) at ${result.targetDir}`,
|
|
16
|
+
`Source: ecc-universal@${result.packageVersion}`,
|
|
17
|
+
'Inspect with: peaks ecc ls',
|
|
18
|
+
'Consume one agent with: peaks ecc show <name>'
|
|
20
19
|
]);
|
|
21
20
|
printResult(io, envelope, asJson);
|
|
22
21
|
}
|
|
23
22
|
catch (error) {
|
|
24
|
-
const
|
|
25
|
-
printResult(io, fail('ecc.install', '
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
const missing = error instanceof EccSourceError;
|
|
24
|
+
printResult(io, fail('ecc.install', missing ? 'ECC_PACKAGE_MISSING' : 'ECC_INSTALL_FAILED', getErrorMessage(error), { source: missing ? error.failure : 'copy-failed' }, missing
|
|
25
|
+
? [
|
|
26
|
+
'The `ecc-universal` dependency is not resolvable from this installation.',
|
|
27
|
+
'Reinstall peaks-loop (`npm i -g peaks-loop@latest`) — the ECC agents ship inside that dependency.',
|
|
28
|
+
'A partial or `--ignore-scripts` install can leave the dependency tree incomplete.'
|
|
29
|
+
]
|
|
30
|
+
: ['Check that ~/.peaks is writable, then re-run `peaks ecc install`.']), asJson);
|
|
31
31
|
process.exitCode = 1;
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
addJsonOption(ecc
|
|
35
35
|
.command('status')
|
|
36
|
-
.description('Show ECC
|
|
36
|
+
.description('Show the materialized ECC copy: source version, agent list, when it landed.')).action((options) => {
|
|
37
37
|
const asJson = options.json === true;
|
|
38
|
-
const manifest =
|
|
38
|
+
const manifest = readEccMaterializeManifest();
|
|
39
39
|
if (manifest === null) {
|
|
40
|
-
printResult(io, fail('ecc.status', '
|
|
40
|
+
printResult(io, fail('ecc.status', 'NOT_INSTALLED', 'No materialized ECC agents found. Run `peaks ecc install` first.', { installed: false }, [
|
|
41
|
+
'Run `peaks ecc install` to copy the bundled ecc-universal agents into ~/.peaks/agents/ecc/.'
|
|
42
|
+
]), asJson);
|
|
41
43
|
process.exitCode = 1;
|
|
42
44
|
return;
|
|
43
45
|
}
|
|
44
|
-
|
|
46
|
+
// `status` reads the copy and reports it. It does not re-probe the package:
|
|
47
|
+
// `manifest.packageVersion` already records which version landed the agents,
|
|
48
|
+
// and probing again would need either a throw that hides the answer or a
|
|
49
|
+
// swallowed error — the second is the class the repository's silent-catch
|
|
50
|
+
// ratchet counts, the first would turn "here is what the LLM can read" into a
|
|
51
|
+
// failure just because a dependency moved.
|
|
52
|
+
printResult(io, ok('ecc.status', manifest, [], [
|
|
53
|
+
`Materialized from ecc-universal@${manifest.packageVersion}.`,
|
|
54
|
+
'Inspect agents with: peaks ecc ls',
|
|
55
|
+
'Print one agent with: peaks ecc show <name>'
|
|
56
|
+
]), asJson);
|
|
45
57
|
});
|
|
46
58
|
addJsonOption(ecc
|
|
47
59
|
.command('ls')
|
|
48
|
-
.description('List
|
|
60
|
+
.description('List materialized agents from ~/.peaks/agents/ecc/*.md with parsed frontmatter.')).action((options) => {
|
|
49
61
|
const asJson = options.json === true;
|
|
50
|
-
const agents =
|
|
62
|
+
const agents = listEccAgents();
|
|
51
63
|
printResult(io, ok('ecc.ls', { agents }, [], ['Print one with: `peaks ecc show <name>`']), asJson);
|
|
52
64
|
});
|
|
53
65
|
addJsonOption(ecc
|
|
@@ -61,11 +73,11 @@ export function registerEccCommands(program, io) {
|
|
|
61
73
|
process.exitCode = 1;
|
|
62
74
|
return;
|
|
63
75
|
}
|
|
64
|
-
const body =
|
|
76
|
+
const body = readMaterializedAgent(name);
|
|
65
77
|
if (body === null) {
|
|
66
|
-
printResult(io, fail('ecc.show', 'NOT_FOUND', `agent "${name}" is not in the
|
|
78
|
+
printResult(io, fail('ecc.show', 'NOT_FOUND', `agent "${name}" is not in the materialized copy`, { name }, [
|
|
67
79
|
'Run `peaks ecc ls` to see available agents.',
|
|
68
|
-
'Or run `peaks ecc install` to (re-)
|
|
80
|
+
'Or run `peaks ecc install` to (re-)copy the bundled agents into ~/.peaks/agents/ecc/.'
|
|
69
81
|
]), asJson);
|
|
70
82
|
process.exitCode = 1;
|
|
71
83
|
return;
|
|
@@ -94,7 +94,12 @@ export function registerPrdCommands(program, io) {
|
|
|
94
94
|
bodyBytes: Buffer.byteLength(body, 'utf8'),
|
|
95
95
|
goals: handoff.frontmatter.goals,
|
|
96
96
|
acceptanceCriteria: handoff.frontmatter.acceptanceCriteria,
|
|
97
|
-
preservedBehavior: handoff.frontmatter.preservedBehavior
|
|
97
|
+
preservedBehavior: handoff.frontmatter.preservedBehavior,
|
|
98
|
+
// The derived declaration is printed, not just written. A reader
|
|
99
|
+
// deciding whether to `--apply` must be able to see which gate
|
|
100
|
+
// keys this slice declares and at what paths; before this, the
|
|
101
|
+
// only way to learn the five key names was to read the source.
|
|
102
|
+
gateEvidence: handoff.frontmatter.gateEvidence ?? null
|
|
98
103
|
}), options.json);
|
|
99
104
|
return;
|
|
100
105
|
}
|
|
@@ -104,7 +109,8 @@ export function registerPrdCommands(program, io) {
|
|
|
104
109
|
path: written.path,
|
|
105
110
|
hash: written.hash,
|
|
106
111
|
requestId: handoff.frontmatter.requestId,
|
|
107
|
-
sessionId: handoff.frontmatter.sessionId
|
|
112
|
+
sessionId: handoff.frontmatter.sessionId,
|
|
113
|
+
gateEvidence: handoff.frontmatter.gateEvidence ?? null
|
|
108
114
|
}), options.json);
|
|
109
115
|
}
|
|
110
116
|
catch (error) {
|
package/dist/cli/program.js
CHANGED
|
@@ -18,7 +18,7 @@ import { registerSopCommands } from './commands/sop-commands.js';
|
|
|
18
18
|
import { registerSkillVisibilityCommand } from './commands/skill-visibility.js';
|
|
19
19
|
import { registerPrimerCommand } from './commands/primer-command.js';
|
|
20
20
|
import { registerReinjectCommand } from './commands/reinject-command.js';
|
|
21
|
-
import { applyRetention
|
|
21
|
+
import { applyRetention } from '../services/log/retention.js';
|
|
22
22
|
import { writeLogEntry, maybeWriteStderr } from '../services/log/logger.js';
|
|
23
23
|
import { printSuperCommandCatalog } from './cli-helpers.js';
|
|
24
24
|
export { printErrorEnvelope, printResult } from './cli-helpers.js';
|
|
@@ -57,14 +57,9 @@ function bootstrapLogger(verbose) {
|
|
|
57
57
|
// TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
58
58
|
/* best-effort retention sweep; never block the CLI */
|
|
59
59
|
}
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
66
|
-
/* best-effort ECC retention sweep; never block the CLI */
|
|
67
|
-
}
|
|
60
|
+
// The ECC cache sweep that used to sit here is gone with the cache: ECC comes
|
|
61
|
+
// from the `ecc-universal` dependency, whose files expire when npm removes the
|
|
62
|
+
// package, not on a peaks-loop TTL.
|
|
68
63
|
const dateOverride = process.env.PEAKS_LOG_DATE_OVERRIDE;
|
|
69
64
|
const entry = {
|
|
70
65
|
ts: new Date().toISOString(),
|
|
@@ -21,7 +21,7 @@ export declare const CODE_MODES: readonly CodeMode[];
|
|
|
21
21
|
* "Inventory of unconditional confirmation gates" table. Each row
|
|
22
22
|
* is the bare minimum needed to gate it through `shouldPauseAtGate`.
|
|
23
23
|
*/
|
|
24
|
-
export type GatedStepId = 'step-0.5-openspec-opt-in' | 'step-0.6-audit-goal' | 'step-0.7-resume-detection' | 'step-
|
|
24
|
+
export type GatedStepId = 'step-0.5-openspec-opt-in' | 'step-0.6-audit-goal' | 'step-0.7-resume-detection' | 'step-1-mode-select' | 'step-2.5-session-title' | 'phase-2-prd-confirm' | 'phase-3-swarm-gate-b' | 'phase-6-qa-gate-d' | 'phase-10-txt-memory-extract' | 'step-n+1-final-review' | 'frontend-only-mismatch' | 'step-0.75-checkpoint-resume' | 'standards-preflight';
|
|
25
25
|
export declare const GATED_STEPS: readonly GatedStepId[];
|
|
26
26
|
/**
|
|
27
27
|
* v2.15.0 slice 002 AC-4: the 5 action identifiers that qualify as
|
|
@@ -74,19 +74,11 @@ export function shouldAutoProceed(mode) {
|
|
|
74
74
|
* without user consent (defect #1: new Code session skipped Step 1
|
|
75
75
|
* AskUserQuestion because `full-auto` mode triggered `shouldAutoProceed`
|
|
76
76
|
* on `step-1-mode-select` itself).
|
|
77
|
-
*
|
|
78
|
-
* v2.18.4 slice 002-fix-first-run-step-gates: added
|
|
79
|
-
* `step-0.55-1x-upgrade`. The 1.x → 2.0 upgrade is an irreversible
|
|
80
|
-
* external side effect (migration rewrites config files and the
|
|
81
|
-
* on-disk cache shape) — it must always pause, even in full-auto.
|
|
82
|
-
* Without this, `full-auto` mode auto-proceeds and silently
|
|
83
|
-
* downgrades a 1.x install to 2.0 without user consent.
|
|
84
77
|
*/
|
|
85
78
|
const HARD_PAUSE_STEPS = new Set([
|
|
86
79
|
'step-1-mode-select',
|
|
87
80
|
'step-0.5-openspec-opt-in',
|
|
88
|
-
'step-0.7-resume-detection'
|
|
89
|
-
'step-0.55-1x-upgrade'
|
|
81
|
+
'step-0.7-resume-detection'
|
|
90
82
|
]);
|
|
91
83
|
export function shouldPauseAtGate(opts) {
|
|
92
84
|
if (opts.hardFloorCategory !== undefined && isHardFloorCategory(opts.hardFloorCategory)) {
|
|
@@ -42,13 +42,6 @@ const RAW_CLASSIFICATION = [
|
|
|
42
42
|
fullAutoCanProceed: false,
|
|
43
43
|
userShouldReview: 'always'
|
|
44
44
|
},
|
|
45
|
-
{
|
|
46
|
-
step: 'step-0.55-1x-upgrade',
|
|
47
|
-
kind: 'commit-floor',
|
|
48
|
-
description: '1.x → 2.0 升级检测。不可逆外部副作用(改写 config + cache schema),所有 mode 都必须 AskUserQuestion。full-auto 也暂停。',
|
|
49
|
-
fullAutoCanProceed: false,
|
|
50
|
-
userShouldReview: 'always'
|
|
51
|
-
},
|
|
52
45
|
{
|
|
53
46
|
step: 'step-0.75-checkpoint-resume',
|
|
54
47
|
kind: 'tech',
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
*
|
|
22
22
|
* === Why no `peaksConfig.ecc.*` block ===
|
|
23
23
|
*
|
|
24
|
-
* ECC
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* block
|
|
24
|
+
* ECC ships as the `ecc-universal` npm package — the same repository as
|
|
25
|
+
* github.com/affaan-m/ECC — and peaks-loop depends on it, so the agents are read
|
|
26
|
+
* from that dependency, not downloaded. ECC may ALSO be present as the
|
|
27
|
+
* `everything-claude-code` Claude Code plugin, the native path `detectEcc`
|
|
28
|
+
* prefers. Either way peaks-loop writes and reads no LLM endpoint config for ECC;
|
|
29
|
+
* add a `peaksConfig.ecc.*` block only when a release actually needs one.
|
|
30
30
|
*
|
|
31
31
|
* === Source: peaks-rd/references/parallel-review-fanout.md ===
|
|
32
32
|
*
|