spec-wave 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +327 -0
- package/README.zh-CN.md +327 -0
- package/RELEASING.md +52 -0
- package/assets/README.md +30 -0
- package/assets/ci/samples/README.md +76 -0
- package/assets/ci/samples/hgm-ingest.yml.example +42 -0
- package/assets/ci/samples/lint-wiki-delta.pin.yml.example +47 -0
- package/assets/ci/samples/lint-wiki-delta.yml.example +49 -0
- package/assets/ci/samples/pytest.yml.example +49 -0
- package/assets/ci/samples/quality.yml.example +56 -0
- package/assets/ci/samples/skills-validate.yml.example +38 -0
- package/assets/ci/samples/tech-graph.yml.example +55 -0
- package/assets/coding_wiki/templates/README.md +115 -0
- package/assets/coding_wiki/templates/_index.md +19 -0
- package/assets/coding_wiki/templates/context.md +18 -0
- package/assets/coding_wiki/templates/stable.md +26 -0
- package/assets/coding_wiki/templates/topics/wiki_layout.md +13 -0
- package/assets/coding_wiki/templates/topics/wikilinks_export.md +13 -0
- package/assets/coding_wiki/templates/volatile.md +25 -0
- package/assets/docs/POINTER_ONBOARDING.md +9 -0
- package/assets/docs/POINTER_RUNBOOK_wiki_delta.md +18 -0
- package/assets/docs/POINTER_SDD_HAT_FLOW.md +8 -0
- package/assets/docs/POINTER_USER_GUIDE.md +8 -0
- package/assets/graph/stubs/README.md +6 -0
- package/assets/graph/templates/00_main.graph.yaml +72 -0
- package/assets/graph/templates/00_main.md +85 -0
- package/assets/graph/templates/01_struct.md +38 -0
- package/assets/graph/templates/02_version.md +7 -0
- package/assets/graph/templates/10_flow_MAIN.graph.yaml +105 -0
- package/assets/graph/templates/10_flow_MAIN.md +103 -0
- package/assets/graph/templates/99_mermaid_protocol.md +156 -0
- package/assets/graph/templates/README.md +66 -0
- package/assets/harness/README.md +18 -0
- package/assets/harness/discipline-coverage.yaml +320 -0
- package/assets/harness/invokes/README.md +22 -0
- package/assets/harness/invokes/TEMPLATE_invoke.md +61 -0
- package/assets/harness/lifecycle.yaml +127 -0
- package/assets/harness/prompts/00-orchestrator.md +91 -0
- package/assets/harness/prompts/10-spec-requirements.md +63 -0
- package/assets/harness/prompts/10-task-requirements.md +70 -0
- package/assets/harness/prompts/20-spec-audit.md +50 -0
- package/assets/harness/prompts/20-task-audit.md +90 -0
- package/assets/harness/prompts/30-execute-code.md +64 -0
- package/assets/harness/prompts/40-self-check.md +65 -0
- package/assets/harness/prompts/FRAGMENT_00_delegate_only_v1_zh.md +19 -0
- package/assets/harness/prompts/FRAGMENT_30_gate_verify_v1_zh.md +34 -0
- package/assets/harness/prompts/FRAGMENT_30_invoke_block_v1_zh.md +28 -0
- package/assets/harness/prompts/FRAGMENT_hat_reanchor_v1_zh.md +19 -0
- package/assets/harness/prompts/README.md +67 -0
- package/assets/harness/prompts/TEMPLATE_30_gate_stop.md +36 -0
- package/assets/harness/templates/FRAGMENT_task_domain_infer_v1_zh.md +16 -0
- package/assets/harness/templates/ONTOLOGY_consumer_slice_v1.md +41 -0
- package/assets/harness/templates/QUICKREF_v1_zh.md +35 -0
- package/assets/harness/templates/README.md +50 -0
- package/assets/harness/templates/TASK_TEMPLATE.md +133 -0
- package/assets/harness/templates/TASK_done_README.md +76 -0
- package/assets/harness/templates/TASK_epic.md +103 -0
- package/assets/harness/templates/TASK_graph_bootstrap.md +115 -0
- package/assets/harness/templates/VIEW_done_by_domain.md +66 -0
- package/assets/harness/templates/VIEW_done_thin_pointer.md +16 -0
- package/assets/ide/adapters/AGENTS.md.fragment.example +33 -0
- package/assets/ide/adapters/CLAUDE.md.fragment.example +32 -0
- package/assets/ide/adapters/README.md +57 -0
- package/assets/ide/adapters/cursor-harness-starter.mdc.example +39 -0
- package/assets/ide/commands/claude/kit/apply-standards.md +10 -0
- package/assets/ide/commands/claude/kit/gate-status.md +10 -0
- package/assets/ide/commands/claude/kit/graph-check.md +8 -0
- package/assets/ide/commands/claude/kit/hat-00-delegate.md +9 -0
- package/assets/ide/commands/claude/kit/hat-10-spec.md +9 -0
- package/assets/ide/commands/claude/kit/hat-10-task.md +9 -0
- package/assets/ide/commands/claude/kit/hat-20-spec-audit.md +9 -0
- package/assets/ide/commands/claude/kit/hat-20-task-audit.md +9 -0
- package/assets/ide/commands/claude/kit/hat-reanchor.md +8 -0
- package/assets/ide/commands/claude/kit/init-guide.md +16 -0
- package/assets/ide/commands/claude/kit/sync-prompts-guide.md +9 -0
- package/assets/ide/commands/claude/kit/verify.md +16 -0
- package/assets/ide/commands/cursor/kit-apply-standards.md +12 -0
- package/assets/ide/commands/cursor/kit-gate-status.md +12 -0
- package/assets/ide/commands/cursor/kit-graph-check.md +10 -0
- package/assets/ide/commands/cursor/kit-hat-00-delegate.md +11 -0
- package/assets/ide/commands/cursor/kit-hat-10-spec.md +11 -0
- package/assets/ide/commands/cursor/kit-hat-10-task.md +11 -0
- package/assets/ide/commands/cursor/kit-hat-20-spec-audit.md +11 -0
- package/assets/ide/commands/cursor/kit-hat-20-task-audit.md +11 -0
- package/assets/ide/commands/cursor/kit-hat-reanchor.md +10 -0
- package/assets/ide/commands/cursor/kit-init-guide.md +18 -0
- package/assets/ide/commands/cursor/kit-sync-prompts-guide.md +11 -0
- package/assets/ide/commands/cursor/kit-verify.md +18 -0
- package/assets/ide/host-adapt/README.md +113 -0
- package/assets/ide/host-adapt/examples/mvp-hosts.yaml +78 -0
- package/assets/ide/host-adapt/host-adapt.schema.json +97 -0
- package/assets/ide/skills-orch/README.md +16 -0
- package/assets/ide/skills-orch/kit-apply-standards/SKILL.md +17 -0
- package/assets/ide/skills-orch/kit-gate-status/SKILL.md +17 -0
- package/assets/ide/skills-orch/kit-hat-reanchor/SKILL.md +15 -0
- package/assets/ide/skills-orch/kit-init-guide/SKILL.md +23 -0
- package/assets/ide/skills-orch/kit-verify/SKILL.md +23 -0
- package/assets/ontology.yaml +113 -0
- package/assets/skills/README.md +37 -0
- package/assets/skills/harness-00-delegate-only/SKILL.md +19 -0
- package/assets/skills/harness-10-spec/SKILL.md +63 -0
- package/assets/skills/harness-10-task/SKILL.md +70 -0
- package/assets/skills/harness-20-spec-audit/SKILL.md +50 -0
- package/assets/skills/harness-20-task-audit/SKILL.md +90 -0
- package/assets/skills/harness-20-task-audit/references/TEMPLATE_30_gate_stop.md +36 -0
- package/assets/skills/harness-hat-reanchor/SKILL.md +19 -0
- package/assets/standards/POINTER_workspace_truth_v1_zh.md +42 -0
- package/assets/standards/README.md +35 -0
- package/assets/standards/SOURCES_v1_zh.md +49 -0
- package/assets/standards/TEMPLATE_CODING_BASELINE_L1_v1_zh.md +88 -0
- package/assets/standards/TEMPLATE_CODING_BASELINE_L2_backend_v1_zh.md +75 -0
- package/assets/standards/TEMPLATE_CODING_BASELINE_L2_frontend_v1_zh.md +76 -0
- package/bin/dsh-coding-kit.js +7 -0
- package/bin/specgate.js +7 -0
- package/bin/validate_template_compliance.py +470 -0
- package/cordis.patch.yml +5 -0
- package/lib/cli-checks.d.ts +68 -0
- package/lib/cli-checks.js +812 -0
- package/lib/cli-checks.js.map +1 -0
- package/lib/cli-graph-hgm.d.ts +74 -0
- package/lib/cli-graph-hgm.js +416 -0
- package/lib/cli-graph-hgm.js.map +1 -0
- package/lib/cli-graph-yaml.d.ts +79 -0
- package/lib/cli-graph-yaml.js +602 -0
- package/lib/cli-graph-yaml.js.map +1 -0
- package/lib/cli-graph.d.ts +1 -0
- package/lib/cli-graph.js +199 -0
- package/lib/cli-graph.js.map +1 -0
- package/lib/cli-host.d.ts +24 -0
- package/lib/cli-host.js +1197 -0
- package/lib/cli-host.js.map +1 -0
- package/lib/cli-lifecycle.d.ts +2 -0
- package/lib/cli-lifecycle.js +401 -0
- package/lib/cli-lifecycle.js.map +1 -0
- package/lib/cli-refresh-ide-blocks.d.ts +66 -0
- package/lib/cli-refresh-ide-blocks.js +484 -0
- package/lib/cli-refresh-ide-blocks.js.map +1 -0
- package/lib/cli-shared.d.ts +74 -0
- package/lib/cli-shared.js +281 -0
- package/lib/cli-shared.js.map +1 -0
- package/lib/cli-skills.d.ts +52 -0
- package/lib/cli-skills.js +363 -0
- package/lib/cli-skills.js.map +1 -0
- package/lib/cli-status.d.ts +2 -0
- package/lib/cli-status.js +251 -0
- package/lib/cli-status.js.map +1 -0
- package/lib/cli-sync-prompts.d.ts +25 -0
- package/lib/cli-sync-prompts.js +158 -0
- package/lib/cli-sync-prompts.js.map +1 -0
- package/lib/cli-sync.d.ts +2 -0
- package/lib/cli-sync.js +124 -0
- package/lib/cli-sync.js.map +1 -0
- package/lib/cli-task-extra.d.ts +53 -0
- package/lib/cli-task-extra.js +408 -0
- package/lib/cli-task-extra.js.map +1 -0
- package/lib/cli-timeline.d.ts +28 -0
- package/lib/cli-timeline.js +68 -0
- package/lib/cli-timeline.js.map +1 -0
- package/lib/cli-wiki.d.ts +21 -0
- package/lib/cli-wiki.js +189 -0
- package/lib/cli-wiki.js.map +1 -0
- package/lib/cli.d.ts +22 -0
- package/lib/cli.js +1034 -0
- package/lib/cli.js.map +1 -0
- package/lib/host-contract.d.ts +23 -0
- package/lib/host-contract.js +143 -0
- package/lib/host-contract.js.map +1 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.js +290 -0
- package/lib/index.js.map +1 -0
- package/lib/yaml.d.ts +4 -0
- package/lib/yaml.js +10 -0
- package/lib/yaml.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fail, isS2AbsPath, packageRoot, resolveTarget, takeOption } from "./cli-shared.js";
|
|
5
|
+
import { yamlDump, yamlLoad } from "./yaml.js";
|
|
6
|
+
export const EXECUTE_TRACK = 'starter-experimental';
|
|
7
|
+
const RESOURCE_RE = /(?<![A-Za-z0-9_])(?:FRAGMENT|TEMPLATE)_[A-Za-z0-9_\-]+\.md/g;
|
|
8
|
+
const MD_LINK_RE = /\]\(\.\/((?:FRAGMENT|TEMPLATE)_[A-Za-z0-9_\-]+\.md)\)/g;
|
|
9
|
+
const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
10
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
11
|
+
export function parseSkillPrompt(content) {
|
|
12
|
+
const m = FRONTMATTER_RE.exec(content);
|
|
13
|
+
if (!m)
|
|
14
|
+
return { frontmatter: null, body: content };
|
|
15
|
+
try {
|
|
16
|
+
return { frontmatter: yamlLoad(m[1]), body: m[2] };
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
return { frontmatter: null, body: content, parseError: e.message };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function validateSkillFrontmatter(fm, source) {
|
|
23
|
+
const errors = [];
|
|
24
|
+
if (!fm || typeof fm !== 'object')
|
|
25
|
+
return [`${source}: 缺 frontmatter 或非对象`];
|
|
26
|
+
const { name, description, compatibility } = fm;
|
|
27
|
+
if (typeof name !== 'string' || name.length < 1 || name.length > 64) {
|
|
28
|
+
errors.push(`${source}: name 缺失或超长(1–64)`);
|
|
29
|
+
}
|
|
30
|
+
else if (!NAME_RE.test(name)) {
|
|
31
|
+
errors.push(`${source}: name 非法(仅小写字母/数字/单连字符 · 不得首尾连字符/连续连字符): ${name}`);
|
|
32
|
+
}
|
|
33
|
+
if (typeof description !== 'string' || description.length < 1) {
|
|
34
|
+
errors.push(`${source}: description 缺失`);
|
|
35
|
+
}
|
|
36
|
+
else if (description.length > 1024) {
|
|
37
|
+
errors.push(`${source}: description 超长(${description.length} > 1024)`);
|
|
38
|
+
}
|
|
39
|
+
if (compatibility !== undefined) {
|
|
40
|
+
if (typeof compatibility !== 'string' || compatibility.length > 500) {
|
|
41
|
+
errors.push(`${source}: compatibility 超长或非字符串(≤500)`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return errors;
|
|
45
|
+
}
|
|
46
|
+
export function loadSkillPrompts({ promptsDir }) {
|
|
47
|
+
const files = readdirSync(promptsDir)
|
|
48
|
+
.filter((f) => f.endsWith('.md'))
|
|
49
|
+
// 00 全文不进默认 Skills(不建 harness-00);TEMPLATE / README 非 skill
|
|
50
|
+
// FRAGMENT_* 仅当带合法 skill frontmatter(name)才入分发(re-anchor / 00-delegate)
|
|
51
|
+
.filter((f) => !/^(README|TEMPLATE_|00-)/.test(f))
|
|
52
|
+
.sort();
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const file of files) {
|
|
55
|
+
const content = readFileSync(path.join(promptsDir, file), 'utf8');
|
|
56
|
+
const { frontmatter, body, parseError } = parseSkillPrompt(content);
|
|
57
|
+
if (/^FRAGMENT_/.test(file) && !parseError && !frontmatter?.name)
|
|
58
|
+
continue;
|
|
59
|
+
const errors = parseError
|
|
60
|
+
? [`${file}: frontmatter YAML 解析失败: ${parseError}`]
|
|
61
|
+
: validateSkillFrontmatter(frontmatter, file);
|
|
62
|
+
out.push({ file, frontmatter: frontmatter || {}, body, errors });
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function rewriteLinks(body) {
|
|
67
|
+
return body.replace(MD_LINK_RE, '](references/$1)');
|
|
68
|
+
}
|
|
69
|
+
function referencedResources(body) {
|
|
70
|
+
return [...new Set(body.match(RESOURCE_RE) || [])].sort();
|
|
71
|
+
}
|
|
72
|
+
function renderSkillMd(frontmatter, body) {
|
|
73
|
+
return `---\n${yamlDump(frontmatter, { lineWidth: -1 })}---\n${body}`;
|
|
74
|
+
}
|
|
75
|
+
function firstSentence(description) {
|
|
76
|
+
const idx = description.indexOf('。');
|
|
77
|
+
return idx === -1 ? description : description.slice(0, idx + 1);
|
|
78
|
+
}
|
|
79
|
+
function renderReadme(skills, { withExecuteHats }) {
|
|
80
|
+
const rows = skills
|
|
81
|
+
.map((s) => `| [\`${s.frontmatter.name}/\`](./${s.frontmatter.name}/SKILL.md) | ${s.frontmatter.metadata?.hat_id ?? ''} | ${firstSentence(s.frontmatter.description || '')} |`)
|
|
82
|
+
.join('\n');
|
|
83
|
+
const executeNote = withExecuteHats
|
|
84
|
+
? ''
|
|
85
|
+
: `
|
|
86
|
+
## 执行帽缺席说明
|
|
87
|
+
|
|
88
|
+
\`harness-30-execute\` / \`harness-40-self-check\`(执行帽)**不在本分发**:其 skill 化须先通过 T1 闸绕开评测(\`eval/t1_gate_bypass/\` S1–S3)。
|
|
89
|
+
评测/维护者可用 \`npx spec-wave skills build --with-execute-hats\` 本地生成(仅供评测环境,勿装入生产 client)。
|
|
90
|
+
`;
|
|
91
|
+
const zeroNote = `
|
|
92
|
+
## 00 全文缺席说明
|
|
93
|
+
|
|
94
|
+
\`00-orchestrator.md\`(00 全文)**不在本分发**(仅 prompts 同步)。短片段 \`harness-00-delegate-only\` / \`harness-hat-reanchor\` **默认可分发**。
|
|
95
|
+
`;
|
|
96
|
+
return `# skills/ · Agent Skills 标准封装(生成物 · 勿手改)
|
|
97
|
+
|
|
98
|
+
> **本目录由 \`npx spec-wave skills build\` 生成**;真值 = \`harness/prompts/\` 条文(frontmatter + 正文)。
|
|
99
|
+
> 改动请改条文后重跑 build;\`npx spec-wave skills check\` 会拦截任何手改 drift。
|
|
100
|
+
> 规范:https://agentskills.io/specification
|
|
101
|
+
|
|
102
|
+
## 安装(各 client 路径不同 · 复制或软链均可)
|
|
103
|
+
|
|
104
|
+
| 路径 | 用途 | 谁写入 |
|
|
105
|
+
|------|------|--------|
|
|
106
|
+
| DSH \`<repo>/.dsh/skills/\` | 消费者 Skill 安装落点 | \`npx spec-wave skills install\` |
|
|
107
|
+
| DSH \`$HOME/.dsh/skills/\` | 用户级安装落点 | \`npx spec-wave skills install --global\` |
|
|
108
|
+
| Claude Code \`<repo>/.claude/skills/\` 或 \`~/.claude/skills/\` | Claude skill 目录 | 用户另拷或 \`--out\`;**默认不写** |
|
|
109
|
+
|
|
110
|
+
\`.dsh/coding-kit\` / \`.coding-kit\` 是规范覆盖(\`apply_coding_standards\` / \`init_coding_kit\`),**不是** skill 目录,禁止当作 install dest。
|
|
111
|
+
|
|
112
|
+
已验证(2026-08-22 · 对照 DSH 上游源码 deepseek-harness@141eb6f,dsh 0.1.0-rc.8):DSH runtime **会自动扫描** \`.dsh/skills\` 与 \`$HOME/.dsh/skills\` 两个 **安装落点** 并 **按需加载**。证据锚点:\`packages/skill/skill-filesystem/src/index.ts:246\`(\`<projectRoot>/.dsh/skills\`,rank 100)与 \`:253\`(\`$DSH_HOME\` 或 \`~/.dsh\` 下 \`skills/\`,rank 400);目录级文档 \`docs/subsystems/skills.md\`「Local discovery priority」表同口径。结构要求:目录包 \`<name>/SKILL.md\` 或平铺 \`<name>.md\`(index.ts:724-728);frontmatter 必填 \`name\`/\`description\`,\`name\` 须 kebab-case(index.ts:810-816)。扫描/加载属 DSH runtime 行为契约,随上游版本演进,锚点对应 0.1.0-rc.8。
|
|
113
|
+
|
|
114
|
+
## 技能清单
|
|
115
|
+
|
|
116
|
+
| skill | hat_id | 用途 |
|
|
117
|
+
|-------|--------|------|
|
|
118
|
+
${rows}
|
|
119
|
+
${executeNote}${zeroNote}`;
|
|
120
|
+
}
|
|
121
|
+
export function generateSkills({ promptsDir, withExecuteHats = false, }) {
|
|
122
|
+
const prompts = loadSkillPrompts({ promptsDir });
|
|
123
|
+
const errors = prompts.flatMap((p) => p.errors);
|
|
124
|
+
const files = new Map();
|
|
125
|
+
const selected = prompts.filter((p) => withExecuteHats || p.frontmatter.metadata?.track !== EXECUTE_TRACK);
|
|
126
|
+
for (const p of selected) {
|
|
127
|
+
const name = p.frontmatter.name;
|
|
128
|
+
if (!name)
|
|
129
|
+
continue;
|
|
130
|
+
files.set(`${name}/SKILL.md`, renderSkillMd(p.frontmatter, rewriteLinks(p.body)));
|
|
131
|
+
for (const res of referencedResources(p.body)) {
|
|
132
|
+
const src = path.join(promptsDir, res);
|
|
133
|
+
if (existsSync(src))
|
|
134
|
+
files.set(`${name}/references/${res}`, readFileSync(src, 'utf8'));
|
|
135
|
+
else
|
|
136
|
+
errors.push(`${p.file}: 引用的资源不存在: ${res}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
files.set('README.md', renderReadme(selected, { withExecuteHats }));
|
|
140
|
+
return { files, skills: selected, errors };
|
|
141
|
+
}
|
|
142
|
+
export function buildSkills({ promptsDir, outDir, withExecuteHats = false, }) {
|
|
143
|
+
const { files, errors } = generateSkills({ promptsDir, withExecuteHats });
|
|
144
|
+
if (errors.length > 0) {
|
|
145
|
+
const err = new Error(`skills build 前置校验失败:\n${errors.join('\n')}`);
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
rmSync(outDir, { recursive: true, force: true });
|
|
149
|
+
const written = [];
|
|
150
|
+
for (const [rel, content] of [...files.entries()].sort()) {
|
|
151
|
+
const abs = path.join(outDir, rel);
|
|
152
|
+
mkdirSync(path.dirname(abs), { recursive: true });
|
|
153
|
+
writeFileSync(abs, content);
|
|
154
|
+
written.push(rel);
|
|
155
|
+
}
|
|
156
|
+
return { written };
|
|
157
|
+
}
|
|
158
|
+
function readTreeFiles(dir) {
|
|
159
|
+
const out = new Map();
|
|
160
|
+
const walk = (cur, rel) => {
|
|
161
|
+
if (!existsSync(cur))
|
|
162
|
+
return;
|
|
163
|
+
for (const entry of readdirSync(cur, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
164
|
+
const abs = path.join(cur, entry.name);
|
|
165
|
+
const r = rel ? `${rel}/${entry.name}` : entry.name;
|
|
166
|
+
if (entry.isDirectory())
|
|
167
|
+
walk(abs, r);
|
|
168
|
+
else
|
|
169
|
+
out.set(r, readFileSync(abs, 'utf8'));
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
if (existsSync(dir))
|
|
173
|
+
walk(dir, '');
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
export function checkSkills({ promptsDir, skillsDir, }) {
|
|
177
|
+
const { files: expected, errors } = generateSkills({ promptsDir });
|
|
178
|
+
if (errors.length > 0)
|
|
179
|
+
return { ok: false, errors };
|
|
180
|
+
const actual = readTreeFiles(skillsDir);
|
|
181
|
+
const driftErrors = [];
|
|
182
|
+
for (const key of [...expected.keys()].sort()) {
|
|
183
|
+
if (!actual.has(key))
|
|
184
|
+
driftErrors.push(`缺失: ${key}`);
|
|
185
|
+
else if (actual.get(key) !== expected.get(key))
|
|
186
|
+
driftErrors.push(`drift: ${key}`);
|
|
187
|
+
}
|
|
188
|
+
for (const key of [...actual.keys()].sort()) {
|
|
189
|
+
if (!expected.has(key))
|
|
190
|
+
driftErrors.push(`多余(非生成物或执行帽误入): ${key}`);
|
|
191
|
+
}
|
|
192
|
+
return { ok: driftErrors.length === 0, errors: driftErrors };
|
|
193
|
+
}
|
|
194
|
+
const SKILLS_USAGE = `用法:
|
|
195
|
+
npx spec-wave skills install [--target DIR] [--out DIR] [--global] [--force] [--with-execute-hats]
|
|
196
|
+
npx spec-wave skills build [--with-execute-hats]
|
|
197
|
+
npx spec-wave skills check
|
|
198
|
+
|
|
199
|
+
落点说明:.dsh/skills 两个安装落点已对照 DSH 上游源码验证(deepseek-harness@141eb6f · 0.1.0-rc.8)——
|
|
200
|
+
DSH runtime 自动扫描 <repo>/.dsh/skills 与 $HOME/.dsh/skills 并按需加载;
|
|
201
|
+
证据锚点 packages/skill/skill-filesystem/src/index.ts:246 / :253,详见 README「扫描验证」节。
|
|
202
|
+
`;
|
|
203
|
+
export const EXECUTE_HAT_DIRS = new Set(['harness-30-execute', 'harness-40-self-check']);
|
|
204
|
+
function posixNorm(absPath) {
|
|
205
|
+
return path.resolve(absPath).replace(/\\/g, '/');
|
|
206
|
+
}
|
|
207
|
+
function isCodingKitDest(absDest) {
|
|
208
|
+
const n = posixNorm(absDest);
|
|
209
|
+
return (n.endsWith('/.coding-kit') ||
|
|
210
|
+
n.includes('/.coding-kit/') ||
|
|
211
|
+
n.endsWith('/.dsh/coding-kit') ||
|
|
212
|
+
n.includes('/.dsh/coding-kit/'));
|
|
213
|
+
}
|
|
214
|
+
export function isExecuteHatSkipped(parentDir, name, withExecuteHats) {
|
|
215
|
+
if (withExecuteHats)
|
|
216
|
+
return false;
|
|
217
|
+
if (EXECUTE_HAT_DIRS.has(name))
|
|
218
|
+
return true;
|
|
219
|
+
const skillMd = path.join(parentDir, name, 'SKILL.md');
|
|
220
|
+
if (!existsSync(skillMd) || !statSync(skillMd).isFile())
|
|
221
|
+
return false;
|
|
222
|
+
const { frontmatter } = parseSkillPrompt(readFileSync(skillMd, 'utf8'));
|
|
223
|
+
return frontmatter?.metadata?.track === EXECUTE_TRACK;
|
|
224
|
+
}
|
|
225
|
+
function listCopyableSkillDirs(src, withExecuteHats) {
|
|
226
|
+
return readdirSync(src, { withFileTypes: true })
|
|
227
|
+
.filter((ent) => ent.isDirectory() && !isExecuteHatSkipped(src, ent.name, withExecuteHats))
|
|
228
|
+
.map((ent) => ent.name)
|
|
229
|
+
.sort();
|
|
230
|
+
}
|
|
231
|
+
function copySkillsTree({ src, dest, force, withExecuteHats, }) {
|
|
232
|
+
let copied = 0;
|
|
233
|
+
let skipped = 0;
|
|
234
|
+
const visit = (from, to) => {
|
|
235
|
+
mkdirSync(to, { recursive: true });
|
|
236
|
+
for (const ent of readdirSync(from, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
237
|
+
if (ent.isDirectory()) {
|
|
238
|
+
if (isExecuteHatSkipped(from, ent.name, withExecuteHats))
|
|
239
|
+
continue;
|
|
240
|
+
visit(path.join(from, ent.name), path.join(to, ent.name));
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (!ent.isFile())
|
|
244
|
+
continue;
|
|
245
|
+
const destFile = path.join(to, ent.name);
|
|
246
|
+
if (existsSync(destFile) && !force) {
|
|
247
|
+
skipped += 1;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
mkdirSync(path.dirname(destFile), { recursive: true });
|
|
251
|
+
copyFileSync(path.join(from, ent.name), destFile);
|
|
252
|
+
copied += 1;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
visit(src, dest);
|
|
256
|
+
return { copied, skipped };
|
|
257
|
+
}
|
|
258
|
+
function cmdSkillsInstall(args) {
|
|
259
|
+
const global = args.includes('--global');
|
|
260
|
+
const force = args.includes('--force');
|
|
261
|
+
const withExecuteHats = args.includes('--with-execute-hats');
|
|
262
|
+
let rest = args.filter((a) => a !== '--global' && a !== '--force' && a !== '--with-execute-hats');
|
|
263
|
+
const { value: targetArg, rest: r1 } = takeOption(rest, '--target');
|
|
264
|
+
rest = r1;
|
|
265
|
+
const { value: outArg, rest: r2 } = takeOption(rest, '--out');
|
|
266
|
+
rest = r2;
|
|
267
|
+
if (rest.length > 0)
|
|
268
|
+
fail(`skills install 未知参数: ${rest.join(' ')}\n${SKILLS_USAGE}`);
|
|
269
|
+
const hasTarget = targetArg !== undefined;
|
|
270
|
+
const hasOut = outArg !== undefined;
|
|
271
|
+
if (global && (hasOut || hasTarget)) {
|
|
272
|
+
fail(`\`--global\` 与 \`--out\` / \`--target\` 互斥\n${SKILLS_USAGE}`);
|
|
273
|
+
}
|
|
274
|
+
if (hasOut && hasTarget) {
|
|
275
|
+
fail(`\`--out\` 与 \`--target\` 互斥\n${SKILLS_USAGE}`);
|
|
276
|
+
}
|
|
277
|
+
if (hasOut && outArg.startsWith('~')) {
|
|
278
|
+
fail('`--out` 不得以 ~ 开头(禁止在 cwd 创建名为 ~ 的目录)。请用绝对路径或 --global');
|
|
279
|
+
}
|
|
280
|
+
let dest;
|
|
281
|
+
if (global) {
|
|
282
|
+
const home = os.homedir();
|
|
283
|
+
if (!home)
|
|
284
|
+
fail('无法解析 homedir(请设置 HOME 或改用 --out 绝对路径)');
|
|
285
|
+
dest = path.join(home, '.dsh', 'skills');
|
|
286
|
+
}
|
|
287
|
+
else if (hasOut) {
|
|
288
|
+
dest = path.resolve(process.cwd(), outArg);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
dest = path.join(resolveTarget(process.cwd(), targetArg), '.dsh', 'skills');
|
|
292
|
+
}
|
|
293
|
+
dest = path.resolve(dest);
|
|
294
|
+
if (isCodingKitDest(dest)) {
|
|
295
|
+
fail('拒写:dest 命中 .dsh/coding-kit 或 .coding-kit(规范覆盖目录 ≠ .dsh/skills)');
|
|
296
|
+
}
|
|
297
|
+
if (isS2AbsPath(dest)) {
|
|
298
|
+
fail('拒写:dest 命中 S2 过程域(docs/tasks/ · reviews/ · invokes/by-task/)');
|
|
299
|
+
}
|
|
300
|
+
// DEBT R-05:第四拒写分支——dest 命中产品包自身 assets/skills(安装源 ≠ 安装落点),
|
|
301
|
+
// 含其子目录;判定基准与下方 src 同根(packageRoot()/assets/skills)。
|
|
302
|
+
const packageSkillsRoot = posixNorm(path.join(packageRoot(), 'assets', 'skills'));
|
|
303
|
+
const destNorm = posixNorm(dest);
|
|
304
|
+
if (destNorm === packageSkillsRoot || destNorm.startsWith(`${packageSkillsRoot}/`)) {
|
|
305
|
+
fail('拒写:dest 命中产品包自身 assets/skills(安装源 ≠ 安装落点,会自我覆盖发布资产)');
|
|
306
|
+
}
|
|
307
|
+
if (existsSync(dest) && !statSync(dest).isDirectory()) {
|
|
308
|
+
fail(`dest 已存在且为文件(非目录): ${dest}`);
|
|
309
|
+
}
|
|
310
|
+
// DSH_CK_SKILLS_SRC:内部测试钩子(非公开契约,勿依赖)——允许测试把 skills 源
|
|
311
|
+
// 指向临时副本,避免 install 测试改动包内 assets/skills(DEF-018 T4 / D2=a)。
|
|
312
|
+
const src = process.env.DSH_CK_SKILLS_SRC
|
|
313
|
+
? path.resolve(process.env.DSH_CK_SKILLS_SRC)
|
|
314
|
+
: path.join(packageRoot(), 'assets', 'skills');
|
|
315
|
+
if (!existsSync(src) || !statSync(src).isDirectory()) {
|
|
316
|
+
fail('源 assets/skills 缺失。消费者请重装 npm 包;维护者请先执行 skills build');
|
|
317
|
+
}
|
|
318
|
+
const skillDirs = listCopyableSkillDirs(src, withExecuteHats);
|
|
319
|
+
if (skillDirs.length === 0) {
|
|
320
|
+
fail('源 assets/skills 无可复制 skill 目录(默认跳过 30/40;可 --with-execute-hats 或先 skills build)');
|
|
321
|
+
}
|
|
322
|
+
const { copied, skipped } = copySkillsTree({ src, dest, force, withExecuteHats });
|
|
323
|
+
console.log(`SKILLS INSTALL: PASS · copied=${copied} skipped=${skipped} → ${dest}`);
|
|
324
|
+
}
|
|
325
|
+
export async function cmdSkills(args) {
|
|
326
|
+
const promptsDir = path.join(packageRoot(), 'assets', 'harness', 'prompts');
|
|
327
|
+
const skillsDir = path.join(packageRoot(), 'assets', 'skills');
|
|
328
|
+
const [sub, ...rest] = args;
|
|
329
|
+
if (!sub || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
330
|
+
console.log(SKILLS_USAGE);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (sub === 'install') {
|
|
334
|
+
cmdSkillsInstall(rest);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (sub === 'build') {
|
|
338
|
+
const withExecuteHats = rest.includes('--with-execute-hats');
|
|
339
|
+
const unknown = rest.filter((a) => a !== '--with-execute-hats');
|
|
340
|
+
if (unknown.length > 0)
|
|
341
|
+
fail(`skills build 未知参数: ${unknown.join(' ')}`);
|
|
342
|
+
const { written } = buildSkills({ promptsDir, outDir: skillsDir, withExecuteHats });
|
|
343
|
+
console.log(`SKILLS BUILD: PASS · ${written.length} 文件 → ${skillsDir}${withExecuteHats ? '(含执行帽 · 仅供评测环境)' : ''}`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (sub === 'check') {
|
|
347
|
+
const unknown = rest.filter((a) => a !== '--json');
|
|
348
|
+
if (unknown.length > 0)
|
|
349
|
+
fail(`skills check 未知参数: ${unknown.join(' ')}`);
|
|
350
|
+
const r = checkSkills({ promptsDir, skillsDir });
|
|
351
|
+
if (rest.includes('--json'))
|
|
352
|
+
console.log(JSON.stringify(r, null, 2));
|
|
353
|
+
else if (r.ok)
|
|
354
|
+
console.log('SKILLS CHECK: PASS · frontmatter 合法 · skills/ 无 drift');
|
|
355
|
+
else
|
|
356
|
+
console.log(`SKILLS CHECK: FAIL\n${r.errors.map((e) => ` - ${e}`).join('\n')}`);
|
|
357
|
+
if (!r.ok)
|
|
358
|
+
fail('skills check FAIL', 2);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
fail(`skills 子命令未知: ${sub ?? '(空)'}\n${SKILLS_USAGE}`);
|
|
362
|
+
}
|
|
363
|
+
//# sourceMappingURL=cli-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-skills.js","sourceRoot":"","sources":["../src/cli-skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { evaluateMayStart30, extractHatsFromInvokeFilename, fail, normalizeSlug, parseHarnessMeta, parseHumanGates, resolveTarget, resolveTaskPath, STATUS_RE, takeOption, toRel, } from "./cli-shared.js";
|
|
4
|
+
// DEF-003 T3/T4:findReview 单一实现源迁至 cli-checks.ts(verify / dry-run / status 共用)
|
|
5
|
+
import { findReview } from "./cli-checks.js";
|
|
6
|
+
import { summarizeTaskHgm } from "./cli-graph-hgm.js";
|
|
7
|
+
const OBS_STATUS_SCHEMA = 'obs_status.v1';
|
|
8
|
+
// DEF-016 D1:reviews.CLOSE 检测口径与 cli.ts CLOSE_STATUSES 同(done/completed)
|
|
9
|
+
const CLOSE_STATUSES = new Set(['done', 'completed']);
|
|
10
|
+
function extractTaskStatus(content) {
|
|
11
|
+
const m = content.match(STATUS_RE);
|
|
12
|
+
return m ? m[1].toLowerCase() : null;
|
|
13
|
+
}
|
|
14
|
+
function listActiveTasks(target) {
|
|
15
|
+
const dirs = [path.join(target, 'docs/tasks/active'), path.join(target, 'docs/harness/tasks/active')];
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const dir of dirs) {
|
|
18
|
+
if (!existsSync(dir))
|
|
19
|
+
continue;
|
|
20
|
+
for (const name of readdirSync(dir)) {
|
|
21
|
+
if (!name.endsWith('.md') || !name.startsWith('task_'))
|
|
22
|
+
continue;
|
|
23
|
+
out.push(toRel(target, path.join(dir, name)));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return out.sort();
|
|
27
|
+
}
|
|
28
|
+
function findLastInvoke(target, slug) {
|
|
29
|
+
const empty = { path: null, hat_id: null };
|
|
30
|
+
if (!slug)
|
|
31
|
+
return empty;
|
|
32
|
+
const candidates = [
|
|
33
|
+
path.join(target, 'docs/harness/invokes/by-task', slug),
|
|
34
|
+
path.join(target, 'docs/harness/invokes/by-task', normalizeSlug(slug)),
|
|
35
|
+
path.join(target, 'invokes/by-task', slug),
|
|
36
|
+
];
|
|
37
|
+
if (slug.includes('-')) {
|
|
38
|
+
candidates.push(path.join(target, 'docs/harness/invokes/by-task', slug.replace(/-/g, '_')));
|
|
39
|
+
}
|
|
40
|
+
let best = null;
|
|
41
|
+
for (const dir of candidates) {
|
|
42
|
+
if (!existsSync(dir))
|
|
43
|
+
continue;
|
|
44
|
+
for (const name of readdirSync(dir)) {
|
|
45
|
+
if (!name.endsWith('.md'))
|
|
46
|
+
continue;
|
|
47
|
+
const abs = path.join(dir, name);
|
|
48
|
+
const st = statSync(abs);
|
|
49
|
+
if (!best || st.mtimeMs > best.mtimeMs) {
|
|
50
|
+
const hats = extractHatsFromInvokeFilename(name);
|
|
51
|
+
best = {
|
|
52
|
+
mtimeMs: st.mtimeMs,
|
|
53
|
+
path: toRel(target, abs),
|
|
54
|
+
hat_id: hats[0] || null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!best)
|
|
60
|
+
return empty;
|
|
61
|
+
return { path: best.path, hat_id: best.hat_id };
|
|
62
|
+
}
|
|
63
|
+
function buildTaskStatus(target, taskFile, options = {}) {
|
|
64
|
+
const absTask = resolveTaskPath(target, taskFile);
|
|
65
|
+
if (!existsSync(absTask))
|
|
66
|
+
fail(`task 文件不存在: ${taskFile}`);
|
|
67
|
+
const content = readFileSync(absTask, 'utf8');
|
|
68
|
+
const meta = parseHarnessMeta(content);
|
|
69
|
+
const gates = parseHumanGates(content);
|
|
70
|
+
const gateEval = evaluateMayStart30(gates);
|
|
71
|
+
const slug = meta.task_slug || path.basename(absTask, '.md');
|
|
72
|
+
const blockers = [];
|
|
73
|
+
for (const g of gates) {
|
|
74
|
+
if (g.status !== 'approved' && String(g.blocksHats || '').includes('30')) {
|
|
75
|
+
blockers.push(`${g.id}=${g.status}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (blockers.length === 0 && !gateEval.ok && gateEval.reason)
|
|
79
|
+
blockers.push(gateEval.reason);
|
|
80
|
+
const reviewFound = findReview(target, absTask);
|
|
81
|
+
const lastInvoke = findLastInvoke(target, slug);
|
|
82
|
+
const hgm = summarizeTaskHgm(target, slug);
|
|
83
|
+
const taskStatus = extractTaskStatus(content);
|
|
84
|
+
// DEF-016 D1(接线):CLOSE = 关账完成信号(代理口径,非「close 审查通过」强证据)——
|
|
85
|
+
// task 状态 ∈ CLOSE_STATUSES(与 task close 归档行为对齐)或文件位于 done/ 目录。
|
|
86
|
+
const inDoneDir = absTask.split(path.sep).includes('done');
|
|
87
|
+
const closeDone = (taskStatus !== null && CLOSE_STATUSES.has(taskStatus)) || inDoneDir;
|
|
88
|
+
const payload = {
|
|
89
|
+
schema_version: OBS_STATUS_SCHEMA,
|
|
90
|
+
task_slug: slug,
|
|
91
|
+
task_path: toRel(target, absTask),
|
|
92
|
+
status: taskStatus || 'unknown',
|
|
93
|
+
gates: gates.map((g) => ({ id: g.id, status: g.status, blocks_hats: g.blocksHats })),
|
|
94
|
+
may_start_30: gateEval.ok,
|
|
95
|
+
blockers,
|
|
96
|
+
last_invoke: lastInvoke,
|
|
97
|
+
reviews: { R1: reviewFound, CLOSE: closeDone },
|
|
98
|
+
verify_preview: {
|
|
99
|
+
ok: gateEval.ok,
|
|
100
|
+
reason: gateEval.ok ? '闸投影 PASS(非正式 verify)' : gateEval.reason,
|
|
101
|
+
},
|
|
102
|
+
hgm: { event_count: hgm.event_count, last_at: hgm.last_at },
|
|
103
|
+
kpi_section: /^###\s*KPI/m.test(content),
|
|
104
|
+
next_hint: gateEval.ok ? '闸已齐 · 跑 verify 后开 30(status 不替代 verify)' : `签收阻塞闸后再开 30(${blockers[0] || '见 gates'})`,
|
|
105
|
+
};
|
|
106
|
+
const warnings = [];
|
|
107
|
+
let checkFailed = false;
|
|
108
|
+
if (options.check) {
|
|
109
|
+
const checkReasons = [];
|
|
110
|
+
if (!reviewFound)
|
|
111
|
+
checkReasons.push('missing R<n> review');
|
|
112
|
+
if (!gateEval.ok) {
|
|
113
|
+
checkReasons.push(`may_start_30=false · ${gateEval.reason || blockers.join('; ') || 'blocked'}`);
|
|
114
|
+
}
|
|
115
|
+
if (checkReasons.length > 0) {
|
|
116
|
+
checkFailed = true;
|
|
117
|
+
for (const r of checkReasons)
|
|
118
|
+
warnings.push(`FAIL: status --check · ${r}`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
warnings.push('OK: status --check passed(仍须正式 harness verify;--check ≠ 替代 verify)');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { payload, warnings, checkFailed };
|
|
125
|
+
}
|
|
126
|
+
function formatStatusHuman(payload) {
|
|
127
|
+
const gates = payload.gates.length === 0
|
|
128
|
+
? ' (none)'
|
|
129
|
+
: payload.gates.map((g) => ` - ${g.id}=${g.status} blocks=${g.blocks_hats || '—'}`).join('\n');
|
|
130
|
+
return [
|
|
131
|
+
`task: ${payload.task_slug}`,
|
|
132
|
+
`path: ${payload.task_path}`,
|
|
133
|
+
`status: ${payload.status}`,
|
|
134
|
+
`gates:`,
|
|
135
|
+
gates,
|
|
136
|
+
`may_start_30: ${payload.may_start_30}`,
|
|
137
|
+
`blockers: ${payload.blockers.length === 0 ? '—' : payload.blockers.join('; ')}`,
|
|
138
|
+
`last_invoke: ${payload.last_invoke.path ?? '—'} hat=${payload.last_invoke.hat_id ?? '—'}`,
|
|
139
|
+
`reviews: R1=${payload.reviews.R1 ? 'yes' : 'no'} CLOSE=${payload.reviews.CLOSE ? 'yes' : 'no'}`,
|
|
140
|
+
`verify_preview: ${payload.verify_preview.ok ? 'PASS' : 'BLOCK'} reason=${payload.verify_preview.reason}`,
|
|
141
|
+
`hgm: events=${payload.hgm.event_count ?? 'unknown'} last_at=${payload.hgm.last_at ?? '—'}`,
|
|
142
|
+
`next_hint: ${payload.next_hint}`,
|
|
143
|
+
'',
|
|
144
|
+
'NOTE: verify_preview 为只读预览;30 前仍须正式运行 verify。',
|
|
145
|
+
].join('\n');
|
|
146
|
+
}
|
|
147
|
+
export async function cmdStatus(args) {
|
|
148
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
149
|
+
console.log(`用法: npx spec-wave status [--target PATH] [--task FILE] [--json] [--check]
|
|
150
|
+
`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
let rest = args;
|
|
154
|
+
const { value: targetArg, rest: r1 } = takeOption(rest, '--target');
|
|
155
|
+
rest = r1;
|
|
156
|
+
const { value: taskFile, rest: r2 } = takeOption(rest, '--task');
|
|
157
|
+
rest = r2;
|
|
158
|
+
const json = rest.includes('--json');
|
|
159
|
+
const check = rest.includes('--check');
|
|
160
|
+
rest = rest.filter((a) => a !== '--json' && a !== '--check');
|
|
161
|
+
if (rest.length > 0)
|
|
162
|
+
fail(`status 未知参数: ${rest.join(' ')}`);
|
|
163
|
+
const target = resolveTarget(process.cwd(), targetArg);
|
|
164
|
+
if (!taskFile) {
|
|
165
|
+
if (check)
|
|
166
|
+
fail('status --check 须配合 --task FILE');
|
|
167
|
+
const rows = listActiveTasks(target).map((taskPath) => {
|
|
168
|
+
const abs = path.join(target, taskPath);
|
|
169
|
+
const content = readFileSync(abs, 'utf8');
|
|
170
|
+
const meta = parseHarnessMeta(content);
|
|
171
|
+
const gates = parseHumanGates(content);
|
|
172
|
+
const gateEval = evaluateMayStart30(gates);
|
|
173
|
+
const pending = gates.find((g) => g.status !== 'approved' && String(g.blocksHats || '').includes('30'));
|
|
174
|
+
return {
|
|
175
|
+
task_slug: meta.task_slug || path.basename(taskPath, '.md'),
|
|
176
|
+
task_path: taskPath.replace(/\\/g, '/'),
|
|
177
|
+
status: extractTaskStatus(content) || 'unknown',
|
|
178
|
+
blocking_gate: pending ? `${pending.id}=${pending.status}` : null,
|
|
179
|
+
may_start_30: gateEval.ok,
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
if (json)
|
|
183
|
+
console.log(JSON.stringify({ schema_version: 'obs_status_list.v1', tasks: rows }, null, 2));
|
|
184
|
+
else {
|
|
185
|
+
if (rows.length === 0)
|
|
186
|
+
process.stdout.write('active tasks: (none)\n');
|
|
187
|
+
else {
|
|
188
|
+
const lines = ['active tasks:'];
|
|
189
|
+
for (const r of rows) {
|
|
190
|
+
lines.push(` - ${r.task_slug} status=${r.status} blocking=${r.blocking_gate || '—'} may_start_30=${r.may_start_30} path=${r.task_path}`);
|
|
191
|
+
}
|
|
192
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const { payload, warnings, checkFailed } = buildTaskStatus(target, taskFile, { check });
|
|
198
|
+
for (const w of warnings)
|
|
199
|
+
console.error(w);
|
|
200
|
+
if (json)
|
|
201
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
202
|
+
else
|
|
203
|
+
console.log(formatStatusHuman(payload));
|
|
204
|
+
if (check && checkFailed)
|
|
205
|
+
fail('', 2);
|
|
206
|
+
}
|
|
207
|
+
export async function cmdTimeline(args) {
|
|
208
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
209
|
+
console.log(`用法: npx spec-wave timeline --task FILE [--target PATH] [--json] [--limit N] [--ingest]
|
|
210
|
+
`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
let rest = args.filter((a) => a !== '--json' && a !== '--ingest');
|
|
214
|
+
const json = args.includes('--json');
|
|
215
|
+
const ingest = args.includes('--ingest');
|
|
216
|
+
const { value: targetArg, rest: r1 } = takeOption(rest, '--target');
|
|
217
|
+
rest = r1;
|
|
218
|
+
const { value: taskOpt, rest: r2 } = takeOption(rest, '--task');
|
|
219
|
+
rest = r2;
|
|
220
|
+
const { value: limitRaw, rest: r3 } = takeOption(rest, '--limit');
|
|
221
|
+
rest = r3;
|
|
222
|
+
let taskFile = taskOpt;
|
|
223
|
+
if (taskFile != null && taskFile.startsWith('-')) {
|
|
224
|
+
fail(`timeline --task 须紧跟文件路径(收到了旗标 ${taskFile})`);
|
|
225
|
+
}
|
|
226
|
+
if (!taskFile && rest.length === 1 && !rest[0].startsWith('-')) {
|
|
227
|
+
taskFile = rest[0];
|
|
228
|
+
rest = [];
|
|
229
|
+
}
|
|
230
|
+
if (rest.length > 0)
|
|
231
|
+
fail(`timeline 未知参数: ${rest.join(' ')}`);
|
|
232
|
+
if (!taskFile)
|
|
233
|
+
fail('timeline 须指定 --task FILE');
|
|
234
|
+
let limit = null;
|
|
235
|
+
if (limitRaw != null) {
|
|
236
|
+
limit = Number(limitRaw);
|
|
237
|
+
if (!Number.isFinite(limit) || limit < 0)
|
|
238
|
+
fail(`timeline --limit 须为非负整数: ${limitRaw}`);
|
|
239
|
+
limit = Math.floor(limit);
|
|
240
|
+
}
|
|
241
|
+
const target = resolveTarget(process.cwd(), targetArg);
|
|
242
|
+
const { buildTaskTimeline, formatTimelineHuman } = await import("./cli-timeline.js");
|
|
243
|
+
const { payload, warnings } = buildTaskTimeline(target, taskFile, { limit, ingest });
|
|
244
|
+
for (const w of warnings)
|
|
245
|
+
console.error(w);
|
|
246
|
+
if (json)
|
|
247
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
248
|
+
else
|
|
249
|
+
process.stdout.write(formatTimelineHuman(payload));
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=cli-status.js.map
|