cc-devflow 4.5.11 → 4.5.13
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/cc-act/CHANGELOG.md +18 -0
- package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
- package/.claude/skills/cc-act/SKILL.md +38 -425
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +2 -13
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +1 -9
- package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +21 -177
- package/.claude/skills/cc-act/references/closure-contract.md +12 -63
- package/.claude/skills/cc-act/references/git-commit-guidelines.md +5 -5
- package/.claude/skills/cc-act/scripts/cc-act-common.sh +5 -322
- package/.claude/skills/cc-act/scripts/detect-ship-target.sh +11 -2
- package/.claude/skills/cc-act/scripts/inspect-git-index.sh +58 -0
- package/.claude/skills/cc-act/scripts/render-pr-brief.sh +40 -440
- package/.claude/skills/cc-act/scripts/verify-act-gate.sh +10 -50
- package/.claude/skills/cc-check/CHANGELOG.md +18 -0
- package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
- package/.claude/skills/cc-check/SKILL.md +33 -456
- package/.claude/skills/cc-check/references/review-contract.md +12 -147
- package/.claude/skills/cc-dev/CHANGELOG.md +15 -0
- package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
- package/.claude/skills/cc-dev/SKILL.md +52 -137
- package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
- package/.claude/skills/cc-do/CHANGELOG.md +11 -0
- package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
- package/.claude/skills/cc-do/SKILL.md +39 -245
- package/.claude/skills/cc-do/references/execution-recovery.md +15 -109
- package/.claude/skills/cc-do/scripts/cc-do-common.sh +5 -57
- package/.claude/skills/cc-do/scripts/check-task-status.sh +35 -65
- package/.claude/skills/cc-do/scripts/mark-task-complete.sh +9 -46
- package/.claude/skills/cc-do/scripts/select-ready-tasks.sh +29 -97
- package/.claude/skills/cc-investigate/CHANGELOG.md +16 -0
- package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
- package/.claude/skills/cc-investigate/SKILL.md +64 -246
- package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -98
- package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -218
- package/.claude/skills/cc-next/CHANGELOG.md +6 -0
- package/.claude/skills/cc-next/PLAYBOOK.md +12 -8
- package/.claude/skills/cc-next/SKILL.md +34 -140
- package/.claude/skills/cc-plan/CHANGELOG.md +16 -0
- package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
- package/.claude/skills/cc-plan/SKILL.md +45 -295
- package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -228
- package/.claude/skills/cc-plan/references/planning-contract.md +24 -161
- package/.claude/skills/cc-plan/scripts/next-change-key.sh +8 -44
- package/.claude/skills/cc-plan/scripts/parse-task-dependencies.js +2 -2
- package/.claude/skills/cc-plan/scripts/validate-scope.sh +1 -1
- package/.claude/skills/cc-pr-land/SKILL.md +14 -114
- package/.claude/skills/cc-pr-review/CHANGELOG.md +4 -0
- package/.claude/skills/cc-pr-review/SKILL.md +20 -103
- package/.claude/skills/cc-review/CHANGELOG.md +23 -0
- package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
- package/.claude/skills/cc-review/SKILL.md +67 -238
- package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +2 -2
- package/.claude/skills/cc-review/references/implementation-review-branch.md +7 -147
- package/.claude/skills/cc-review/references/plan-review-branch.md +5 -147
- package/.claude/skills/cc-review/references/review-methods.md +10 -218
- package/.claude/skills/cc-review/scripts/collect-review-context.sh +4 -63
- package/.claude/skills/cc-roadmap/PLAYBOOK.md +1 -1
- package/.claude/skills/cc-roadmap/SKILL.md +3 -3
- package/.claude/skills/cc-simplify/CHANGELOG.md +7 -0
- package/.claude/skills/cc-simplify/SKILL.md +26 -21
- package/.claude/skills/cc-spec-init/PLAYBOOK.md +12 -48
- package/.claude/skills/cc-spec-init/SKILL.md +29 -132
- package/.claude/skills/cc-spec-init/references/spec-contract.md +8 -17
- package/CHANGELOG.md +21 -0
- package/bin/cc-devflow-cli.js +20 -260
- package/bin/cc-devflow.js +44 -7
- package/docs/commands/README.md +1 -1
- package/docs/commands/README.zh-CN.md +1 -1
- package/docs/examples/README.md +1 -1
- package/docs/examples/START-HERE.md +14 -15
- package/docs/examples/example-bindings.json +11 -11
- package/docs/examples/full-design-blocked/README.md +4 -6
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
- package/docs/examples/local-handoff/README.md +8 -11
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
- package/docs/examples/pdca-loop/README.md +6 -9
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
- package/docs/examples/scripts/check-example-bindings.sh +11 -62
- package/docs/guides/artifact-contract.md +10 -40
- package/docs/guides/getting-started.md +8 -8
- package/docs/guides/getting-started.zh-CN.md +8 -8
- package/docs/guides/minimize-artifacts.md +16 -130
- package/docs/guides/project-postmortem.md +14 -71
- package/lib/compiler/__tests__/skills-registry.test.js +9 -8
- package/lib/compiler/resource-copier.js +29 -0
- package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
- package/lib/skill-runtime/__tests__/benchmark-skills.test.js +3 -3
- package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
- package/lib/skill-runtime/errors.js +3 -3
- package/lib/skill-runtime/index.js +5 -23
- package/lib/skill-runtime/paths.js +5 -52
- package/lib/skill-runtime/query-registry.js +4 -4
- package/lib/skill-runtime/query.js +89 -201
- package/lib/skill-runtime/store.js +4 -40
- package/lib/skill-runtime/trace.js +2 -2
- package/package.json +2 -5
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
- package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
- package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
- package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
- package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
- package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
- package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
- package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
- package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
- package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
- package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
- package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
- package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
- package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -224
- package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -178
- package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
- package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
- package/docs/get-shit-done-strategy-audit.md +0 -518
- package/docs/skill-runtime-migration.md +0 -46
- package/lib/skill-runtime/__tests__/approve.test.js +0 -92
- package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
- package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
- package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
- package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
- package/lib/skill-runtime/__tests__/intent.test.js +0 -203
- package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
- package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
- package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
- package/lib/skill-runtime/__tests__/query.test.js +0 -860
- package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
- package/lib/skill-runtime/__tests__/release.test.js +0 -85
- package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
- package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
- package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
- package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
- package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
- package/lib/skill-runtime/__tests__/task-contract.test.js +0 -874
- package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
- package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
- package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
- package/lib/skill-runtime/__tests__/worker.test.js +0 -56
- package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
- package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
- package/lib/skill-runtime/artifacts.js +0 -88
- package/lib/skill-runtime/context-index.js +0 -545
- package/lib/skill-runtime/delegation.js +0 -533
- package/lib/skill-runtime/intent.js +0 -309
- package/lib/skill-runtime/lifecycle.js +0 -294
- package/lib/skill-runtime/operations/CLAUDE.md +0 -19
- package/lib/skill-runtime/operations/approve.js +0 -81
- package/lib/skill-runtime/operations/autopilot-core.js +0 -337
- package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
- package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
- package/lib/skill-runtime/operations/autopilot.js +0 -163
- package/lib/skill-runtime/operations/dispatch.js +0 -416
- package/lib/skill-runtime/operations/init.js +0 -60
- package/lib/skill-runtime/operations/janitor.js +0 -61
- package/lib/skill-runtime/operations/plan.js +0 -59
- package/lib/skill-runtime/operations/prepare-pr.js +0 -25
- package/lib/skill-runtime/operations/release.js +0 -99
- package/lib/skill-runtime/operations/resume.js +0 -126
- package/lib/skill-runtime/operations/review-records.js +0 -265
- package/lib/skill-runtime/operations/snapshot.js +0 -45
- package/lib/skill-runtime/operations/task-contract.js +0 -593
- package/lib/skill-runtime/operations/verify.js +0 -170
- package/lib/skill-runtime/operations/worker-run.js +0 -531
- package/lib/skill-runtime/operations/worker.js +0 -33
- package/lib/skill-runtime/planner.js +0 -539
- package/lib/skill-runtime/readiness.js +0 -84
- package/lib/skill-runtime/review-records.js +0 -123
- package/lib/skill-runtime/review.js +0 -855
- package/lib/skill-runtime/schemas.js +0 -746
- package/lib/skill-runtime/task-contract.js +0 -188
- package/lib/skill-runtime/team-state.js +0 -122
- package/lib/skill-runtime/workflow-context.js +0 -748
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 依赖 delegation 层提供 worker handoff 与 journal/state 工件,接收 changeId/workerId 以准备本地 worker 会话。
|
|
3
|
-
* [OUTPUT]: 返回 worker launch/prompt/state/assignment 路径,并写入 handoff 准备日志。
|
|
4
|
-
* [POS]: skill runtime 的 worker 会话入口,为未来本地 subagent/worker shell 对接提供稳定接口。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
buildWorkerHandoff,
|
|
10
|
-
appendWorkerJournal,
|
|
11
|
-
appendMessageBus,
|
|
12
|
-
updateWorkerState
|
|
13
|
-
} = require('../delegation');
|
|
14
|
-
|
|
15
|
-
async function runWorkerSession({ repoRoot, changeId, workerId }) {
|
|
16
|
-
const handoff = await buildWorkerHandoff(repoRoot, changeId, workerId);
|
|
17
|
-
|
|
18
|
-
await updateWorkerState(repoRoot, changeId, workerId, {
|
|
19
|
-
role: handoff.role,
|
|
20
|
-
planVersion: handoff.planVersion,
|
|
21
|
-
status: 'handoff_ready',
|
|
22
|
-
currentTask: handoff.currentTask || 'assigned',
|
|
23
|
-
workspace: 'pending'
|
|
24
|
-
});
|
|
25
|
-
await appendWorkerJournal(repoRoot, changeId, workerId, 'handoff prepared for local worker session');
|
|
26
|
-
await appendMessageBus(repoRoot, changeId, `${workerId} handoff prepared`);
|
|
27
|
-
|
|
28
|
-
return handoff;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = {
|
|
32
|
-
runWorkerSession
|
|
33
|
-
};
|
|
@@ -1,539 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 依赖 store 读取 tasks.md 与输出路径,接收 changeId/changeKey,依赖 schemas 校验 manifest。
|
|
3
|
-
* [OUTPUT]: 对外提供 tasks.md → task-manifest.json 的解析与生成能力。
|
|
4
|
-
* [POS]: skill runtime 计划编排层,被 operations/plan 直接调用。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
nowIso,
|
|
10
|
-
readText,
|
|
11
|
-
readJson,
|
|
12
|
-
writeJson,
|
|
13
|
-
exists,
|
|
14
|
-
getTaskManifestPath,
|
|
15
|
-
getTasksMarkdownPath
|
|
16
|
-
} = require('./store');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const { parseManifest } = require('./schemas');
|
|
19
|
-
const { isTaskCompletedStatus } = require('./lifecycle');
|
|
20
|
-
|
|
21
|
-
const TASK_LINE = /^- \[( |x|X)\]\s+(T\d{3})\s*(.*)$/;
|
|
22
|
-
const TRAILING_PATHS = /\(([^)]+)\)\s*$/;
|
|
23
|
-
const DEPENDS_TAG = /dependsOn:([A-Za-z0-9_,-]+)/i;
|
|
24
|
-
const PHASE_HEADING = /^##\s+Phase\s+(\d+)\s*:/i;
|
|
25
|
-
const FIELD_LINE = /^\s{2,}([A-Za-z ]+):\s*(.*)$/;
|
|
26
|
-
|
|
27
|
-
function normalizeTitle(rawTitle) {
|
|
28
|
-
return rawTitle
|
|
29
|
-
.replace(/\[P\]/g, '')
|
|
30
|
-
.replace(DEPENDS_TAG, '')
|
|
31
|
-
.replace(/`[^`]+`/g, '')
|
|
32
|
-
.trim();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function parseTouches(rawTail) {
|
|
36
|
-
const match = rawTail.match(TRAILING_PATHS);
|
|
37
|
-
if (!match) {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return match[1]
|
|
42
|
-
.split(',')
|
|
43
|
-
.map((item) => item.trim())
|
|
44
|
-
.filter(Boolean);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function parseInlineCodeRefs(rawText) {
|
|
48
|
-
const refs = [];
|
|
49
|
-
const pattern = /`([^`]+)`/g;
|
|
50
|
-
let match;
|
|
51
|
-
|
|
52
|
-
while ((match = pattern.exec(rawText)) !== null) {
|
|
53
|
-
const value = match[1].trim();
|
|
54
|
-
if (!value || value.includes('&&') || value.includes('--') || value.includes(' ')) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
refs.push(value);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return refs;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function parseCsvLike(rawText) {
|
|
64
|
-
return String(rawText || '')
|
|
65
|
-
.split(',')
|
|
66
|
-
.map((item) => item.trim())
|
|
67
|
-
.filter(Boolean);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function quoteShellArg(value) {
|
|
71
|
-
return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function buildTaskRunCommand(taskId, title) {
|
|
75
|
-
return `printf '%s\\n' ${quoteShellArg(`[TASK ${taskId}] ${title}`)}`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function parseFieldValues(rawValue) {
|
|
79
|
-
const inlineRefs = parseInlineCodeRefs(rawValue);
|
|
80
|
-
if (inlineRefs.length > 0) {
|
|
81
|
-
return inlineRefs;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return parseCsvLike(
|
|
85
|
-
String(rawValue || '')
|
|
86
|
-
.replace(/`/g, '')
|
|
87
|
-
.replace(/\s*\+\s*/g, ', ')
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function applyTaskField(task, label, value) {
|
|
92
|
-
const normalizedLabel = label.trim().toLowerCase();
|
|
93
|
-
|
|
94
|
-
if (normalizedLabel === 'goal') {
|
|
95
|
-
const goal = value.trim();
|
|
96
|
-
if (goal) {
|
|
97
|
-
task.acceptance.push(goal);
|
|
98
|
-
task.context.notes.push(goal);
|
|
99
|
-
}
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (normalizedLabel === 'files') {
|
|
104
|
-
const files = parseFieldValues(value);
|
|
105
|
-
task.files.push(...files);
|
|
106
|
-
task.touches.push(...files);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (normalizedLabel === 'read first') {
|
|
111
|
-
task.context.readFiles.push(...parseFieldValues(value));
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (normalizedLabel === 'verification') {
|
|
116
|
-
const verification = value.trim();
|
|
117
|
-
if (verification) {
|
|
118
|
-
task.verification.push(verification);
|
|
119
|
-
task.checks.push(verification);
|
|
120
|
-
task.context.commands.push(verification);
|
|
121
|
-
}
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (normalizedLabel === 'evidence') {
|
|
126
|
-
task.evidence.push(...parseCsvLike(value));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function dedupeList(values) {
|
|
131
|
-
return [...new Set(values.filter(Boolean))];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function pushMissing(target, values) {
|
|
135
|
-
for (const value of values) {
|
|
136
|
-
if (value && !target.includes(value)) {
|
|
137
|
-
target.push(value);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function pickPrimaryTestTarget(task) {
|
|
143
|
-
return [...(task.files || []), ...(task.touches || [])].find((item) => /\.test\./i.test(item)) || '';
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function enrichTaskMetadata(task, options = {}) {
|
|
147
|
-
const primaryTestTarget = pickPrimaryTestTarget(task);
|
|
148
|
-
const defaultReadFiles = options.defaultReadFiles || ['design.md'];
|
|
149
|
-
|
|
150
|
-
if (task.acceptance.length === 0) {
|
|
151
|
-
if (task.type === 'TEST') {
|
|
152
|
-
task.acceptance.push(`Prove ${extractFeatureName(task.title) || task.title} fails before implementation`);
|
|
153
|
-
} else if (task.type === 'IMPL') {
|
|
154
|
-
task.acceptance.push(`Make ${extractFeatureName(task.title) || task.title} pass with the smallest implementation`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (task.verification.length === 0) {
|
|
159
|
-
if (primaryTestTarget) {
|
|
160
|
-
task.verification.push(`npm test -- ${primaryTestTarget}`);
|
|
161
|
-
} else if (task.type !== 'OTHER') {
|
|
162
|
-
task.verification.push(`echo "verify ${task.id}"`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (task.evidence.length === 0) {
|
|
167
|
-
if (task.type === 'TEST') {
|
|
168
|
-
task.evidence.push('failing test output');
|
|
169
|
-
} else if (task.type === 'IMPL') {
|
|
170
|
-
task.evidence.push('passing test output');
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (task.context.readFiles.length === 0) {
|
|
175
|
-
pushMissing(task.context.readFiles, defaultReadFiles);
|
|
176
|
-
if (task.type === 'TEST') {
|
|
177
|
-
pushMissing(task.context.readFiles, ['tasks.md']);
|
|
178
|
-
}
|
|
179
|
-
if (primaryTestTarget && !task.context.readFiles.includes(primaryTestTarget)) {
|
|
180
|
-
task.context.readFiles.push(primaryTestTarget);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (task.context.commands.length === 0) {
|
|
185
|
-
task.context.commands.push(...task.verification);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function parseDependsOn(rawTail, fallbackDependsOn, isParallel) {
|
|
190
|
-
const explicit = rawTail.match(DEPENDS_TAG);
|
|
191
|
-
if (explicit) {
|
|
192
|
-
return explicit[1]
|
|
193
|
-
.split(',')
|
|
194
|
-
.map((item) => item.trim())
|
|
195
|
-
.filter(Boolean);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (isParallel || !fallbackDependsOn) {
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return [fallbackDependsOn];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function parseTasksMarkdown(content, options = {}) {
|
|
206
|
-
const lines = content.split(/\r?\n/);
|
|
207
|
-
const tasks = [];
|
|
208
|
-
let previousTaskId = null;
|
|
209
|
-
let currentPhase = 1;
|
|
210
|
-
let currentTask = null;
|
|
211
|
-
|
|
212
|
-
function finalizeTask(task) {
|
|
213
|
-
if (!task) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
task.touches = dedupeList(task.touches);
|
|
218
|
-
task.files = dedupeList(task.files);
|
|
219
|
-
task.checks = dedupeList(task.checks);
|
|
220
|
-
task.acceptance = dedupeList(task.acceptance);
|
|
221
|
-
task.verification = dedupeList(task.verification);
|
|
222
|
-
task.evidence = dedupeList(task.evidence);
|
|
223
|
-
task.context.readFiles = dedupeList(task.context.readFiles);
|
|
224
|
-
task.context.commands = dedupeList(task.context.commands);
|
|
225
|
-
task.context.notes = dedupeList(task.context.notes);
|
|
226
|
-
enrichTaskMetadata(task, options);
|
|
227
|
-
task.acceptance = dedupeList(task.acceptance);
|
|
228
|
-
task.verification = dedupeList(task.verification);
|
|
229
|
-
task.evidence = dedupeList(task.evidence);
|
|
230
|
-
task.context.readFiles = dedupeList(task.context.readFiles);
|
|
231
|
-
task.context.commands = dedupeList(task.context.commands);
|
|
232
|
-
tasks.push(task);
|
|
233
|
-
previousTaskId = task.id;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
for (const line of lines) {
|
|
237
|
-
const phaseMatch = line.match(PHASE_HEADING);
|
|
238
|
-
if (phaseMatch) {
|
|
239
|
-
currentPhase = Number(phaseMatch[1]);
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const match = line.match(TASK_LINE);
|
|
244
|
-
if (match) {
|
|
245
|
-
finalizeTask(currentTask);
|
|
246
|
-
|
|
247
|
-
const [, doneMark, taskId, tail] = match;
|
|
248
|
-
const isParallel = /\[P\]/.test(tail);
|
|
249
|
-
const inlineFiles = parseInlineCodeRefs(tail);
|
|
250
|
-
const touches = [...parseTouches(tail), ...inlineFiles];
|
|
251
|
-
const dependsOn = parseDependsOn(tail, previousTaskId, isParallel);
|
|
252
|
-
const title = normalizeTitle(tail).replace(TRAILING_PATHS, '').trim() || `Task ${taskId}`;
|
|
253
|
-
|
|
254
|
-
// 提取任务类型 [TEST] 或 [IMPL]
|
|
255
|
-
const taskType = extractTaskType(title);
|
|
256
|
-
|
|
257
|
-
currentTask = {
|
|
258
|
-
id: taskId,
|
|
259
|
-
title,
|
|
260
|
-
type: taskType,
|
|
261
|
-
phase: currentPhase,
|
|
262
|
-
parallel: isParallel,
|
|
263
|
-
dependsOn,
|
|
264
|
-
touches,
|
|
265
|
-
files: inlineFiles,
|
|
266
|
-
run: [buildTaskRunCommand(taskId, title)],
|
|
267
|
-
checks: [],
|
|
268
|
-
acceptance: [],
|
|
269
|
-
verification: [],
|
|
270
|
-
evidence: [],
|
|
271
|
-
context: {
|
|
272
|
-
readFiles: [],
|
|
273
|
-
commands: [],
|
|
274
|
-
notes: []
|
|
275
|
-
},
|
|
276
|
-
reviews: {
|
|
277
|
-
spec: 'pending',
|
|
278
|
-
code: 'pending'
|
|
279
|
-
},
|
|
280
|
-
status: doneMark.toLowerCase() === 'x' ? 'passed' : 'pending',
|
|
281
|
-
attempts: 0,
|
|
282
|
-
maxRetries: 1
|
|
283
|
-
};
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (!currentTask) {
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const fieldMatch = line.match(FIELD_LINE);
|
|
292
|
-
if (fieldMatch) {
|
|
293
|
-
applyTaskField(currentTask, fieldMatch[1], fieldMatch[2]);
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
finalizeTask(currentTask);
|
|
299
|
-
|
|
300
|
-
// TDD 顺序验证 (Constitution Article VI)
|
|
301
|
-
validateTDDOrder(tasks);
|
|
302
|
-
|
|
303
|
-
return tasks;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function extractTaskType(title) {
|
|
307
|
-
if (/\[TEST\]/i.test(title)) {
|
|
308
|
-
return 'TEST';
|
|
309
|
-
}
|
|
310
|
-
if (/\[IMPL\]/i.test(title)) {
|
|
311
|
-
return 'IMPL';
|
|
312
|
-
}
|
|
313
|
-
return 'OTHER';
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* TDD 顺序验证 (Constitution Article VI)
|
|
318
|
-
*
|
|
319
|
-
* 规则:
|
|
320
|
-
* 1. 每个 [IMPL] 任务必须有对应的 [TEST] 任务
|
|
321
|
-
* 2. [IMPL] 任务必须依赖对应的 [TEST] 任务 (通过 dependsOn)
|
|
322
|
-
* 3. [TEST] 任务不能依赖 [IMPL] 任务
|
|
323
|
-
*
|
|
324
|
-
* @param {Array} tasks - 任务列表
|
|
325
|
-
* @throws {Error} - TDD 顺序违规时抛出错误
|
|
326
|
-
*/
|
|
327
|
-
function validateTDDOrder(tasks) {
|
|
328
|
-
const violations = [];
|
|
329
|
-
const testTasks = tasks.filter(t => t.type === 'TEST');
|
|
330
|
-
const implTasks = tasks.filter(t => t.type === 'IMPL');
|
|
331
|
-
|
|
332
|
-
// 为每个 IMPL 任务查找对应的 TEST 任务
|
|
333
|
-
for (const implTask of implTasks) {
|
|
334
|
-
// 提取功能名称 (去除 [IMPL] 标记后的主要描述)
|
|
335
|
-
const implFeature = extractFeatureName(implTask.title);
|
|
336
|
-
|
|
337
|
-
// 查找匹配的 TEST 任务 (使用模糊匹配)
|
|
338
|
-
const matchingTest = testTasks.find(testTask => {
|
|
339
|
-
const testFeature = extractFeatureName(testTask.title);
|
|
340
|
-
// 模糊匹配:检查是否包含相同的核心关键词
|
|
341
|
-
return isSimilarFeature(testFeature, implFeature);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
if (!matchingTest) {
|
|
345
|
-
violations.push(
|
|
346
|
-
`Task ${implTask.id} (${implTask.title}) missing corresponding TEST task. ` +
|
|
347
|
-
`Constitution Article VI requires: NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.`
|
|
348
|
-
);
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// 检查依赖关系
|
|
353
|
-
if (!implTask.dependsOn.includes(matchingTest.id)) {
|
|
354
|
-
violations.push(
|
|
355
|
-
`Task ${implTask.id} (${implTask.title}) must depend on ${matchingTest.id} (${matchingTest.title}). ` +
|
|
356
|
-
`TDD violation: Implementation must come AFTER test.`
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// 检查 TEST 任务不能依赖 IMPL 任务
|
|
362
|
-
for (const testTask of testTasks) {
|
|
363
|
-
const implDeps = testTask.dependsOn.filter(depId => {
|
|
364
|
-
const depTask = tasks.find(t => t.id === depId);
|
|
365
|
-
return depTask && depTask.type === 'IMPL';
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
if (implDeps.length > 0) {
|
|
369
|
-
violations.push(
|
|
370
|
-
`Task ${testTask.id} (${testTask.title}) depends on IMPL tasks: ${implDeps.join(', ')}. ` +
|
|
371
|
-
`TDD violation: Tests must be written BEFORE implementation.`
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (violations.length > 0) {
|
|
377
|
-
throw new Error(
|
|
378
|
-
`TDD Order Validation Failed (Constitution Article VI):\n\n` +
|
|
379
|
-
violations.map((v, i) => `${i + 1}. ${v}`).join('\n\n') +
|
|
380
|
-
`\n\nFix tasks.md to follow TDD sequence: [TEST] tasks BEFORE [IMPL] tasks.`
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* 从任务标题中提取功能名称
|
|
387
|
-
* 例如: "[TEST] 用户登录功能" -> "用户登录功能"
|
|
388
|
-
* "[IMPL] 用户登录功能" -> "用户登录功能"
|
|
389
|
-
*/
|
|
390
|
-
function extractFeatureName(title) {
|
|
391
|
-
return title
|
|
392
|
-
.replace(/\[TEST\]/gi, '')
|
|
393
|
-
.replace(/\[IMPL\]/gi, '')
|
|
394
|
-
.replace(/\[P\]/gi, '')
|
|
395
|
-
.replace(/\(dependsOn:[^)]*\)/gi, '')
|
|
396
|
-
.replace(/\([^)]*\)/g, '')
|
|
397
|
-
.trim();
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* 判断两个功能名称是否相似
|
|
402
|
-
* 使用核心关键词匹配策略
|
|
403
|
-
*/
|
|
404
|
-
function isSimilarFeature(feature1, feature2) {
|
|
405
|
-
// 提取核心关键词 (去除常见后缀如"测试"、"实现"、"功能"等)
|
|
406
|
-
const normalize = (str) => str
|
|
407
|
-
.replace(/测试$/g, '')
|
|
408
|
-
.replace(/实现$/g, '')
|
|
409
|
-
.replace(/功能$/g, '')
|
|
410
|
-
.replace(/开发$/g, '')
|
|
411
|
-
.replace(/编写$/g, '')
|
|
412
|
-
.trim();
|
|
413
|
-
|
|
414
|
-
const core1 = normalize(feature1);
|
|
415
|
-
const core2 = normalize(feature2);
|
|
416
|
-
|
|
417
|
-
// 精确匹配
|
|
418
|
-
if (core1 === core2) {
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// 包含匹配 (较长的包含较短的)
|
|
423
|
-
if (core1.length > core2.length) {
|
|
424
|
-
return core1.includes(core2);
|
|
425
|
-
} else {
|
|
426
|
-
return core2.includes(core1);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function buildDefaultTasks(changeId) {
|
|
431
|
-
return [
|
|
432
|
-
{
|
|
433
|
-
id: 'T001',
|
|
434
|
-
title: `Bootstrap ${changeId}`,
|
|
435
|
-
type: 'OTHER',
|
|
436
|
-
phase: 1,
|
|
437
|
-
parallel: false,
|
|
438
|
-
dependsOn: [],
|
|
439
|
-
touches: [],
|
|
440
|
-
files: [],
|
|
441
|
-
run: [buildTaskRunCommand('T001', `Bootstrap ${changeId}`)],
|
|
442
|
-
checks: [],
|
|
443
|
-
acceptance: ['Bootstrap the requirement workspace'],
|
|
444
|
-
verification: ['echo "[TASK T001] Bootstrap complete"'],
|
|
445
|
-
evidence: ['bootstrap output'],
|
|
446
|
-
context: {
|
|
447
|
-
readFiles: ['tasks.md', 'design.md'],
|
|
448
|
-
commands: ['echo "[TASK T001] Bootstrap complete"'],
|
|
449
|
-
notes: ['Default bootstrap task created because tasks.md was missing']
|
|
450
|
-
},
|
|
451
|
-
reviews: {
|
|
452
|
-
spec: 'pending',
|
|
453
|
-
code: 'pending'
|
|
454
|
-
},
|
|
455
|
-
status: 'pending',
|
|
456
|
-
attempts: 0,
|
|
457
|
-
maxRetries: 1
|
|
458
|
-
}
|
|
459
|
-
];
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function deriveManifestExecutionState(tasks) {
|
|
463
|
-
const unfinished = tasks.filter((task) => !isTaskCompletedStatus(task.status));
|
|
464
|
-
|
|
465
|
-
if (unfinished.length === 0) {
|
|
466
|
-
return {
|
|
467
|
-
currentTaskId: null,
|
|
468
|
-
activePhase: null
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const activePhase = Math.min(...unfinished.map((task) => task.phase || 1));
|
|
473
|
-
const completedIds = new Set(
|
|
474
|
-
tasks.filter((task) => isTaskCompletedStatus(task.status)).map((task) => task.id)
|
|
475
|
-
);
|
|
476
|
-
const currentTask = unfinished.find((task) => {
|
|
477
|
-
if ((task.phase || 1) !== activePhase) {
|
|
478
|
-
return false;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return (task.dependsOn || []).every((depId) => completedIds.has(depId));
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
return {
|
|
485
|
-
currentTaskId: currentTask ? currentTask.id : null,
|
|
486
|
-
activePhase
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function applyManifestExecutionState(manifest, updatedAt = nowIso()) {
|
|
491
|
-
const executionState = deriveManifestExecutionState(manifest.tasks || []);
|
|
492
|
-
manifest.currentTaskId = executionState.currentTaskId;
|
|
493
|
-
delete manifest.activePhase;
|
|
494
|
-
manifest.updatedAt = updatedAt;
|
|
495
|
-
return manifest;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
async function createTaskManifest({ repoRoot, changeId, changeKey, goal, overwrite = false }) {
|
|
499
|
-
const pathOptions = changeKey ? { changeKey } : {};
|
|
500
|
-
const manifestPath = getTaskManifestPath(repoRoot, changeId, pathOptions);
|
|
501
|
-
const previous = await readJson(manifestPath, null);
|
|
502
|
-
|
|
503
|
-
if (!overwrite && (await exists(manifestPath))) {
|
|
504
|
-
return parseManifest(previous);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const tasksPath = getTasksMarkdownPath(repoRoot, changeId, pathOptions);
|
|
508
|
-
const hasTasksFile = await exists(tasksPath);
|
|
509
|
-
const legacyDesignPath = path.join(path.dirname(tasksPath), 'design.md');
|
|
510
|
-
const defaultReadFiles = await exists(legacyDesignPath) ? ['design.md'] : ['tasks.md'];
|
|
511
|
-
const rawTasks = hasTasksFile ? parseTasksMarkdown(await readText(tasksPath), { defaultReadFiles }) : [];
|
|
512
|
-
const tasks = rawTasks.length > 0 ? rawTasks : buildDefaultTasks(changeId);
|
|
513
|
-
const previousPlanVersion = previous?.metadata?.planVersion || 0;
|
|
514
|
-
const manifest = applyManifestExecutionState({
|
|
515
|
-
changeId,
|
|
516
|
-
goal: goal || `Deliver ${changeId} safely with task-state truth.`,
|
|
517
|
-
createdAt: previous?.createdAt || nowIso(),
|
|
518
|
-
updatedAt: nowIso(),
|
|
519
|
-
currentTaskId: null,
|
|
520
|
-
tasks,
|
|
521
|
-
metadata: {
|
|
522
|
-
source: hasTasksFile ? 'tasks.md' : 'default',
|
|
523
|
-
generatedBy: 'skill:cc-plan',
|
|
524
|
-
planVersion: previousPlanVersion > 0 ? previousPlanVersion + 1 : 1
|
|
525
|
-
}
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
const parsedManifest = parseManifest(manifest);
|
|
529
|
-
|
|
530
|
-
await writeJson(manifestPath, parsedManifest);
|
|
531
|
-
return parsedManifest;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
module.exports = {
|
|
535
|
-
parseTasksMarkdown,
|
|
536
|
-
deriveManifestExecutionState,
|
|
537
|
-
applyManifestExecutionState,
|
|
538
|
-
createTaskManifest
|
|
539
|
-
};
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 接收 report-card 对象与 report artifact path。
|
|
3
|
-
* [OUTPUT]: 派生 ship-readiness 结果,并在未 ready 时抛出 named error。
|
|
4
|
-
* [POS]: skill runtime 的交付就绪单一真相源,被 query/release 共享。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { namedError } = require('./errors');
|
|
9
|
-
|
|
10
|
-
function deriveVerdict(report) {
|
|
11
|
-
return report.verdict || (report.overall === 'pass' ? 'pass' : 'fail');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function collectShipReadinessBlockers(report) {
|
|
15
|
-
const verdict = deriveVerdict(report);
|
|
16
|
-
const blockers = [];
|
|
17
|
-
|
|
18
|
-
if (report.overall !== 'pass') {
|
|
19
|
-
blockers.push('report-card overall is not pass');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (verdict !== 'pass') {
|
|
23
|
-
blockers.push(`verdict is ${verdict}`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if ((report.reroute || 'none') !== 'none') {
|
|
27
|
-
blockers.push(`reroute is ${report.reroute}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (report.specSyncReady !== true) {
|
|
31
|
-
blockers.push('specSyncReady is not true');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
blockers.push(...(report.blockingFindings || []));
|
|
35
|
-
blockers.push(...(report.gaps || []));
|
|
36
|
-
return blockers;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function deriveShipReadiness(report, { reportPath = '' } = {}) {
|
|
40
|
-
const verdict = deriveVerdict(report);
|
|
41
|
-
const reroute = report.reroute || 'none';
|
|
42
|
-
const specSyncReady = report.specSyncReady === true;
|
|
43
|
-
const blockers = collectShipReadinessBlockers(report);
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
ready: blockers.length === 0,
|
|
47
|
-
verdict,
|
|
48
|
-
reroute,
|
|
49
|
-
specSyncReady,
|
|
50
|
-
blockers,
|
|
51
|
-
reportPath,
|
|
52
|
-
timestamp: report.timestamp || ''
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function assertShipReady(report, {
|
|
57
|
-
reportPath = '',
|
|
58
|
-
errorName = 'ShipReadinessError',
|
|
59
|
-
rescueAction = 'run cc-check until ship-readiness is ready'
|
|
60
|
-
} = {}) {
|
|
61
|
-
const readiness = deriveShipReadiness(report, { reportPath });
|
|
62
|
-
|
|
63
|
-
if (readiness.ready) {
|
|
64
|
-
return readiness;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
throw namedError(
|
|
68
|
-
errorName,
|
|
69
|
-
`Ship readiness blocked: ${readiness.blockers.join('; ')}`,
|
|
70
|
-
{
|
|
71
|
-
artifactRefs: reportPath ? [reportPath] : [],
|
|
72
|
-
rescueAction,
|
|
73
|
-
details: {
|
|
74
|
-
blockers: readiness.blockers
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
module.exports = {
|
|
81
|
-
collectShipReadinessBlockers,
|
|
82
|
-
deriveShipReadiness,
|
|
83
|
-
assertShipReady
|
|
84
|
-
};
|