sillyspec 3.32.9 → 3.33.0
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/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -114
- package/.claude/skills/sillyspec-execute/SKILL.md +222 -222
- package/.claude/skills/sillyspec-explore/SKILL.md +107 -107
- package/.claude/skills/sillyspec-flow/SKILL.md +1 -1
- package/.claude/skills/sillyspec-plan/SKILL.md +156 -156
- package/.github/workflows/ci.yml +58 -0
- package/.husky/pre-push +7 -6
- package/AGENTS.md +1 -1
- package/assets/command-cards/flow.md +1 -1
- package/eval/eval.config.json +3 -3
- package/eval/lib/config.mjs +6 -3
- package/eval/lib/util.mjs +16 -3
- package/eval/lib/workdir.mjs +4 -1
- package/eval/results/eval.db +0 -0
- package/eval/results/logs/2026-10-15T12-37-22-986Z-004-phase-stats-armA/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T12-37-22-986Z-004-phase-stats-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T12-37-22-986Z-004-phase-stats-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T12-45-37-124Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T12-45-37-124Z-004-phase-stats-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T12-45-37-124Z-004-phase-stats-armB/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T12-56-19-259Z-004-phase-stats-armA/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T12-56-19-259Z-004-phase-stats-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T12-56-19-259Z-004-phase-stats-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T13-00-05-625Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-04-51-003Z-004-phase-stats-armA/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-04-51-003Z-004-phase-stats-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T13-04-51-003Z-004-phase-stats-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T13-06-25-271Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-06-25-271Z-004-phase-stats-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T13-06-25-271Z-004-phase-stats-armB/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T13-24-38-308Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-24-38-308Z-004-phase-stats-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T13-24-38-308Z-004-phase-stats-armB/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T14-50-01-950Z-005-orders-export-armA/prompt.txt +8 -0
- package/eval/results/logs/2026-10-15T14-50-01-950Z-005-orders-export-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T14-50-01-950Z-005-orders-export-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T14-51-56-316Z-005-orders-export-armB/prompt.txt +8 -0
- package/eval/results/logs/2026-10-15T14-51-56-316Z-005-orders-export-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T14-51-56-316Z-005-orders-export-armB/stdout.txt +1 -0
- package/eval/results/workdirs/001-slugify__armA/package.json +6 -0
- package/eval/results/workdirs/001-slugify__armA/src/slugify.mjs +26 -0
- package/eval/results/workdirs/001-slugify__armB/package.json +6 -0
- package/eval/results/workdirs/001-slugify__armB/src/slugify.mjs +26 -0
- package/eval/results/workdirs/002-each-day__armA/package.json +6 -0
- package/eval/results/workdirs/002-each-day__armA/src/dates.mjs +30 -0
- package/eval/results/workdirs/002-each-day__armB/DRY_RUN_WRONG_ANSWER.mjs +1 -0
- package/eval/results/workdirs/002-each-day__armB/package.json +6 -0
- package/eval/results/workdirs/002-each-day__armB/src/dates.mjs +4 -0
- package/eval/results/workdirs/003-word-report__armA/bin/report.mjs +36 -0
- package/eval/results/workdirs/003-word-report__armA/data/notes.txt +5 -0
- package/eval/results/workdirs/003-word-report__armA/package.json +6 -0
- package/eval/results/workdirs/003-word-report__armB/bin/report.mjs +36 -0
- package/eval/results/workdirs/003-word-report__armB/data/notes.txt +5 -0
- package/eval/results/workdirs/003-word-report__armB/package.json +6 -0
- package/eval/results/workdirs/004-phase-stats__armA/data/events.log +16 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/flow.md +44 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-archive.md +27 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-brainstorm.md +34 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-execute.md +30 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-plan.md +28 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-verify.md +30 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/status.md +28 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-auto/SKILL.md +54 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-continue/SKILL.md +17 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-execute/SKILL.md +222 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-explore/SKILL.md +107 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-flow/SKILL.md +62 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-knowledge/SKILL.md +40 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-propose/SKILL.md +16 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-resume/SKILL.md +21 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-state/SKILL.md +25 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-workspace/SKILL.md +33 -0
- package/eval/results/workdirs/004-phase-stats__armB/AGENTS.md +45 -0
- package/eval/results/workdirs/004-phase-stats__armB/CLAUDE.md +3 -0
- package/eval/results/workdirs/004-phase-stats__armB/data/events.log +16 -0
- package/eval/results/workdirs/run-001-slugify__armA/package.json +6 -0
- package/eval/results/workdirs/run-001-slugify__armA/src/slugify.mjs +26 -0
- package/eval/results/workdirs/run-001-slugify__armB/package.json +6 -0
- package/eval/results/workdirs/run-001-slugify__armB/src/slugify.mjs +26 -0
- package/eval/results/workdirs/run-002-each-day__armA/package.json +6 -0
- package/eval/results/workdirs/run-002-each-day__armA/src/dates.mjs +30 -0
- package/eval/results/workdirs/run-002-each-day__armB/DRY_RUN_WRONG_ANSWER.mjs +1 -0
- package/eval/results/workdirs/run-002-each-day__armB/package.json +6 -0
- package/eval/results/workdirs/run-002-each-day__armB/src/dates.mjs +4 -0
- package/eval/results/workdirs/run-003-word-report__armA/bin/report.mjs +36 -0
- package/eval/results/workdirs/run-003-word-report__armA/data/notes.txt +5 -0
- package/eval/results/workdirs/run-003-word-report__armA/package.json +6 -0
- package/eval/results/workdirs/run-003-word-report__armB/bin/report.mjs +36 -0
- package/eval/results/workdirs/run-003-word-report__armB/data/notes.txt +5 -0
- package/eval/results/workdirs/run-003-word-report__armB/package.json +6 -0
- package/eval/results/workdirs/run-004-phase-stats__armA/bin/phase-stats.mjs +37 -0
- package/eval/results/workdirs/run-004-phase-stats__armA/data/events.log +16 -0
- package/eval/results/workdirs/run-004-phase-stats__armB/DRY_RUN_WRONG_ANSWER.mjs +1 -0
- package/eval/results/workdirs/run-004-phase-stats__armB/data/events.log +16 -0
- package/eval/results/workdirs/run-005-orders-export__armA/bin/export-orders.mjs +57 -0
- package/eval/results/workdirs/run-005-orders-export__armA/data/.gitattributes +1 -0
- package/eval/results/workdirs/run-005-orders-export__armA/data/orders.csv +9 -0
- package/eval/results/workdirs/run-005-orders-export__armA/out/summary.json +1 -0
- package/eval/results/workdirs/run-005-orders-export__armB/bin/export-orders.mjs +57 -0
- package/eval/results/workdirs/run-005-orders-export__armB/data/.gitattributes +1 -0
- package/eval/results/workdirs/run-005-orders-export__armB/data/orders.csv +9 -0
- package/eval/results/workdirs/run-005-orders-export__armB/out/summary.json +1 -0
- package/eval/tasks/004-phase-stats/files/data/events.log +16 -0
- package/eval/tasks/004-phase-stats/solution/bin/phase-stats.mjs +37 -0
- package/eval/tasks/004-phase-stats/task.json +6 -0
- package/eval/tasks/004-phase-stats/verify.mjs +38 -0
- package/eval/tasks/005-orders-export/files/data/.gitattributes +1 -0
- package/eval/tasks/005-orders-export/files/data/orders.csv +9 -0
- package/eval/tasks/005-orders-export/solution/bin/export-orders.mjs +57 -0
- package/eval/tasks/005-orders-export/task.json +6 -0
- package/eval/tasks/005-orders-export/verify.mjs +28 -0
- package/package.json +1 -1
- package/quicklog/tmp-fix-review.mjs +21 -0
- package/src/ceremony-tier.js +2 -1
- package/src/commit-suggest.js +45 -4
- package/src/config-schema.js +14 -0
- package/src/decision-distill.js +106 -24
- package/src/decision-redomain.js +148 -0
- package/src/decision-renumber.js +157 -0
- package/src/design-facts.js +3 -3
- package/src/docs-check.js +30 -15
- package/src/docs-debt.js +6 -5
- package/src/doctor-diagnostics.js +3 -1
- package/src/flow-parity.js +28 -4
- package/src/flow.js +76 -13
- package/src/forensics.js +236 -0
- package/src/fr-index.js +17 -2
- package/src/hooks/worktree-guard.js +3 -3
- package/src/index.js +41 -6
- package/src/knowledge-digest.js +23 -6
- package/src/knowledge-graph.js +95 -18
- package/src/knowledge-match.js +34 -9
- package/src/knowledge-stats.js +12 -13
- package/src/knowledge-vector.js +98 -7
- package/src/machine-interface.js +26 -0
- package/src/module-impact.js +2 -5
- package/src/modules.js +31 -0
- package/src/quicklog.js +6 -1
- package/src/run/archive-distill.js +3 -0
- package/src/run/command.js +3 -3
- package/src/run/complete-handlers.js +8 -3
- package/src/run/complete.js +113 -44
- package/src/run/gates.js +100 -103
- package/src/run/lint-ownership.js +37 -0
- package/src/run/prompt.js +43 -28
- package/src/run/stage.js +1 -1
- package/src/run/verify-quality-scan.js +10 -1
- package/src/sentinel-assertions.js +43 -1
- package/src/spec-sync.js +81 -2
- package/src/stage-contract.js +26 -3
- package/src/stage-review.js +46 -12
- package/src/stage-templates.js +2 -0
- package/src/stages/brainstorm.js +76 -21
- package/src/stages/knowledge.js +56 -5
- package/src/stages/plan-postcheck.js +13 -2
- package/src/stages/plan.js +19 -0
- package/src/sync.js +28 -2
- package/src/task-tick.js +12 -0
- package/src/test-bindings.js +32 -3
- package/src/timeline.js +60 -19
- package/src/ui-visual.js +119 -4
- package/src/verify-postcheck.js +166 -18
- package/src/verify-probes.js +61 -18
- package/src/worktree-apply.js +26 -2
- package/templates/agents-instruction.md +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sillyspec:scan
|
|
3
|
+
description: 用于扫描项目代码库,生成架构文档、代码约定、目录结构等。适合用户说"扫描项目、分析代码库、生成文档、scan"。产出 7 份扫描文档 + 模块映射。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 何时使用
|
|
7
|
+
|
|
8
|
+
- 用户说"扫描项目、分析代码库、生成文档、scan"
|
|
9
|
+
- 棕地项目首次接入 sillyspec,生成架构/约定/结构等基础文档
|
|
10
|
+
- 产出 7 份 scan 文档(PROJECT/ARCHITECTURE/CONVENTIONS/STRUCTURE/INTEGRATIONS/TESTING/CONCERNS)+ 模块映射
|
|
11
|
+
|
|
12
|
+
## 多变更说明
|
|
13
|
+
|
|
14
|
+
scan 是辅助阶段,通常不需要 `--change`。但项目有多个活跃变更时,所有 `sillyspec run` 命令加 `--change <变更名>` 可指定操作目标。
|
|
15
|
+
|
|
16
|
+
## 步骤生命周期(所有阶段通用)
|
|
17
|
+
|
|
18
|
+
> `sillyspec scan` 是 `sillyspec run scan` 的顶层别名,两者等价。
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
sillyspec run scan # 输出当前步骤 prompt
|
|
22
|
+
sillyspec run scan --done --output "摘要" # 完成当前步骤
|
|
23
|
+
sillyspec run scan --status # 查看阶段进度
|
|
24
|
+
sillyspec run scan --skip # 跳过可选步骤
|
|
25
|
+
sillyspec run scan --reset # 重置阶段(从头开始)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 通用参数(所有阶段适用)
|
|
29
|
+
|
|
30
|
+
| 参数 | 说明 |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
|
|
33
|
+
| `--non-interactive` | CI/脚本下禁用交互式 prompt |
|
|
34
|
+
| `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
|
|
35
|
+
|
|
36
|
+
## scan 特有参数
|
|
37
|
+
|
|
38
|
+
| 参数 | 说明 |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `--quick` | 强制 quick profile(快速接入,仅 4 份核心文档,0 子代理) |
|
|
41
|
+
| `--standard` | 强制 standard profile(压缩步骤,最多 1 子代理) |
|
|
42
|
+
| `--deep` | 强制 deep profile(完整流程,不按规模裁剪) |
|
|
43
|
+
| `--force-rescan` | 覆盖已有 scan 文档的保护(默认覆盖需 source_commit/updated_at 匹配) |
|
|
44
|
+
| `--diff` | 增量漂移清单:`sillyspec scan diff`(或 `--diff` flag)算 source_commit..HEAD 的 A/D/M/R 四分类漂移,按 module-map paths 归模块,agent 据此定点补文档而非全量重扫(2026-08-16) |
|
|
45
|
+
|
|
46
|
+
### scanProfile(显式选择优先,否则按规模自动裁剪)
|
|
47
|
+
|
|
48
|
+
可用 `--quick` / `--standard` / `--deep` **显式指定 profile**(三档互斥,优先于自动判定)。
|
|
49
|
+
不带 flag 时 CLI 按源码规模自动选择:
|
|
50
|
+
|
|
51
|
+
| profile | 触发条件 | 行为 |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| quick | `--quick` 或 ≤30 文件 且 ≤80KB 且 ≤3 项目 | 3 步,0 子代理,4 份核心文档 |
|
|
54
|
+
| standard | `--standard` 或 ≤200 文件 且 ≤800KB | 压缩步骤,最多 1 子代理 |
|
|
55
|
+
| deep | `--deep` 或 大项目 | 完整流程 |
|
|
56
|
+
|
|
57
|
+
> **平台快速接入**:对大项目用 `sillyspec run scan --quick`(含平台参数 `--spec-root`/`--workspace-id`/`--scan-run-id`)只生成 4 份核心文档完成接入,后续 `sillyspec run scan --deep` 覆盖升级为完整 7 份。quick 文档 frontmatter 标 `scan_depth: quick`,深度扫描识别后允许覆盖。
|
|
58
|
+
|
|
59
|
+
### post-check
|
|
60
|
+
|
|
61
|
+
scan 完成时 CLI **按 profile 校验文档齐全**:quick 模式只要求 4 份核心文档(PROJECT/ARCHITECTURE/CONVENTIONS/STRUCTURE),standard/deep 要求完整 7 份。缺失会设状态为 `failed_post_check`,阻断进入主流程下游(brainstorm/plan 等),需修复后重跑 scan。quick 模式带一条 informational warning(`quick_profile_notice`),状态落 `completed_with_warnings`,不阻断完成。
|
|
62
|
+
|
|
63
|
+
## 阶段流转
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
(项目起点) → scan → brainstorm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
scan 完成后,运行 `sillyspec run brainstorm "<需求>"` 开始具体变更的设计。
|
|
70
|
+
|
|
71
|
+
## 铁律
|
|
72
|
+
|
|
73
|
+
- **必须用 exec 工具(shell)执行 CLI,不要自己编造流程**
|
|
74
|
+
- 只做当前步骤 prompt 描述的操作,不跳过
|
|
75
|
+
- scan 文档写入 `{DOCS_ROOT}/scan/`(平台模式用占位符路径,不写裸 `.sillyspec/`)
|
|
76
|
+
- 完成后立即 `--done`,不跳过
|
|
77
|
+
|
|
78
|
+
## 用户指令
|
|
79
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sillyspec:state
|
|
3
|
+
description: 查看当前工作状态 — 显示 SillySpec 进度
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 流程
|
|
7
|
+
|
|
8
|
+
跑 `sillyspec progress show [--change <名>]`,把 CLI 输出**原样转述**给用户即可——输出本身已是成品格式(变更/阶段/最近活跃/未决冲突/滞留信号/下一步建议),勿再按自备模板重排。
|
|
9
|
+
|
|
10
|
+
- 多活跃变更时输出为汇总表;需要单变更细节加 `--change <名>`。
|
|
11
|
+
- 输出「没有活跃的变更」时,提示用户:新项目 `/sillyspec:init`、已有项目 `/sillyspec:scan`、恢复中断 `/sillyspec:resume`。
|
|
12
|
+
|
|
13
|
+
## progress 只读子命令(诊断用)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
sillyspec progress check # 状态一致性检查(只报告,不修复)
|
|
17
|
+
sillyspec progress repair # 修复状态元数据(dry-run,加 --apply 才真改)
|
|
18
|
+
sillyspec progress validate # 校验并修复
|
|
19
|
+
sillyspec progress reset [--stage <阶段>] # 重置进度(破坏性,慎用)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- `/sillyspec:status` 看项目整体进度(change 文件级别);`/sillyspec:state` 看当前工作状态(阶段/步骤级别)——status 看"有什么",state 看"在做什么"。
|
|
23
|
+
|
|
24
|
+
## 用户指令
|
|
25
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sillyspec:status
|
|
3
|
+
description: 用于查看 SillySpec 当前进度和状态。适合用户说"看下状态、当前进度、status"。显示当前阶段、步骤完成度、活跃变更。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 何时使用
|
|
7
|
+
|
|
8
|
+
- 用户说"看下状态、当前进度、status"
|
|
9
|
+
- 查看项目整体进度:活跃变更、各阶段状态、步骤完成度
|
|
10
|
+
|
|
11
|
+
## status vs state 分工
|
|
12
|
+
|
|
13
|
+
- **`/sillyspec:status`**(本 skill)— 查看项目整体进度(change 文件级别、各阶段状态)。走 `sillyspec run status` 阶段。
|
|
14
|
+
- **`/sillyspec:state`** — 查看当前工作状态(阶段/步骤级别、下一步建议)。走 `sillyspec progress show`。
|
|
15
|
+
|
|
16
|
+
两者互补:status 看"有什么",state 看"在做什么"。
|
|
17
|
+
|
|
18
|
+
## 多变更说明
|
|
19
|
+
|
|
20
|
+
status 是辅助阶段。项目有多个活跃变更时,加 `--change <变更名>` 查看指定变更的详情;不指定时汇总显示所有变更。
|
|
21
|
+
|
|
22
|
+
## 步骤生命周期(所有阶段通用)
|
|
23
|
+
|
|
24
|
+
> `sillyspec status` 是 `sillyspec run status` 的顶层别名,两者等价。status 是辅助阶段,只读。
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
sillyspec run status # 输出当前步骤 prompt
|
|
28
|
+
sillyspec run status --done --output "摘要" # 完成阶段
|
|
29
|
+
sillyspec run status --status # 查看阶段状态
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 通用参数(所有阶段适用)
|
|
33
|
+
|
|
34
|
+
| 参数 | 说明 |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `--change <名>` | 指定变更名(多变更时查看指定变更详情) |
|
|
37
|
+
| `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
|
|
38
|
+
|
|
39
|
+
## 配套的只读查询命令(不经 run)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
sillyspec progress show # 当前工作状态(阶段/步骤级)
|
|
43
|
+
sillyspec progress show --change <名> # 指定变更详情
|
|
44
|
+
sillyspec progress check # 状态一致性检查
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 铁律
|
|
48
|
+
|
|
49
|
+
- status 是只读阶段,**不修改任何文件**
|
|
50
|
+
- **必须用 exec 工具(shell)执行 CLI**
|
|
51
|
+
- 完成后立即 `--done`,不跳过
|
|
52
|
+
|
|
53
|
+
## 用户指令
|
|
54
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sillyspec:verify
|
|
3
|
+
description: 用于验证代码实现是否符合 design 和模块文档。适合用户说"验证下、检查下、跑 verify"。对照 design.md + 模块文档检查任务完成度、设计一致性、运行测试。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 何时使用
|
|
7
|
+
|
|
8
|
+
- 用户说"验证下、检查下、跑 verify"
|
|
9
|
+
- 对照 design.md + 模块文档检查任务完成度
|
|
10
|
+
- 设计一致性检查 + 运行测试套件
|
|
11
|
+
- 产出 `verify-result.md`(PASS / PASS WITH NOTES / FAIL)
|
|
12
|
+
|
|
13
|
+
## 多变更说明
|
|
14
|
+
|
|
15
|
+
项目有多个活跃变更(`.sillyspec/changes/` 下有多个目录)时,所有 `sillyspec run` 命令需加 `--change <变更名>` 指定操作目标;只有一个变更时可省略(CLI 自动检测)。
|
|
16
|
+
|
|
17
|
+
## 步骤生命周期(所有阶段通用)
|
|
18
|
+
|
|
19
|
+
> `sillyspec verify` 是 `sillyspec run verify` 的顶层别名,两者等价。
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
sillyspec run verify # 输出当前步骤 prompt
|
|
23
|
+
sillyspec run verify --done --output "摘要" # 完成当前步骤(--input "用户原话" 记录输入)
|
|
24
|
+
sillyspec run verify --status # 查看阶段进度
|
|
25
|
+
sillyspec run verify --skip # 跳过可选步骤
|
|
26
|
+
sillyspec run verify --reset # 重置阶段(从头开始)
|
|
27
|
+
sillyspec run verify --reopen --from-step N # 重新打开已完成阶段修订(N=序号或名称)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 通用参数(所有阶段适用)
|
|
31
|
+
|
|
32
|
+
| 参数 | 说明 |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `--change <名>` | 指定变更名(多活跃变更必填,单变更可省略自动检测) |
|
|
35
|
+
| `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
|
|
36
|
+
| `--non-interactive` | CI/脚本下禁用交互式 prompt |
|
|
37
|
+
| `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
|
|
38
|
+
|
|
39
|
+
## verify 特有:完成门控(重要)
|
|
40
|
+
|
|
41
|
+
verify 是只读阶段(**禁止改代码/改 git 状态**,只检查 + 写报告)。
|
|
42
|
+
|
|
43
|
+
**diff 对账基点**:本变更走 worktree 时,「加载规范并锚定」步骤会注入 worktree 基线锚点(分支名 + `git merge-base` 真实基点)——对照设计做 diff 对账时以它为基点,**不要拿主仓当前 HEAD 当基点**(主仓在 execute 期间可能被并行推进,用错基点会把别人的演进误判为本变更越权改动)。task review 的 base/head 引用分支上的 commit,逐 task 核验用 `git diff <base>..<head>`。完成时有硬校验:
|
|
44
|
+
|
|
45
|
+
- **必须产出 `verify-result.md`**——不存在则阻断完成(不能跳过报告直接 `--done`)
|
|
46
|
+
- **结论为 `FAIL` 则阻断完成**——不能带着 FAIL 标记 verify 完成
|
|
47
|
+
- **`integration-critical` / `deployment-critical` 变更**(design/plan 含 daemon/session/lease/lifecycle 等关键词):结论 PASS WITH NOTES 降级为 FAIL,必须有真实集成证据(Runtime Evidence section)——该证据为 Agent 自报告,CLI 仅校验其字面存在、不独立运行时核验,须真实执行过
|
|
48
|
+
- `verify-required-evidence.json`(execute 写入,schema `{items:[{task,verdict,evidence:[]}]}`)中 cannot_verify 任务未在 verify-result.md 体现 → **advisory warn(不阻断归档)**;evidence 是否真满足(satisfied/missing/partial)由 agent 在 verify-result.md 自报告,CLI 只查任务被提及、不替你语义判定。结论能否 PASS 由你诚实判定(有 missing evidence 不应 PASS)。
|
|
49
|
+
|
|
50
|
+
被阻断时 CLI 打印 ❌ 校验失败,不会提示"验证通过"。修复 `verify-result.md` 后重新 `--done`。
|
|
51
|
+
|
|
52
|
+
## verify-result.md 格式
|
|
53
|
+
|
|
54
|
+
> 🔧 **机械探针 + 报告骨架一条命令**:`sillyspec verify-probes --change <变更名> --init`——探针 1(TODO 标记)/3(测试覆盖)/5(API 契约对账)/6(删除对账)CLI 跑完并预填进 verify-result.md 骨架(七章节、已存在不覆盖);探针 2/4(关键词/决策追踪)与断言抽查、集成盲区标注是语义判断,替换骨架里的 `<!--TODO-->` 完成。结论必须写明 PASS/FAIL——留「待填」会被 gate 判不过。
|
|
55
|
+
>
|
|
56
|
+
> 🔧 **test/lint 实测对账**:`--done` 时 CLI 亲自执行 local.yaml 的 `commands.test`(实测失败阻断 verify 完成)与 `commands.lint`(advisory 对账,实测失败会明示)——自报告与实测不符时以实测为准,勿谎报跑过。
|
|
57
|
+
|
|
58
|
+
```markdown
|
|
59
|
+
# 验证报告
|
|
60
|
+
## 结论
|
|
61
|
+
PASS / PASS WITH NOTES / FAIL ← 必须有结论章节(标题含"结论/Conclusion/Result/结果"即可,不限于确切"## 结论"),FAIL 会阻断 verify 完成
|
|
62
|
+
## 任务完成度
|
|
63
|
+
## 设计一致性
|
|
64
|
+
## 探针结果
|
|
65
|
+
## 测试结果
|
|
66
|
+
## 变更风险等级
|
|
67
|
+
## Runtime Evidence(integration/deployment-critical 必填;自报告,CLI 仅字面校验、不独立核验)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 阶段流转
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
execute → verify → archive
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
verify 通过(PASS)后,运行 `sillyspec run archive --change <变更名>` 归档。FAIL 则修复后重跑 `sillyspec run verify`。
|
|
77
|
+
|
|
78
|
+
## 铁律
|
|
79
|
+
|
|
80
|
+
- **必须用 exec 工具(shell)执行 CLI,不要自己编造流程**
|
|
81
|
+
- verify 阶段**绝对禁止** git checkout/restore/reset、删除/覆盖源码文件——只检查 + 报告
|
|
82
|
+
- 发现问题只报告,不尝试修复(修复回 execute)
|
|
83
|
+
- `verify-result.md` 结论必须基于证据,不写"看起来没问题"
|
|
84
|
+
- 完成后立即 `--done`,不跳过
|
|
85
|
+
|
|
86
|
+
## 用户指令
|
|
87
|
+
$ARGUMENTS
|
package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-workspace/SKILL.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sillyspec:workspace
|
|
3
|
+
description: 工作区管理 — 初始化、管理多项目工作区,查看子项目状态
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 交互规范
|
|
7
|
+
|
|
8
|
+
**当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。** 不要用编号列表让用户手动输入数字。需要自由输入时在选项中加入"Other(自定义输入)"。
|
|
9
|
+
|
|
10
|
+
## CLI 命令(一切读写都走命令,勿手写 yaml / 勿 bash 解析)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
sillyspec workspace add <名> <相对路径> [--role <角色>] [--repo <仓库地址>] # 登记子项目(外科写入 yaml,已有字段保留;路径须存在)
|
|
14
|
+
sillyspec workspace remove <名> # 删除登记(项目本体不动)
|
|
15
|
+
sillyspec workspace status # 三态探测表(未初始化/已初始化/已扫描+文档数)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
无参运行任一子命令可见用法。`status` 的输出即成品状态表,直接转述给用户即可,勿再自行 for-loop 探测。
|
|
19
|
+
|
|
20
|
+
## 核心流程
|
|
21
|
+
|
|
22
|
+
1. **解析指令**:无参数 / `status` → 跑 `workspace status` 转述;`add` / `remove` → 交互收集参数后跑命令;`info <名>` → 读该子项目 `PROJECT.md` / `REQUIREMENTS.md` 摘要转述。
|
|
23
|
+
2. **初始化工作区**(`projects/` 尚无任何 yaml 且用户同意时):用 AskUserQuestion 逐个收集子项目名称/相对路径/角色(可选 repo),每收集一个跑一次 `workspace add`;添加完询问是否继续或完成。共享规范目录 `.sillyspec/shared/` 需要时手动创建。
|
|
24
|
+
3. **收尾**:add/remove 后建议 git 提交(CLI 只写 yaml 不提交);提示下一步对子项目跑 `/sillyspec:init` 或 `/sillyspec:scan`。
|
|
25
|
+
|
|
26
|
+
## 绝对规则
|
|
27
|
+
|
|
28
|
+
- 不修改子项目目录下的任何文件
|
|
29
|
+
- 子项目路径必须是相对于工作区根目录的相对路径,且真实存在(`workspace add` 会校验)
|
|
30
|
+
- yaml 读写一律交给 CLI——字段口径(name/path/status/role/repo)由命令维护,不手工拼
|
|
31
|
+
|
|
32
|
+
## 用户指令
|
|
33
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!-- SillySpec v3.32.10 — 由 sillyspec init 生成,可自由编辑;重跑 init 同版本不更新 -->
|
|
2
|
+
# Agent 指引
|
|
3
|
+
|
|
4
|
+
## 项目说明
|
|
5
|
+
本项目使用 **SillySpec** 管理变更,采用文档驱动开发。SillySpec 是给 Agent 调用的 CLI 流程控制器:你通过 CLI 告诉它"我在哪",它告诉你"下一步做什么";你执行步骤,它校验产出、推进状态,人类只在关键决策点介入审批。要考虑多 agent 同时操作代码,代码随时可能变化。所有变更以稳定、可用、可维护为目标,按生产级标准处理。
|
|
6
|
+
|
|
7
|
+
## 选道(动手前先对号入座)
|
|
8
|
+
| 需求形态 | 走法 |
|
|
9
|
+
|---|---|
|
|
10
|
+
| 需求已含决策(改什么、成功标准说得清——成功标准须来自**用户原话或既有文档转写**;要靠你推断拟出的标准=需求不清晰) | **轻量变更(默认快道)**:`flow start` → 直接干活 → `flow done`(2 次协议调用;操作细节见 `/sillyspec:flow` skill 与 flow start 输出) |
|
|
11
|
+
| 用户显式要求极速(「极速/别验证/只要 spec 和代码」类原话)且需求已含决策 | **轻量变更极速版**:同上行加 `--turbo`(`flow start --change <名> --input "<...>" --turbo`)——**仅用户显式触发,禁止你自行启用**;收口跳过实测与重仪式门(hunk/dirty/ui 探针/评审),spec 结构校验/tasks 全勾/ledger/patch 冻结/distill/归档照常(跳的是验证不是证据);升厚即失效恢复全量仪式;收口输出显式标注「不含测试客观核验」。预段组合极速:头脑风暴预段走轻量跑判定+单轮合并问询+burst 一次 --done,收编时按用户要求带 --turbo |
|
|
12
|
+
| 需求不清晰 / 需要方案探索(含「成功标准要你替用户拟」的形态——自己拟标准再自答是把默认共识伪装成共识) | 头脑风暴预段:`run brainstorm --change <名>` → 完成后 `flow start --change <名>` 收编续跑(design 以头脑风暴版为准)。上个轻量变更被 route-hindsight 标记时全新 start 会被同意门拦下要求显式选道:确认已含决策带 `--no-prestage` 声明留痕,否则先走本行预段 |
|
|
13
|
+
| 大改动:跨模块取舍 / Wave 计划编排 / 多阶段治理 / 设计期人机对抗 | 完整流程五阶段:`run brainstorm → plan → execute → verify → archive`(每阶段一次渲染 + 一次 --done 收口;细节见各阶段 skill 与 CLI 输出) |
|
|
14
|
+
| 代码已先写好(倒推收尾) | 不回头补流程装样子:`flow start --change <YYYY-MM-DD-名> --input` 按下方过门格式写(动机=已做改动的描述,成功标准=已验证达标的行为)→ `flow done` 一步收口 |
|
|
15
|
+
|
|
16
|
+
`--input` 过门格式(轻量道首次调用即过,勿靠报错学;引号内换行合法,下例可照抄):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
sillyspec flow start --change <YYYY-MM-DD-名> --input "<一句中文概括(首行即变更标题);随后动机与背景
|
|
20
|
+
|
|
21
|
+
成功标准:
|
|
22
|
+
- <可验证标准>"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
选道看流程形态需求,不看技术关键词——风险面由收口评审按证据(承诺词/diff 原语/盲维作答)判定。轻量→完整转道是用户决策:须征得用户同意并带 `--upgrade-thick`(同意门留痕,无 flag 拒跑);轻量变更实测失败自动升厚。
|
|
26
|
+
|
|
27
|
+
## 恢复与查看
|
|
28
|
+
- 轻量变更恢复:重跑 `sillyspec flow start --change <名>`(输出恢复简报)或 `flow status --change <名>`;完整流程恢复:`run <stage> --change <名>` 续跑。不直接 commit 半成品
|
|
29
|
+
- 跨会话交接:`sillyspec handoff --change <名>`;变更列表:`sillyspec status`;自检修复:`sillyspec doctor`
|
|
30
|
+
- 知识库:`sillyspec knowledge search "<关键词>"`(命中知识 CLI 会自动注入 prompt,勿自行重复检索)
|
|
31
|
+
|
|
32
|
+
## 核心规则
|
|
33
|
+
1. **禁止绕过本文件规则和 SillySpec 流程**。所有变更走 sillyspec 流程,不裸改裸提交。
|
|
34
|
+
2. **改代码前必须先说明依据**——依据的文档路径(design.md / 模块文档 / file-lifecycle.md(如有))或现有代码依据,无依据不改。
|
|
35
|
+
3. **执行顺序**:文档 → 读代码 → 写测试 → 写实现 → 跑测试 → 验收 → 更新文档。测试按层适用:纯逻辑先写测试,行为契约断言用户可见行为,视觉样式与纯配置/文档不写测试(记「不适用:理由」,验收靠规范与人工)。
|
|
36
|
+
4. **实证核验再收口**:触及 `src`/`test` 的改动,CLI 会在收口时亲自实测(实测失败阻断收口;纯 doc/配置自动跳过)。测试面按变更动态推断(本变更测试 ∪ FR 关联回归 ∪ import 依赖,runner 自项目结构推断——无需配置);lint 走 `.sillyspec/local.yaml` 的 `commands.lint`。以落盘文件与测试结果为准,不信口头"已完成"。
|
|
37
|
+
5. **非测试逻辑本身有误时,禁止改测试来"通过"**——修逻辑,不修测试。
|
|
38
|
+
6. **git hook 拦截提交时禁止跳过**(如 `.husky/pre-push`),修复问题后再提交。
|
|
39
|
+
7. **代码必须兼容 Windows / Linux / macOS**(路径 / 换行 / 并发都要顾)。
|
|
40
|
+
8. **任务记录隔离**:永不重置 / reset / 清零已存在的 change;多个活跃 change 各自 `--change <名>` 隔离不重叠。
|
|
41
|
+
9. **代码可能随时在修改**(多 agent 并行),Edit 前重跑 + 查最新态;破坏性 git op 前先备份。
|
|
42
|
+
10. **会话启动先立身份**:`export SILLYSPEC_SESSION_ID=<唯一标识>`(如 agent 名+任务名)——多会话并行时 change 所有权判定(apply / cleanup / 归档对他会话活跃变更的接管拒绝)依赖此标识;缺省降级为 `anon@<主机名>` 机器级标识(只拦他机,同机并行不设防)。部分 harness 的 shell 状态不持久(env 随命令丢失),此时接管类命令每条显式带 flag:`sillyspec worktree apply <change> --session <唯一标识>`(`--session` 优先级高于 env)。
|
|
43
|
+
11. **禁止目录级 git add / git add -A**(多会话共享仓):目录级暂存会夹带并行会话的进行中文件甚至误删已提交文件。提交一律用显式 pathspec,且 **add 与 commit 用同一份清单**:`git commit -m "..." -- 文件1 文件2`——带 pathspec 的 commit 只提交这些路径,他侧已 staged 的条目既不被带走、也原样留给对方;裸 `git commit` 提交的是整个共享暂存区,他会话 add 过的文件会被一并扫入。核对暂存面固定用 `git diff --cached --name-only` 全量读取,禁用过滤式核对(过滤正是致盲原因);核对与提交分开两条命令执行,不得链在同一命令里(链行没有拦截点)。安装了 commit-guard hook 的仓会对超声明面的 staged 文件警告(只警告不阻断)。
|
|
44
|
+
12. **不奉承用户**,禁止"你说得对"类话术,直接给结论、依据、方案。
|
|
45
|
+
13. **边干边勾(任务进度唯一源=变更的 tasks.md)**:完成一个任务单元(实现到位 + 相关测试跑绿)当场勾对应格——`sillyspec task tick --change <名> --task task-NN`(即时回显进度与下一任务)或直接 Edit 翻格 `- [ ]`→`- [x]`,勿攒到收口一把勾。harness 自带的 TodoWrite 类工具是会话内便利面,不替代 tasks.md——平台进度投影、收口哨兵、断点恢复只读 tasks.md。
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
2026-10-15T09:00:00Z START s1 login
|
|
2
|
+
2026-10-15T09:00:03Z START s2 upload
|
|
3
|
+
2026-10-15T09:00:04Z END s1 login
|
|
4
|
+
2026-10-15T09:00:05Z START s3 download
|
|
5
|
+
2026-10-15T09:00:20Z END s2 upload
|
|
6
|
+
2026-10-15T09:00:15Z END s3 download
|
|
7
|
+
|
|
8
|
+
2026-10-15T09:05:00Z START s4 login
|
|
9
|
+
2026-10-15T09:05:09Z END s4 login
|
|
10
|
+
2026-10-15T09:06:00Z START s5 upload
|
|
11
|
+
2026-10-15T09:07:00Z END s6 download
|
|
12
|
+
2026-10-15T09:08:00Z START s7 login
|
|
13
|
+
2026-10-15T09:08:03Z END s7 login
|
|
14
|
+
2026-10-15T09:08:30Z START s7 login
|
|
15
|
+
2026-10-15T09:08:36Z END s7 login
|
|
16
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// 参考解(dry-run 假 agent 使用,也供人工对照)。
|
|
2
|
+
// 思路:空白先归一为 \u0000 哨兵;字符过滤同时保留 [a-z0-9]、哨兵和字面分隔符;
|
|
3
|
+
// 再把「哨兵/分隔符的连续串」折叠为单个分隔符并去首尾。
|
|
4
|
+
// 注意不能只折叠哨兵——输入里本来就有的连续分隔符(如 'a--b')也要折叠。
|
|
5
|
+
const SENTINEL = '\u0000';
|
|
6
|
+
|
|
7
|
+
function escapeRe(ch) {
|
|
8
|
+
return ch.replace(/[.*+?^${}()|[\]\\-]/g, '\\$&');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function slugify(value, options = {}) {
|
|
12
|
+
if (value === null || value === undefined) return '';
|
|
13
|
+
const sep = options.hyphen ?? '-';
|
|
14
|
+
if (typeof sep !== 'string' || sep.length !== 1) {
|
|
15
|
+
throw new TypeError('hyphen must be a single character');
|
|
16
|
+
}
|
|
17
|
+
const esc = escapeRe(sep);
|
|
18
|
+
return String(value)
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.replace(/\s+/g, SENTINEL)
|
|
21
|
+
.replace(new RegExp(`[^a-z0-9\\u0000${esc}]`, 'g'), '')
|
|
22
|
+
.replace(new RegExp(`[\\u0000${esc}]+`, 'g'), sep)
|
|
23
|
+
.replace(new RegExp(`^${esc}+|${esc}+$`, 'g'), '');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default slugify;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// 参考解(dry-run 假 agent 使用,也供人工对照)。
|
|
2
|
+
// 思路:空白先归一为 \u0000 哨兵;字符过滤同时保留 [a-z0-9]、哨兵和字面分隔符;
|
|
3
|
+
// 再把「哨兵/分隔符的连续串」折叠为单个分隔符并去首尾。
|
|
4
|
+
// 注意不能只折叠哨兵——输入里本来就有的连续分隔符(如 'a--b')也要折叠。
|
|
5
|
+
const SENTINEL = '\u0000';
|
|
6
|
+
|
|
7
|
+
function escapeRe(ch) {
|
|
8
|
+
return ch.replace(/[.*+?^${}()|[\]\\-]/g, '\\$&');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function slugify(value, options = {}) {
|
|
12
|
+
if (value === null || value === undefined) return '';
|
|
13
|
+
const sep = options.hyphen ?? '-';
|
|
14
|
+
if (typeof sep !== 'string' || sep.length !== 1) {
|
|
15
|
+
throw new TypeError('hyphen must be a single character');
|
|
16
|
+
}
|
|
17
|
+
const esc = escapeRe(sep);
|
|
18
|
+
return String(value)
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.replace(/\s+/g, SENTINEL)
|
|
21
|
+
.replace(new RegExp(`[^a-z0-9\\u0000${esc}]`, 'g'), '')
|
|
22
|
+
.replace(new RegExp(`[\\u0000${esc}]+`, 'g'), sep)
|
|
23
|
+
.replace(new RegExp(`^${esc}+|${esc}+$`, 'g'), '');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default slugify;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// 参考解(dry-run 假 agent 使用,也供人工对照)。
|
|
2
|
+
const DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})(?:[T ].*)?$/;
|
|
3
|
+
const DAY_MS = 86_400_000;
|
|
4
|
+
|
|
5
|
+
function parseUtc(value) {
|
|
6
|
+
if (typeof value !== 'string') throw new TypeError('invalid date');
|
|
7
|
+
const m = DATE_PREFIX.exec(value.trim());
|
|
8
|
+
if (!m) throw new TypeError('invalid date');
|
|
9
|
+
const y = Number(m[1]);
|
|
10
|
+
const mo = Number(m[2]);
|
|
11
|
+
const d = Number(m[3]);
|
|
12
|
+
const ts = Date.UTC(y, mo - 1, d);
|
|
13
|
+
const dt = new Date(ts);
|
|
14
|
+
// 往返校验拦住 '2024-02-30' 这类被 Date 自动进位的假日期
|
|
15
|
+
if (dt.getUTCFullYear() !== y || dt.getUTCMonth() !== mo - 1 || dt.getUTCDate() !== d) {
|
|
16
|
+
throw new TypeError('invalid date');
|
|
17
|
+
}
|
|
18
|
+
return ts;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function eachDay(start, end) {
|
|
22
|
+
const s = parseUtc(start);
|
|
23
|
+
const e = parseUtc(end);
|
|
24
|
+
if (e < s) throw new RangeError('end must not be before start');
|
|
25
|
+
const days = [];
|
|
26
|
+
for (let t = s; t <= e; t += DAY_MS) {
|
|
27
|
+
days.push(new Date(t).toISOString().slice(0, 10));
|
|
28
|
+
}
|
|
29
|
+
return days;
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// dry-run 失败路径占位产物
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 参考解(dry-run 假 agent 使用,也供人工对照)。
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const out = { input: 'data/notes.txt', top: 5, minLen: 1 };
|
|
7
|
+
for (let i = 0; i < argv.length; i++) {
|
|
8
|
+
const a = argv[i];
|
|
9
|
+
if (a === '--input') out.input = argv[++i];
|
|
10
|
+
else if (a === '--top') out.top = Number(argv[++i]);
|
|
11
|
+
else if (a === '--min-len') out.minLen = Number(argv[++i]);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { input, top, minLen } = parseArgs(process.argv.slice(2));
|
|
17
|
+
let text;
|
|
18
|
+
try {
|
|
19
|
+
text = readFileSync(input, 'utf8');
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error(`无法读取输入文件: ${input}(${err.code ?? err.message})`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const counts = new Map();
|
|
26
|
+
for (const word of text.toLowerCase().split(/\s+/)) {
|
|
27
|
+
if (!word || word.length < minLen) continue;
|
|
28
|
+
counts.set(word, (counts.get(word) ?? 0) + 1);
|
|
29
|
+
}
|
|
30
|
+
const totalWords = [...counts.values()].reduce((a, b) => a + b, 0);
|
|
31
|
+
const topWords = [...counts.entries()]
|
|
32
|
+
.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1))
|
|
33
|
+
.slice(0, top)
|
|
34
|
+
.map(([word, count]) => ({ word, count }));
|
|
35
|
+
|
|
36
|
+
process.stdout.write(`${JSON.stringify({ totalWords, top: topWords })}\n`);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 参考解(dry-run 假 agent 使用,也供人工对照)。
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const out = { input: 'data/notes.txt', top: 5, minLen: 1 };
|
|
7
|
+
for (let i = 0; i < argv.length; i++) {
|
|
8
|
+
const a = argv[i];
|
|
9
|
+
if (a === '--input') out.input = argv[++i];
|
|
10
|
+
else if (a === '--top') out.top = Number(argv[++i]);
|
|
11
|
+
else if (a === '--min-len') out.minLen = Number(argv[++i]);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { input, top, minLen } = parseArgs(process.argv.slice(2));
|
|
17
|
+
let text;
|
|
18
|
+
try {
|
|
19
|
+
text = readFileSync(input, 'utf8');
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error(`无法读取输入文件: ${input}(${err.code ?? err.message})`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const counts = new Map();
|
|
26
|
+
for (const word of text.toLowerCase().split(/\s+/)) {
|
|
27
|
+
if (!word || word.length < minLen) continue;
|
|
28
|
+
counts.set(word, (counts.get(word) ?? 0) + 1);
|
|
29
|
+
}
|
|
30
|
+
const totalWords = [...counts.values()].reduce((a, b) => a + b, 0);
|
|
31
|
+
const topWords = [...counts.entries()]
|
|
32
|
+
.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1))
|
|
33
|
+
.slice(0, top)
|
|
34
|
+
.map(([word, count]) => ({ word, count }));
|
|
35
|
+
|
|
36
|
+
process.stdout.write(`${JSON.stringify({ totalWords, top: topWords })}\n`);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 参考解(eval dry-run 参考与判别力测试用;不进 agent 工作副本、不进任务指纹)。
|
|
3
|
+
// 覆盖全部隐含面:乱序交错行按 (sessionId, phase) 配对;空行/纯空白行跳过;行字段 trim
|
|
4
|
+
// (兼容 CRLF);未闭合 START 与孤儿 END 零贡献;同 (sessionId, phase) 多轮累加。
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
const raw = readFileSync('data/events.log', 'utf8');
|
|
8
|
+
const open = new Map(); // "session|phase" -> start ms
|
|
9
|
+
const agg = new Map(); // phase -> { rounds, totalSeconds }
|
|
10
|
+
|
|
11
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
12
|
+
const t = line.trim();
|
|
13
|
+
if (!t) continue; // 空行 / 纯空白行
|
|
14
|
+
const parts = t.split(/\s+/);
|
|
15
|
+
if (parts.length < 4) continue;
|
|
16
|
+
const [ts, kind, session, phase] = parts;
|
|
17
|
+
const ms = Date.parse(ts);
|
|
18
|
+
if (!Number.isFinite(ms)) continue;
|
|
19
|
+
const key = `${session}|${phase}`;
|
|
20
|
+
if (kind === 'START') {
|
|
21
|
+
if (!open.has(key)) open.set(key, ms); // 重复 START 以首者计,防覆盖丢轮
|
|
22
|
+
} else if (kind === 'END') {
|
|
23
|
+
const start = open.get(key);
|
|
24
|
+
if (start === undefined) continue; // 孤儿 END 零贡献
|
|
25
|
+
open.delete(key);
|
|
26
|
+
const a = agg.get(phase) ?? { rounds: 0, totalSeconds: 0 };
|
|
27
|
+
a.rounds += 1;
|
|
28
|
+
a.totalSeconds += Math.round((ms - start) / 1000);
|
|
29
|
+
agg.set(phase, a);
|
|
30
|
+
}
|
|
31
|
+
// 未闭合 START:循环结束自然零贡献
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const phases = [...agg.entries()]
|
|
35
|
+
.map(([phase, a]) => ({ phase, rounds: a.rounds, totalSeconds: a.totalSeconds }))
|
|
36
|
+
.sort((x, y) => (x.phase < y.phase ? -1 : x.phase > y.phase ? 1 : 0));
|
|
37
|
+
process.stdout.write(JSON.stringify({ phases }) + '\n');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
2026-10-15T09:00:00Z START s1 login
|
|
2
|
+
2026-10-15T09:00:03Z START s2 upload
|
|
3
|
+
2026-10-15T09:00:04Z END s1 login
|
|
4
|
+
2026-10-15T09:00:05Z START s3 download
|
|
5
|
+
2026-10-15T09:00:20Z END s2 upload
|
|
6
|
+
2026-10-15T09:00:15Z END s3 download
|
|
7
|
+
|
|
8
|
+
2026-10-15T09:05:00Z START s4 login
|
|
9
|
+
2026-10-15T09:05:09Z END s4 login
|
|
10
|
+
2026-10-15T09:06:00Z START s5 upload
|
|
11
|
+
2026-10-15T09:07:00Z END s6 download
|
|
12
|
+
2026-10-15T09:08:00Z START s7 login
|
|
13
|
+
2026-10-15T09:08:03Z END s7 login
|
|
14
|
+
2026-10-15T09:08:30Z START s7 login
|
|
15
|
+
2026-10-15T09:08:36Z END s7 login
|
|
16
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// dry-run 失败路径占位产物
|