specline 1.3.0 → 1.3.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/README.md +15 -7
- package/cli.mjs +21 -19
- package/package.json +1 -1
- package/templates/.cursor/skills/specline-pipeline/SKILL.md +58 -600
- package/templates/.cursor/skills/specline-pipeline/references/error-recovery-details.md +49 -0
- package/templates/.cursor/skills/specline-pipeline/references/event-log-spec.md +59 -0
- package/templates/.cursor/skills/specline-pipeline/references/pipeline-state-schema.md +87 -0
- package/templates/.cursor/skills/specline-pipeline/templates/subagent-prompts.md +221 -0
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
- **确定性门禁**:每个阶段用 Shell 脚本的退出码判定是否通过,不做模糊判断
|
|
44
44
|
- **黑盒测试**:测试 Agent 只看 Spec 文档,不能读取任何实现源码
|
|
45
45
|
- **断点续跑**:随时中断,下次从最后一个可信门禁自动恢复(tasks.md 的 `[x]`/`[ ]` 标记进度)
|
|
46
|
-
- **人机协作**:3 个人工检查点——Spec 确认、Review
|
|
46
|
+
- **人机协作**:3 个人工检查点——Spec 确认、Review 可选复核、归档确认,支持 `full`/`minimal`/`none` 三级自动化策略配置(`specline/config.yaml` 中 `pipeline.human_gate_policy`)
|
|
47
47
|
- **Hook 约束体系**:sessionStart 注入 pipeline 上下文 → preToolUse 违规拦截 → postToolUse 操作后提醒,确保长对话中 Agent 不偏离流水线逻辑
|
|
48
48
|
- **安全 Hook**:自动拦截危险 Shell 命令(如 `rm -rf`、`curl|bash`)+ 代码变更后自动格式化
|
|
49
49
|
- **零外部依赖**:不依赖 OpenSpec CLI,全部功能自包含
|
|
@@ -77,10 +77,14 @@ my-project/
|
|
|
77
77
|
│ ├── agents/ ← 9 个 Specline Agent 定义
|
|
78
78
|
│ ├── commands/ ← 3 个 Slash 命令入口
|
|
79
79
|
│ ├── skills/ ← 6 个 Skill 指令
|
|
80
|
+
│ │ └── specline-pipeline/
|
|
81
|
+
│ │ ├── SKILL.md ← 核心编排指令(~500 行)
|
|
82
|
+
│ │ ├── templates/ ← 子 Agent prompt 模板
|
|
83
|
+
│ │ └── references/ ← Schema / 事件日志 / 约束参考文档
|
|
80
84
|
│ ├── hooks/ ← 7 个 Gate/Hook 脚本
|
|
81
85
|
│ └── hooks.json ← Cursor Hook 配置
|
|
82
86
|
├── specline/ ← 运行时目录
|
|
83
|
-
│ ├── config.yaml
|
|
87
|
+
│ ├── config.yaml ← 项目配置(含 pipeline 人机门禁策略)
|
|
84
88
|
│ ├── changes/ ← 变更目录
|
|
85
89
|
│ │ └── archive/ ← 归档目录
|
|
86
90
|
│ └── specs/ ← 主规格目录
|
|
@@ -132,8 +136,8 @@ PHASE 1: SPEC(规格)
|
|
|
132
136
|
├── design.md — 技术设计(架构/数据流/决策)
|
|
133
137
|
└── tasks.md — 任务清单(Type/Depends/Covers/Testable/Files + [ ] 进度标记)
|
|
134
138
|
→ specline-spec-reviewer 审核
|
|
135
|
-
→ Gate: grep + jq 格式校验
|
|
136
|
-
→ 🟡 人工确认 Spec
|
|
139
|
+
→ Gate: grep + jq 格式校验 + semantic 语义检查(Covers 引用悬空 / 依赖环路 / 异常场景缺失 / 模糊需求检测)
|
|
140
|
+
→ 🟡 人工确认 Spec 和任务规划(策略可配:`full` 需确认 / `minimal` `none` 自动通过)
|
|
137
141
|
|
|
138
142
|
PHASE 2: CODING(编码)
|
|
139
143
|
解析 tasks.md → 按依赖 DAG 分层 → 同批次前后端/config Agent 并发
|
|
@@ -152,7 +156,7 @@ PHASE 4: TEST(测试)
|
|
|
152
156
|
→ 自动重试最多 2 次
|
|
153
157
|
|
|
154
158
|
PHASE 5: ARCHIVE(归档)
|
|
155
|
-
→ 🟡
|
|
159
|
+
→ 🟡 人工确认归档(策略可配:`full` `minimal` 需确认 / `none` 自动归档)
|
|
156
160
|
→ delta specs 合并到主规格目录
|
|
157
161
|
→ 按日期归档到 specline/changes/archive/
|
|
158
162
|
✅ 完成
|
|
@@ -205,6 +209,10 @@ PHASE 5: ARCHIVE(归档)
|
|
|
205
209
|
▼ ▼
|
|
206
210
|
specline-pipeline SKILL ← 编排层 编排者直接执行(无子 Agent)
|
|
207
211
|
│ Read → Write → ReadLints → Shell → 归档
|
|
212
|
+
├── SKILL.md 核心编排指令(~500 行)
|
|
213
|
+
├── templates/ subagent-prompts.md(3 套 prompt 模板)
|
|
214
|
+
└── references/ Schema / 事件日志 / 约束参考文档
|
|
215
|
+
│
|
|
208
216
|
┌───┼──────────────────┬──────────────────────┐
|
|
209
217
|
▼ ▼ ▼ ▼
|
|
210
218
|
9 个子 Agent specline-pipeline- Cursor Hooks
|
|
@@ -218,7 +226,7 @@ specline-pipeline SKILL ← 编排层 编排者直接执行(
|
|
|
218
226
|
|------|------|
|
|
219
227
|
| `specline init [path]` | 在指定路径(默认当前目录)初始化 Specline 项目,复制模板文件并生成锁文件 |
|
|
220
228
|
| `specline update` | 检查 CLI 是否有新版本可用(npm registry),输出更新提示 |
|
|
221
|
-
| `specline sync [--dry-run] [path]` | 将上游最新模板文件同步到项目,基于 Lock File
|
|
229
|
+
| `specline sync [--dry-run] [path]` | 将上游最新模板文件同步到项目,基于 Lock File 智能识别安全更新/冲突/仅本地修改。hooks.json 语义合并(保留用户自定义 hook)、config.yaml 注释级更新(保留用户配置值)、CONFLICT 覆盖前自动创建 `.orig` 备份。`--dry-run` 预览变更不实际写入 |
|
|
222
230
|
| `specline --version` | 显示当前 CLI 版本号 |
|
|
223
231
|
| `specline --help` | 显示帮助信息 |
|
|
224
232
|
|
|
@@ -242,7 +250,7 @@ specline-pipeline SKILL ← 编排层 编排者直接执行(
|
|
|
242
250
|
|
|
243
251
|
| 门禁 | 检查内容 |
|
|
244
252
|
|------|---------|
|
|
245
|
-
| Spec |
|
|
253
|
+
| Spec | 结构性检查(`grep` 检查章节完整性、WHEN/THEN 配对、字段格式)+ 语义检查(`semantic` 子命令:Covers 引用悬空、依赖环路、异常场景缺失、模糊需求、反向覆盖、Type-文件一致性,分 ERROR/WARNING/INFO 三级严重度) |
|
|
246
254
|
| Build | `tsc --noEmit` / `python -m compileall` 编译检查 + Testable 任务单元测试文件存在性与语法检查 |
|
|
247
255
|
| Lint | `ruff` / `eslint` 退出码 + code-review.json 中 error 数量 |
|
|
248
256
|
| Test | 测试框架退出码 + 覆盖率阈值 |
|
package/cli.mjs
CHANGED
|
@@ -88,11 +88,13 @@ function writeLockFile(projectDir, lockData) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
91
|
+
* 遍历指定目录所有文件,构建锁数据结构
|
|
92
|
+
* rootDir: 要遍历的根目录(必须是目标项目目录,这样 init 后锁哈希与实际文件一致)
|
|
92
93
|
* 返回 { version, synced_at, files: Map<string, string> }
|
|
93
94
|
*/
|
|
94
|
-
function buildLockData(projectDir) {
|
|
95
|
+
function buildLockData(projectDir, rootDir) {
|
|
95
96
|
const files = new Map();
|
|
97
|
+
const walkRoot = rootDir || TEMPLATES_DIR;
|
|
96
98
|
|
|
97
99
|
function walk(dir, base) {
|
|
98
100
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -107,7 +109,7 @@ function buildLockData(projectDir) {
|
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
walk(
|
|
112
|
+
walk(walkRoot, '');
|
|
111
113
|
|
|
112
114
|
return {
|
|
113
115
|
version: VERSION,
|
|
@@ -423,7 +425,7 @@ initialized_at: "${new Date().toISOString()}"
|
|
|
423
425
|
if (existsSync(lockPath) && !forceMode) {
|
|
424
426
|
warn('锁文件已存在,跳过');
|
|
425
427
|
} else {
|
|
426
|
-
const lockData = buildLockData(target);
|
|
428
|
+
const lockData = buildLockData(target, target);
|
|
427
429
|
writeLockFile(target, lockData);
|
|
428
430
|
success('已生成锁文件');
|
|
429
431
|
}
|
|
@@ -521,20 +523,14 @@ function cmd_sync({ dryRun, targetPath }) {
|
|
|
521
523
|
const lockData = readLockFile(target);
|
|
522
524
|
|
|
523
525
|
// 4. 版本校验
|
|
524
|
-
if (lockData) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
if (compareVersions(lockData.version, VERSION) > 0) {
|
|
530
|
-
warn('锁文件版本 (v' + lockData.version + ') 高于 CLI 版本 (v' + VERSION + '),继续同步可能导致问题');
|
|
531
|
-
if (!process.stdin.isTTY) {
|
|
532
|
-
error('非交互式环境,已跳过同步');
|
|
533
|
-
process.exit(1);
|
|
534
|
-
}
|
|
535
|
-
error('锁文件版本高于 CLI,请先更新 CLI');
|
|
526
|
+
if (lockData && compareVersions(lockData.version, VERSION) > 0) {
|
|
527
|
+
warn('锁文件版本 (v' + lockData.version + ') 高于 CLI 版本 (v' + VERSION + '),继续同步可能导致问题');
|
|
528
|
+
if (!process.stdin.isTTY) {
|
|
529
|
+
error('非交互式环境,已跳过同步');
|
|
536
530
|
process.exit(1);
|
|
537
531
|
}
|
|
532
|
+
error('锁文件版本高于 CLI,请先更新 CLI');
|
|
533
|
+
process.exit(1);
|
|
538
534
|
}
|
|
539
535
|
|
|
540
536
|
// 5. 收集所有需要分类的路径
|
|
@@ -549,6 +545,10 @@ function cmd_sync({ dryRun, targetPath }) {
|
|
|
549
545
|
// 6. 分类
|
|
550
546
|
const results = [];
|
|
551
547
|
for (const path of allPaths) {
|
|
548
|
+
if (path === '.specline-config.yaml') {
|
|
549
|
+
// 项目标识文件,由 specline init 生成(含时间戳),sync 不覆盖
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
552
|
const templateHash = upstreamFiles.get(path) || null;
|
|
553
553
|
const lockEntry = lockData ? (lockData.files.get(path) || null) : null;
|
|
554
554
|
const projectPath = join(target, path);
|
|
@@ -621,7 +621,8 @@ function cmd_sync({ dryRun, targetPath }) {
|
|
|
621
621
|
const labels = { NEW: '➕ 新增', WILL_UPDATE: '🔄 更新', CONFLICT: '⚠️ 冲突(将备份后覆盖)', NO_LOCK_CONFLICT: '⚠️ 无锁记录', UPSTREAM_REMOVED: '🗑️ 上游移除' };
|
|
622
622
|
log(labels[r.type] + ' ' + r.path);
|
|
623
623
|
}
|
|
624
|
-
if (stats.newCount === 0 && stats.updated === 0 && stats.conflicted === 0
|
|
624
|
+
if (stats.newCount === 0 && stats.updated === 0 && stats.conflicted === 0
|
|
625
|
+
&& stats.skippedModified === 0 && stats.upstreamRemoved === 0) {
|
|
625
626
|
log('所有模板文件已是最新,无需同步');
|
|
626
627
|
} else {
|
|
627
628
|
log('\n以上为预览,未实际执行。去掉 --dry-run 以执行同步。');
|
|
@@ -718,9 +719,10 @@ function cmd_sync({ dryRun, targetPath }) {
|
|
|
718
719
|
});
|
|
719
720
|
|
|
720
721
|
// 11. 输出摘要
|
|
721
|
-
if (stats.newCount === 0 && stats.updated === 0 && stats.conflicted === 0
|
|
722
|
+
if (stats.newCount === 0 && stats.updated === 0 && stats.conflicted === 0
|
|
723
|
+
&& stats.skippedModified === 0 && stats.upstreamRemoved === 0
|
|
722
724
|
&& !mergeStats.hooksMerged && !mergeStats.configUpdated) {
|
|
723
|
-
|
|
725
|
+
success('项目模板已是最新,无需同步 (v' + VERSION + ')');
|
|
724
726
|
} else {
|
|
725
727
|
log('📊 同步摘要:');
|
|
726
728
|
log(' 总模板文件: ' + allPaths.size);
|