cc-devflow 4.1.6 → 4.2.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/commands/core/architecture.md +30 -0
- package/.claude/commands/core/guidelines.md +25 -0
- package/.claude/commands/core/roadmap.md +29 -0
- package/.claude/commands/core/style.md +18 -0
- package/.claude/skills/cc-devflow-orchestrator/SKILL.md +25 -0
- package/.claude/skills/workflow/flow-dev/SKILL.md +36 -0
- package/.claude/skills/workflow/flow-init/SKILL.md +29 -0
- package/.claude/skills/workflow/flow-spec/SKILL.md +32 -0
- package/CHANGELOG.md +41 -0
- package/README.md +5 -0
- package/README.zh-CN.md +5 -0
- package/bin/cc-devflow-cli.js +154 -0
- package/package.json +1 -1
|
@@ -29,6 +29,36 @@ architecture-designer (research, no dialogue)
|
|
|
29
29
|
ARCHITECTURE.md (4 diagrams)
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## Harness Engineering 增强(Long-running Session Protocol)
|
|
33
|
+
|
|
34
|
+
为防止跨上下文窗口后出现“图表半生成、ADR 丢失、误判已完成”,执行分为 Initializer 与 Worker 两类会话。
|
|
35
|
+
|
|
36
|
+
### Phase 0: Initializer Session(首次或 --force)
|
|
37
|
+
|
|
38
|
+
先建立/刷新架构任务工件(不要一次性生成整份文档):
|
|
39
|
+
- `devflow/.core-harness/architecture/checklist.json`
|
|
40
|
+
- 列出 4 张图、ADR、NFR、演进路径等验收项,默认 `passes=false`
|
|
41
|
+
- `devflow/.core-harness/architecture/progress.md`
|
|
42
|
+
- 记录本次修改、风险、下一步
|
|
43
|
+
- `devflow/.core-harness/architecture/init.md`
|
|
44
|
+
- 固定启动流程(读取 ROADMAP、读取最近进展、执行 Mermaid 冒烟验证)
|
|
45
|
+
|
|
46
|
+
### Worker Session(增量交付)
|
|
47
|
+
|
|
48
|
+
每个窗口只处理一个子目标,例如:
|
|
49
|
+
- 只完成一张 Mermaid 图并自检语法
|
|
50
|
+
- 只补齐 ADR 与对应图表映射
|
|
51
|
+
- 只修复依赖图节点/边一致性问题
|
|
52
|
+
|
|
53
|
+
会话收尾要求:
|
|
54
|
+
1. 运行当前文档校验(占位符、图表数量、语法)
|
|
55
|
+
2. 更新 checklist 的 `passes` 字段
|
|
56
|
+
3. 写入 progress/handoff,明确下一窗口优先任务
|
|
57
|
+
|
|
58
|
+
### Done Gate
|
|
59
|
+
|
|
60
|
+
`checklist.json` 全绿 + Phase 4 校验通过,才可输出成功报告。
|
|
61
|
+
|
|
32
62
|
## 执行流程
|
|
33
63
|
|
|
34
64
|
### Phase 1: Prerequisites Check
|
|
@@ -35,6 +35,31 @@ description: 'Generate project-specific development guidelines skills (frontend/
|
|
|
35
35
|
/core-guidelines --dry-run
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## Harness Engineering 增强(Eval-Driven + Incremental)
|
|
39
|
+
|
|
40
|
+
`/core-guidelines` 容易出现两类失败:一次生成过大导致中断、未验证触发规则就标记完成。为此引入双阶段协议。
|
|
41
|
+
|
|
42
|
+
### Step 0: Initializer Session
|
|
43
|
+
|
|
44
|
+
创建会话工件:
|
|
45
|
+
- `devflow/.core-harness/guidelines/skill-checklist.json`
|
|
46
|
+
- Frontend/Backend 分别列出验收项:触发规则、关键资源文件、示例质量、约束合规,默认 `passes=false`
|
|
47
|
+
- `devflow/.core-harness/guidelines/progress.md`
|
|
48
|
+
- `devflow/.core-harness/guidelines/session-handoff.md`
|
|
49
|
+
|
|
50
|
+
### Worker Session
|
|
51
|
+
|
|
52
|
+
- 一次只处理一个 skill(frontend 或 backend),禁止同窗口双线并发重写
|
|
53
|
+
- 每次只完成一个最小闭环:`生成/更新 → 触发验证 → 示例验证 → 勾选 passes`
|
|
54
|
+
- 结束前必须写 handoff,明确下一窗口要做的单一目标
|
|
55
|
+
|
|
56
|
+
### Eval Gate(禁止“看起来差不多”)
|
|
57
|
+
|
|
58
|
+
只有当以下检查全部通过才标记对应 skill 完成:
|
|
59
|
+
1. `skill-rules.json` 触发范围正确,且前后端互不污染
|
|
60
|
+
2. SKILL + resources 无占位符/虚构路径
|
|
61
|
+
3. 至少 1 个真实文件路径触发测试通过(frontend 与 backend 各自独立)
|
|
62
|
+
|
|
38
63
|
---
|
|
39
64
|
|
|
40
65
|
## Process
|
|
@@ -102,6 +102,35 @@ guides:
|
|
|
102
102
|
- **roadmap-planner (Agent)**: 根据上下文生成文档,无对话
|
|
103
103
|
- **architecture-designer (Agent)**: 生成架构图,无对话
|
|
104
104
|
|
|
105
|
+
## Harness Engineering 增强(OpenAI + Anthropic 实践融合)
|
|
106
|
+
|
|
107
|
+
为避免长会话中的“半成品漂移 / 提前宣布完成 / 上下文断片”,`/core-roadmap` 强制采用双阶段执行。
|
|
108
|
+
|
|
109
|
+
### Stage -1: Initializer Session(只做地基,不做全量生成)
|
|
110
|
+
|
|
111
|
+
在首次运行或 `--regenerate` 时先建立可恢复工件:
|
|
112
|
+
- `devflow/.core-harness/roadmap/feature-checklist.json`
|
|
113
|
+
- 结构化列出路线图必须产物(愿景、里程碑、依赖图、容量评估、季度分配等),默认 `passes=false`
|
|
114
|
+
- `devflow/.core-harness/roadmap/progress.md`
|
|
115
|
+
- 记录每次会话完成内容、未决问题、下一步
|
|
116
|
+
- `devflow/.core-harness/roadmap/session-handoff.md`
|
|
117
|
+
- 给下一窗口的启动指令(从哪个 Stage 继续、先做什么验证)
|
|
118
|
+
|
|
119
|
+
### Worker Session(增量推进,每次只完成一个最小目标)
|
|
120
|
+
|
|
121
|
+
每个窗口必须遵守:
|
|
122
|
+
1. 启动先读状态:`pwd` → `progress.md` → `feature-checklist.json` → 最近 git log(若仓库可用)
|
|
123
|
+
2. 先做冒烟检查:确认现有 `ROADMAP.md/BACKLOG.md/ARCHITECTURE.md` 不是损坏状态
|
|
124
|
+
3. 只推进一个最小单元:
|
|
125
|
+
- 一个 Stage 的信息收集,或
|
|
126
|
+
- 一个 RM 依赖簇的整理,或
|
|
127
|
+
- 一次时间线冲突修复
|
|
128
|
+
4. 结束前必须更新工件:勾选 `passes`、写入 progress/handoff、明确下一步
|
|
129
|
+
|
|
130
|
+
### 完成判定(禁止“口头完成”)
|
|
131
|
+
|
|
132
|
+
只有 `feature-checklist.json` 全部 `passes=true`,且 Stage 8 输出文件全部存在并通过校验,才允许宣告完成。
|
|
133
|
+
|
|
105
134
|
---
|
|
106
135
|
|
|
107
136
|
## 执行流程骨架
|
|
@@ -21,6 +21,24 @@ $ARGUMENTS = "[--update]"
|
|
|
21
21
|
/core-style --update # 更新已有风格指南
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
## Harness Engineering 增强(多窗口稳定执行)
|
|
25
|
+
|
|
26
|
+
为避免风格文档在多轮会话中变成“补丁堆”,执行采用 initializer + incremental worker。
|
|
27
|
+
|
|
28
|
+
### 0. Initializer Session
|
|
29
|
+
- 建立 `devflow/.core-harness/style/` 工件:
|
|
30
|
+
- `checklist.json`: token 体系、组件约束、可访问性、响应式、动效、禁用项等验收项(默认 `passes=false`)
|
|
31
|
+
- `progress.md`: 本轮修改与未决项
|
|
32
|
+
- `session-handoff.md`: 下一轮优先任务与验证命令
|
|
33
|
+
|
|
34
|
+
### 1. Worker Session
|
|
35
|
+
- 每轮只处理一个最小块(例如颜色 token、排版 token、某一类组件约束)
|
|
36
|
+
- 修改后立即做局部验证,不通过则回滚该块后重试
|
|
37
|
+
- 收尾必须同步 checklist/progress/handoff,保持可恢复
|
|
38
|
+
|
|
39
|
+
### 2. Done Gate
|
|
40
|
+
- 只有 checklist 全部 `passes=true` 且第 4 步校验通过,才允许输出“STYLE 生成完成”
|
|
41
|
+
|
|
24
42
|
## 执行步骤
|
|
25
43
|
|
|
26
44
|
### 1. 入口检查
|
|
@@ -19,6 +19,29 @@ Guide users to the correct command/skill without duplicating detailed implementa
|
|
|
19
19
|
/core:style → STYLE.md
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Project-Level Harness Protocol (Long-running)
|
|
23
|
+
|
|
24
|
+
For `/core:*` commands, enforce a two-session model before declaring completion:
|
|
25
|
+
|
|
26
|
+
1. Initializer session
|
|
27
|
+
- establish/update `devflow/.core-harness/<command>/checklist.json`, `progress.md`, `session-handoff.md`
|
|
28
|
+
- convert high-level goal into structured acceptance checks (default all failing)
|
|
29
|
+
2. Worker session(s)
|
|
30
|
+
- resume from `session-handoff.md` + `progress.md`
|
|
31
|
+
- execute one smallest deliverable per session
|
|
32
|
+
- update checklist status only after command-specific validation
|
|
33
|
+
3. Completion gate
|
|
34
|
+
- completion is allowed only when checklist is fully passing and command validation gates pass
|
|
35
|
+
- never declare success from “looks complete”; require artifact evidence
|
|
36
|
+
|
|
37
|
+
### Core Route Defaults
|
|
38
|
+
|
|
39
|
+
- no `devflow/ROADMAP.md` → route to `/core:roadmap` (initializer first)
|
|
40
|
+
- roadmap exists but architecture missing/stale → route to `/core:architecture`
|
|
41
|
+
- architecture exists but guidelines missing/stale → route to `/core:guidelines`
|
|
42
|
+
- style missing/stale → route to `/core:style`
|
|
43
|
+
- interrupted core command → rerun same command from handoff (`/core:roadmap --resume` if supported; otherwise run command again and continue from `session-handoff.md`)
|
|
44
|
+
|
|
22
45
|
### Requirement-Level Canonical Mainline (v6)
|
|
23
46
|
|
|
24
47
|
```text
|
|
@@ -140,5 +163,7 @@ This skill only does routing:
|
|
|
140
163
|
- Which command to run next
|
|
141
164
|
- Which gate blocks progress
|
|
142
165
|
- Which migration path applies for deprecated commands
|
|
166
|
+
- Prefer incremental convergence over one-shot generation
|
|
167
|
+
- Require artifact-backed completion for long-running sessions
|
|
143
168
|
|
|
144
169
|
Detailed quality standards stay in command files and workflow skills.
|
|
@@ -11,6 +11,42 @@ description: 'Execute task-manifest with dependency-aware parallel dispatch and
|
|
|
11
11
|
|
|
12
12
|
执行 `task-manifest.json` 中的任务,默认并行调度并写入 checkpoint/events 供恢复。
|
|
13
13
|
|
|
14
|
+
## Long-Running Harness Protocol(Initializer/Worker/Done Gate)
|
|
15
|
+
|
|
16
|
+
`/flow:dev` 是最长链路阶段,必须严格执行“先读状态、再增量、后留证据”。
|
|
17
|
+
|
|
18
|
+
### Session Start(禁止盲跑)
|
|
19
|
+
|
|
20
|
+
每个窗口开始时先同步以下信息:
|
|
21
|
+
- `devflow/requirements/${REQ_ID}/session-checklist.json`(若存在)
|
|
22
|
+
- `devflow/requirements/${REQ_ID}/session-progress.md`(若存在)
|
|
23
|
+
- `devflow/requirements/${REQ_ID}/session-handoff.md`(若存在)
|
|
24
|
+
- `devflow/requirements/${REQ_ID}/task-manifest.json`
|
|
25
|
+
- `.harness/runtime/${REQ_ID}/**/checkpoint.json` 与 `events.jsonl`(若存在)
|
|
26
|
+
|
|
27
|
+
然后执行一次基础健康检查:
|
|
28
|
+
1. 不存在损坏的 `running` 残留状态
|
|
29
|
+
2. 上轮失败任务与失败原因可定位
|
|
30
|
+
|
|
31
|
+
### Worker Session(最小可执行前沿)
|
|
32
|
+
|
|
33
|
+
单窗口仅推进一个最小前沿:
|
|
34
|
+
- 仅处理当前 `dependsOn` 已满足的一组任务
|
|
35
|
+
- 每完成一组任务即写入 checkpoint/events
|
|
36
|
+
- 遇到失败立即收敛原因并记录,不并行扩散新的失败面
|
|
37
|
+
|
|
38
|
+
会话收尾必须:
|
|
39
|
+
1. 更新 `task-manifest.json` 状态
|
|
40
|
+
2. 更新 `session-progress.md` 与 `session-handoff.md`
|
|
41
|
+
3. 推荐提交一个可回滚的最小 git commit(若仓库策略允许)
|
|
42
|
+
|
|
43
|
+
### Done Gate
|
|
44
|
+
|
|
45
|
+
仅当以下条件满足才标记 `flow:dev` 完成:
|
|
46
|
+
- 全部任务为 `passed` 或 `skipped`
|
|
47
|
+
- 无 `running`/`failed` 残留
|
|
48
|
+
- `session-checklist.json` 中 `flow:dev.passes == true`
|
|
49
|
+
|
|
14
50
|
## Input Format
|
|
15
51
|
|
|
16
52
|
```bash
|
|
@@ -11,6 +11,35 @@ description: 'Initialize a requirement with harness state and context package. U
|
|
|
11
11
|
|
|
12
12
|
初始化需求目录的 harness 运行状态,并生成可执行上下文包。
|
|
13
13
|
|
|
14
|
+
## Long-Running Harness Protocol(Initializer/Worker/Done Gate)
|
|
15
|
+
|
|
16
|
+
为与 `/core:*` 保持一致,`/flow:init` 也采用会话分层协议。
|
|
17
|
+
|
|
18
|
+
### Initializer Session(为后续窗口建立可恢复上下文)
|
|
19
|
+
|
|
20
|
+
在执行 `harness:init + harness:pack` 后,确保以下工件存在:
|
|
21
|
+
- `devflow/requirements/${REQ_ID}/session-checklist.json`
|
|
22
|
+
- 至少包含 `flow:init/flow:spec/flow:dev/flow:verify/flow:release` 五个阶段,默认 `passes=false`
|
|
23
|
+
- `devflow/requirements/${REQ_ID}/session-progress.md`
|
|
24
|
+
- 记录本窗口完成内容、风险和阻塞
|
|
25
|
+
- `devflow/requirements/${REQ_ID}/session-handoff.md`
|
|
26
|
+
- 明确下一窗口唯一优先动作(通常是 `/flow:spec "${REQ_ID}"`)
|
|
27
|
+
|
|
28
|
+
### Worker Session(只做 init 的原子闭环)
|
|
29
|
+
|
|
30
|
+
每次仅执行一个最小闭环:
|
|
31
|
+
1. 读取 `session-progress.md` 与 `session-handoff.md`
|
|
32
|
+
2. 运行 `harness:init`
|
|
33
|
+
3. 运行 `harness:pack`
|
|
34
|
+
4. 验证产物并更新 session 工件
|
|
35
|
+
|
|
36
|
+
### Done Gate(禁止口头完成)
|
|
37
|
+
|
|
38
|
+
仅当以下条件同时满足才允许标记 `flow:init` 为完成:
|
|
39
|
+
- `harness-state.json.status == "initialized"`
|
|
40
|
+
- `context-package.md` 存在且包含 Next Commands
|
|
41
|
+
- `session-checklist.json` 中 `flow:init.passes == true`
|
|
42
|
+
|
|
14
43
|
## Input Format
|
|
15
44
|
|
|
16
45
|
```bash
|
|
@@ -11,6 +11,38 @@ description: 'Generate and refresh task-manifest for a requirement. Use when con
|
|
|
11
11
|
|
|
12
12
|
将需求上下文转换为 `task-manifest.json`,作为后续执行唯一任务源。
|
|
13
13
|
|
|
14
|
+
## Long-Running Harness Protocol(Initializer/Worker/Done Gate)
|
|
15
|
+
|
|
16
|
+
`/flow:spec` 必须以“可恢复、可验证”的增量方式生成计划,而不是一次性黑箱产出。
|
|
17
|
+
|
|
18
|
+
### Session Start(先同步上下文,再规划)
|
|
19
|
+
|
|
20
|
+
每个窗口启动先读取:
|
|
21
|
+
- `devflow/requirements/${REQ_ID}/session-checklist.json`(若存在)
|
|
22
|
+
- `devflow/requirements/${REQ_ID}/session-progress.md`(若存在)
|
|
23
|
+
- `devflow/requirements/${REQ_ID}/session-handoff.md`(若存在)
|
|
24
|
+
- `devflow/requirements/${REQ_ID}/context-package.md`
|
|
25
|
+
- 旧版 `task-manifest.json`(若存在)
|
|
26
|
+
|
|
27
|
+
### Worker Session(一次只收敛一个规划目标)
|
|
28
|
+
|
|
29
|
+
每轮只完成一个最小目标,例如:
|
|
30
|
+
- 修正一个依赖子图
|
|
31
|
+
- 补齐一组缺失的 `run` 指令
|
|
32
|
+
- 处理一次 `--overwrite` 下的清理重建
|
|
33
|
+
|
|
34
|
+
完成后必须:
|
|
35
|
+
1. 运行 `harness:plan` 生成/刷新 manifest
|
|
36
|
+
2. 校验 schema 与依赖无环
|
|
37
|
+
3. 更新 progress/handoff,写明下一轮唯一目标
|
|
38
|
+
|
|
39
|
+
### Done Gate
|
|
40
|
+
|
|
41
|
+
仅当以下条件满足才标记 `flow:spec` 完成:
|
|
42
|
+
- `task-manifest.json` 存在且 `tasks` 非空
|
|
43
|
+
- `id/dependsOn/run` 字段完整且无自循环
|
|
44
|
+
- `session-checklist.json` 中 `flow:spec.passes == true`
|
|
45
|
+
|
|
14
46
|
## Input Format
|
|
15
47
|
|
|
16
48
|
```bash
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [4.2.0] - 2026-02-19
|
|
11
|
+
|
|
12
|
+
### 🧠 Long-Running Harness Protocol Alignment
|
|
13
|
+
|
|
14
|
+
v4.2.0 unifies long-running execution protocol across project-level `/core:*` commands and requirement-level `/flow:*` skills, reducing context-loss regressions and premature completion.
|
|
15
|
+
|
|
16
|
+
#### Added
|
|
17
|
+
|
|
18
|
+
- **Project-level harness protocol**
|
|
19
|
+
- Added `Initializer / Worker / Done Gate` guidance to:
|
|
20
|
+
- `.claude/commands/core/roadmap.md`
|
|
21
|
+
- `.claude/commands/core/architecture.md`
|
|
22
|
+
- `.claude/commands/core/guidelines.md`
|
|
23
|
+
- `.claude/commands/core/style.md`
|
|
24
|
+
- Standardized resumable artifacts under `devflow/.core-harness/*` (`checklist`, `progress`, `handoff`)
|
|
25
|
+
|
|
26
|
+
- **Requirement-level protocol parity**
|
|
27
|
+
- Added the same long-running protocol to:
|
|
28
|
+
- `.claude/skills/workflow/flow-init/SKILL.md`
|
|
29
|
+
- `.claude/skills/workflow/flow-spec/SKILL.md`
|
|
30
|
+
- `.claude/skills/workflow/flow-dev/SKILL.md`
|
|
31
|
+
- Standardized requirement session artifacts:
|
|
32
|
+
- `session-checklist.json`
|
|
33
|
+
- `session-progress.md`
|
|
34
|
+
- `session-handoff.md`
|
|
35
|
+
|
|
36
|
+
- **Routing-level enforcement**
|
|
37
|
+
- Extended `.claude/skills/cc-devflow-orchestrator/SKILL.md` with project-level harness routing defaults and artifact-backed completion rules
|
|
38
|
+
|
|
39
|
+
#### Benefits
|
|
40
|
+
|
|
41
|
+
- ✅ Consistent long-running behavior between `/core:*` and `/flow:*`
|
|
42
|
+
- ✅ Clear resumability across context windows
|
|
43
|
+
- ✅ Stronger done criteria via artifact-backed gates (instead of subjective completion)
|
|
44
|
+
|
|
10
45
|
## [4.1.6] - 2026-02-18
|
|
11
46
|
|
|
12
47
|
### 🔧 Multi-Platform Adapt Pipeline Stabilization
|
|
@@ -19,6 +54,12 @@ v4.1.6 fixes multi-platform `adapt` compilation drift and parsing failures, with
|
|
|
19
54
|
- `.claude/commands/**/CLAUDE.md` is now excluded from command parsing
|
|
20
55
|
- Prevents false `Missing YAML frontmatter` failures during `npm run adapt`
|
|
21
56
|
|
|
57
|
+
- **Harness runtime chain bootstrap**
|
|
58
|
+
- `cc-devflow init` and `cc-devflow adapt` now auto-add missing `harness:*` npm scripts into target `package.json`
|
|
59
|
+
- Added `cc-devflow harness <subcommand>` passthrough so injected scripts have a stable runtime entry
|
|
60
|
+
- Injected scripts are now machine-portable (`cc-devflow harness ...`) with no absolute-path coupling
|
|
61
|
+
- Auto-repairs legacy `node bin/harness.js <cmd>`, old `npx` wrappers, and prior absolute-path script values
|
|
62
|
+
|
|
22
63
|
- **Manifest consistency across platforms**
|
|
23
64
|
- Separated source hash (`sourceHash`) and emitted target hash (`hash`)
|
|
24
65
|
- Drift detection now compares emitted artifact hashes correctly
|
package/README.md
CHANGED
|
@@ -184,8 +184,13 @@ cc-devflow init --dir /path/to/project
|
|
|
184
184
|
# Compile for specific platform
|
|
185
185
|
cc-devflow adapt --platform codex
|
|
186
186
|
cc-devflow adapt --cwd /path/to/project --platform cursor
|
|
187
|
+
|
|
188
|
+
# Run harness runtime directly (for npm script delegation)
|
|
189
|
+
cc-devflow harness release --change-id REQ-123
|
|
187
190
|
```
|
|
188
191
|
|
|
192
|
+
`cc-devflow init` and `cc-devflow adapt` now auto-bootstrap missing `harness:*` npm scripts in `package.json` using portable `cc-devflow harness <subcommand>` entries (no machine-specific absolute paths), so `/flow:*` can execute the runtime chain without manual script patching.
|
|
193
|
+
|
|
189
194
|
### Optional Dependencies
|
|
190
195
|
|
|
191
196
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -184,8 +184,13 @@ cc-devflow init --dir /path/to/project
|
|
|
184
184
|
# 编译到特定平台
|
|
185
185
|
cc-devflow adapt --platform codex
|
|
186
186
|
cc-devflow adapt --cwd /path/to/project --platform cursor
|
|
187
|
+
|
|
188
|
+
# 直接运行 harness runtime(供 npm scripts 委派)
|
|
189
|
+
cc-devflow harness release --change-id REQ-123
|
|
187
190
|
```
|
|
188
191
|
|
|
192
|
+
`cc-devflow init` 与 `cc-devflow adapt` 现在会自动补齐 `package.json` 中缺失的 `harness:*` scripts,且统一写入可移植的 `cc-devflow harness <subcommand>`(不使用机器绝对路径),避免 `/flow:*` 因脚本缺失退化到手工 fallback。
|
|
193
|
+
|
|
189
194
|
### 验证安装
|
|
190
195
|
|
|
191
196
|
```bash
|
package/bin/cc-devflow-cli.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* [INPUT]: 依赖 .claude 模板、adapt 编译入口与 harness 运行时入口。
|
|
4
|
+
* [OUTPUT]: 提供 init/adapt/harness 命令,并在目标仓库自动补齐 harness npm scripts。
|
|
5
|
+
* [POS]: cc-devflow 的统一 CLI 门面,串联安装、编译与运行时发布链路。
|
|
6
|
+
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
7
|
+
*/
|
|
2
8
|
const fs = require('fs');
|
|
3
9
|
const path = require('path');
|
|
4
10
|
const { spawnSync } = require('child_process');
|
|
@@ -7,6 +13,17 @@ const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
|
7
13
|
const TEMPLATE_DIR = path.join(PACKAGE_ROOT, '.claude');
|
|
8
14
|
const ADAPT_BIN = path.join(PACKAGE_ROOT, 'bin', 'adapt.js');
|
|
9
15
|
const ADAPTER_BIN = path.join(PACKAGE_ROOT, 'bin', 'cc-devflow.js');
|
|
16
|
+
const HARNESS_BIN = path.join(PACKAGE_ROOT, 'bin', 'harness.js');
|
|
17
|
+
const HARNESS_SCRIPT_COMMANDS = {
|
|
18
|
+
'harness:init': 'init',
|
|
19
|
+
'harness:pack': 'pack',
|
|
20
|
+
'harness:plan': 'plan',
|
|
21
|
+
'harness:dispatch': 'dispatch',
|
|
22
|
+
'harness:verify': 'verify',
|
|
23
|
+
'harness:release': 'release',
|
|
24
|
+
'harness:resume': 'resume',
|
|
25
|
+
'harness:janitor': 'janitor'
|
|
26
|
+
};
|
|
10
27
|
|
|
11
28
|
function showHelp() {
|
|
12
29
|
console.log(`
|
|
@@ -15,6 +32,7 @@ Usage: cc-devflow <command> [options]
|
|
|
15
32
|
Commands:
|
|
16
33
|
init Install .claude template into a project
|
|
17
34
|
adapt Compile .claude into multi-platform outputs
|
|
35
|
+
harness Run harness runtime commands (init/verify/release/janitor...)
|
|
18
36
|
|
|
19
37
|
Init options:
|
|
20
38
|
--dir <path> Target project path (default: cwd)
|
|
@@ -34,6 +52,7 @@ Examples:
|
|
|
34
52
|
cc-devflow init --dir /path/to/project
|
|
35
53
|
cc-devflow adapt --platform cursor
|
|
36
54
|
cc-devflow adapt --cwd /path/to/project --platform codex
|
|
55
|
+
cc-devflow harness release --change-id REQ-123
|
|
37
56
|
`);
|
|
38
57
|
}
|
|
39
58
|
|
|
@@ -122,6 +141,114 @@ function copyIncremental(src, dest) {
|
|
|
122
141
|
}
|
|
123
142
|
}
|
|
124
143
|
|
|
144
|
+
function detectJsonIndent(content) {
|
|
145
|
+
const match = content.match(/\n([ \t]+)"[^"\n]+":/);
|
|
146
|
+
if (!match) {
|
|
147
|
+
return 2;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const indent = match[1];
|
|
151
|
+
return indent.includes('\t') ? '\t' : indent.length;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function hasLocalHarnessBinary(targetRoot) {
|
|
155
|
+
return fs.existsSync(path.join(targetRoot, 'bin', 'harness.js'));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getHarnessScriptCommand(targetRoot, subcommand) {
|
|
159
|
+
if (hasLocalHarnessBinary(targetRoot)) {
|
|
160
|
+
return `node bin/harness.js ${subcommand}`;
|
|
161
|
+
}
|
|
162
|
+
return `cc-devflow harness ${subcommand}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function ensureHarnessScripts(targetRoot) {
|
|
166
|
+
const pkgPath = path.join(targetRoot, 'package.json');
|
|
167
|
+
if (!fs.existsSync(pkgPath)) {
|
|
168
|
+
console.warn(`[WARN] Missing package.json in ${targetRoot}; skipping harness script bootstrap.`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let raw;
|
|
173
|
+
let packageJson;
|
|
174
|
+
try {
|
|
175
|
+
raw = fs.readFileSync(pkgPath, 'utf8');
|
|
176
|
+
packageJson = JSON.parse(raw);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.warn(`[WARN] Skipping harness script bootstrap: cannot parse package.json (${error.message})`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!packageJson || typeof packageJson !== 'object' || Array.isArray(packageJson)) {
|
|
183
|
+
console.warn('[WARN] Skipping harness script bootstrap: package.json root must be an object.');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!packageJson.scripts || typeof packageJson.scripts !== 'object' || Array.isArray(packageJson.scripts)) {
|
|
188
|
+
packageJson.scripts = {};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const hasLocalBinary = hasLocalHarnessBinary(targetRoot);
|
|
192
|
+
const added = [];
|
|
193
|
+
const repaired = [];
|
|
194
|
+
let changed = false;
|
|
195
|
+
|
|
196
|
+
for (const [scriptName, subcommand] of Object.entries(HARNESS_SCRIPT_COMMANDS)) {
|
|
197
|
+
const desired = getHarnessScriptCommand(targetRoot, subcommand);
|
|
198
|
+
const existing = packageJson.scripts[scriptName];
|
|
199
|
+
|
|
200
|
+
if (typeof existing === 'undefined') {
|
|
201
|
+
packageJson.scripts[scriptName] = desired;
|
|
202
|
+
added.push(scriptName);
|
|
203
|
+
changed = true;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (typeof existing !== 'string') {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Auto-repair legacy value when local harness binary does not exist.
|
|
212
|
+
if (!hasLocalBinary && existing.trim() === `node bin/harness.js ${subcommand}`) {
|
|
213
|
+
packageJson.scripts[scriptName] = desired;
|
|
214
|
+
repaired.push(scriptName);
|
|
215
|
+
changed = true;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Auto-repair previous injected npx-based value to deterministic runtime path.
|
|
220
|
+
if (!hasLocalBinary && existing.trim() === `npx --yes cc-devflow harness ${subcommand}`) {
|
|
221
|
+
packageJson.scripts[scriptName] = desired;
|
|
222
|
+
repaired.push(scriptName);
|
|
223
|
+
changed = true;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Auto-repair previous absolute-path fallback value (not portable across machines).
|
|
228
|
+
const escapedHarnessBin = HARNESS_BIN.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
229
|
+
const absoluteLegacy = new RegExp(`^node\\s+["']?${escapedHarnessBin}["']?\\s+${subcommand}(\\s*\\|\\|\\s*cc-devflow\\s+harness\\s+${subcommand})?$`);
|
|
230
|
+
if (!hasLocalBinary && absoluteLegacy.test(existing.trim())) {
|
|
231
|
+
packageJson.scripts[scriptName] = desired;
|
|
232
|
+
repaired.push(scriptName);
|
|
233
|
+
changed = true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!changed) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const indent = detectJsonIndent(raw);
|
|
242
|
+
fs.writeFileSync(pkgPath, `${JSON.stringify(packageJson, null, indent)}\n`);
|
|
243
|
+
|
|
244
|
+
if (added.length > 0) {
|
|
245
|
+
console.log(`[UPDATE] package.json (added ${added.join(', ')})`);
|
|
246
|
+
}
|
|
247
|
+
if (repaired.length > 0) {
|
|
248
|
+
console.log(`[UPDATE] package.json (repaired ${repaired.join(', ')})`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
125
252
|
function runInit(args) {
|
|
126
253
|
const { options } = parseCliArgs(args);
|
|
127
254
|
|
|
@@ -141,6 +268,7 @@ function runInit(args) {
|
|
|
141
268
|
// Case 1: Directory does not exist - Clean Install
|
|
142
269
|
if (!fs.existsSync(targetDir)) {
|
|
143
270
|
fs.cpSync(TEMPLATE_DIR, targetDir, { recursive: true });
|
|
271
|
+
ensureHarnessScripts(targetRoot);
|
|
144
272
|
console.log(`Initialized .claude in ${targetRoot}`);
|
|
145
273
|
return 0;
|
|
146
274
|
}
|
|
@@ -150,6 +278,7 @@ function runInit(args) {
|
|
|
150
278
|
console.log('Force flag detected. Resetting .claude directory...');
|
|
151
279
|
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
152
280
|
fs.cpSync(TEMPLATE_DIR, targetDir, { recursive: true });
|
|
281
|
+
ensureHarnessScripts(targetRoot);
|
|
153
282
|
console.log(`Re-initialized .claude in ${targetRoot}`);
|
|
154
283
|
return 0;
|
|
155
284
|
}
|
|
@@ -157,6 +286,7 @@ function runInit(args) {
|
|
|
157
286
|
// Case 3: Directory exists - Incremental Update
|
|
158
287
|
console.log(`Target ${targetDir} already exists. Performing incremental update...`);
|
|
159
288
|
copyIncremental(TEMPLATE_DIR, targetDir);
|
|
289
|
+
ensureHarnessScripts(targetRoot);
|
|
160
290
|
console.log('Incremental update complete. Existing files were preserved.');
|
|
161
291
|
return 0;
|
|
162
292
|
}
|
|
@@ -178,6 +308,8 @@ function runAdapt(args) {
|
|
|
178
308
|
return 1;
|
|
179
309
|
}
|
|
180
310
|
|
|
311
|
+
ensureHarnessScripts(targetRoot);
|
|
312
|
+
|
|
181
313
|
const result = spawnSync(process.execPath, [ADAPT_BIN, ...rest], {
|
|
182
314
|
stdio: 'inherit',
|
|
183
315
|
cwd: targetRoot
|
|
@@ -191,6 +323,24 @@ function runAdapt(args) {
|
|
|
191
323
|
return typeof result.status === 'number' ? result.status : 1;
|
|
192
324
|
}
|
|
193
325
|
|
|
326
|
+
function runHarness(args) {
|
|
327
|
+
const { options, rest } = parseCliArgs(args);
|
|
328
|
+
|
|
329
|
+
const targetRoot = path.resolve(options.cwd || process.cwd());
|
|
330
|
+
const cliArgs = options.help || rest.length === 0 ? ['--help'] : rest;
|
|
331
|
+
const result = spawnSync(process.execPath, [HARNESS_BIN, ...cliArgs], {
|
|
332
|
+
stdio: 'inherit',
|
|
333
|
+
cwd: targetRoot
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
if (result.error) {
|
|
337
|
+
console.error(`Failed to run harness: ${result.error.message}`);
|
|
338
|
+
return 1;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return typeof result.status === 'number' ? result.status : 1;
|
|
342
|
+
}
|
|
343
|
+
|
|
194
344
|
function runAdapter(command, args) {
|
|
195
345
|
const result = spawnSync(process.execPath, [ADAPTER_BIN, command, ...args], {
|
|
196
346
|
stdio: 'inherit'
|
|
@@ -221,6 +371,10 @@ function main() {
|
|
|
221
371
|
return runAdapt(rest);
|
|
222
372
|
}
|
|
223
373
|
|
|
374
|
+
if (command === 'harness') {
|
|
375
|
+
return runHarness(rest);
|
|
376
|
+
}
|
|
377
|
+
|
|
224
378
|
return runAdapter(command, rest);
|
|
225
379
|
}
|
|
226
380
|
|