@localsummer/incspec 0.0.7 → 0.0.9
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 +66 -9
- package/commands/analyze.mjs +26 -10
- package/commands/apply.mjs +78 -33
- package/commands/archive.mjs +25 -3
- package/commands/collect-req.mjs +8 -0
- package/commands/help.mjs +9 -3
- package/commands/merge.mjs +64 -33
- package/commands/reset.mjs +166 -0
- package/commands/status.mjs +30 -7
- package/index.mjs +8 -1
- package/lib/agents.mjs +1 -1
- package/lib/config.mjs +7 -7
- package/lib/cursor.mjs +20 -5
- package/lib/workflow.mjs +115 -22
- package/package.json +1 -1
- package/templates/AGENTS.md +101 -502
- package/templates/INCSPEC_BLOCK.md +1 -1
- package/templates/WORKFLOW.md +1 -0
- package/templates/cursor-commands/analyze-codeflow.md +12 -1
- package/templates/cursor-commands/apply-increment-code.md +129 -1
- package/templates/cursor-commands/merge-to-baseline.md +87 -1
- package/templates/cursor-commands/structured-requirements-collection.md +6 -0
- package/templates/inc-spec-skill/SKILL.md +60 -5
- package/templates/inc-spec-skill/references/analyze-codeflow.md +12 -1
- package/templates/inc-spec-skill/references/apply-increment-code.md +129 -1
- package/templates/inc-spec-skill/references/merge-to-baseline.md +87 -1
- package/templates/inc-spec-skill/references/structured-requirements-collection.md +6 -0
- package/templates/inc-spec-skill/.security-scan-passed +0 -4
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|

|
|
14
14
|
|
|
15
|
-
IncSpec 通过**增量规范驱动开发**让人类与 AI 编程助手保持一致 - 这是一个
|
|
15
|
+
IncSpec 通过**增量规范驱动开发**让人类与 AI 编程助手保持一致 - 这是一个 7 步工作流,在修改代码前先捕获代码流程基线。**无需 API 密钥。**
|
|
16
16
|
|
|
17
17
|
## 为什么选择 IncSpec?
|
|
18
18
|
|
|
@@ -30,7 +30,7 @@ AI 编程助手在处理复杂前端代码库时常常力不从心,因为 API
|
|
|
30
30
|
## IncSpec 对比一览
|
|
31
31
|
|
|
32
32
|
- **前端专注**:专为 API 数据流程分析和组件依赖设计。
|
|
33
|
-
- **
|
|
33
|
+
- **7 步循环**:分析 → 收集需求 → 收集依赖 → 设计 → 应用 → 合并 → 归档。
|
|
34
34
|
- **基线管理**:每个循环产出新基线,作为下一轮迭代的起点。
|
|
35
35
|
- **与 OpenSpec 对比**:OpenSpec 擅长 0→1 功能规范。incspec 擅长理解和修改现有前端代码库(1→n),特别是 API 流程和状态管理复杂的场景。
|
|
36
36
|
|
|
@@ -38,6 +38,10 @@ AI 编程助手在处理复杂前端代码库时常常力不从心,因为 API
|
|
|
38
38
|
|
|
39
39
|
## 工作原理
|
|
40
40
|
|
|
41
|
+
### 完整模式 (7步)
|
|
42
|
+
|
|
43
|
+
适用于复杂功能开发,需要完整的 UI 依赖分析和增量设计。
|
|
44
|
+
|
|
41
45
|
```
|
|
42
46
|
┌────────────────────┐
|
|
43
47
|
│ 1. 分析 │ 分析现有代码流程
|
|
@@ -91,6 +95,48 @@ AI 编程助手在处理复杂前端代码库时常常力不从心,因为 API
|
|
|
91
95
|
└───────────┘
|
|
92
96
|
```
|
|
93
97
|
|
|
98
|
+
### 快速模式 (5步)
|
|
99
|
+
|
|
100
|
+
适用于 Bug 修复、简单功能、不涉及复杂 UI 依赖的变更。
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
┌────────────────────┐
|
|
104
|
+
│ 1. 分析 │ 分析现有代码流程
|
|
105
|
+
│ 代码流程 │ 生成基线快照
|
|
106
|
+
└────────┬───────────┘
|
|
107
|
+
│
|
|
108
|
+
▼
|
|
109
|
+
┌────────────────────┐
|
|
110
|
+
│ 2. 收集 │ 捕获结构化需求
|
|
111
|
+
│ 需求 │ 5 列表格格式
|
|
112
|
+
└────────┬───────────┘
|
|
113
|
+
│
|
|
114
|
+
│ (跳过步骤 3 UI依赖采集)
|
|
115
|
+
│ (跳过步骤 4 增量设计)
|
|
116
|
+
│
|
|
117
|
+
▼
|
|
118
|
+
┌────────────────────┐
|
|
119
|
+
│ 5. 应用 │ 基于需求文档
|
|
120
|
+
│ 变更 │ 直接实现代码变更
|
|
121
|
+
└────────┬───────────┘
|
|
122
|
+
│
|
|
123
|
+
▼
|
|
124
|
+
┌────────────────────┐
|
|
125
|
+
│ 6. 合并 │ 重新分析代码
|
|
126
|
+
│ 到基线 │ 生成新基线
|
|
127
|
+
└────────┬───────────┘
|
|
128
|
+
│
|
|
129
|
+
▼
|
|
130
|
+
┌────────────────────┐
|
|
131
|
+
│ 7. 归档 │ 归档本轮产出
|
|
132
|
+
│ 产出 │ 清空工作区
|
|
133
|
+
└────────────────────┘
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**模式选择建议:**
|
|
137
|
+
- **完整模式**: 复杂 UI 功能、多组件交互、需要详细设计审查
|
|
138
|
+
- **快速模式**: Bug 修复、简单功能、不涉及 UI 依赖变更
|
|
139
|
+
|
|
94
140
|
> **提示**: 如果已有基准报告,可使用 `incspec analyze --baseline=<file>` 直接跳过分析步骤,快速进入后续工作流。
|
|
95
141
|
|
|
96
142
|
## 快速开始
|
|
@@ -197,6 +243,14 @@ $ incspec status # 检查当前工作流状态
|
|
|
197
243
|
$ incspec analyze src/views/Home --complete # 标记步骤 1 完成
|
|
198
244
|
```
|
|
199
245
|
|
|
246
|
+
**启动快速模式** (5步流程):
|
|
247
|
+
|
|
248
|
+
适用于 Bug 修复或简单功能变更:
|
|
249
|
+
```bash
|
|
250
|
+
$ incspec analyze src/views/Home --quick # 启动快速模式工作流
|
|
251
|
+
```
|
|
252
|
+
快速模式跳过步骤 3 (UI依赖采集) 和步骤 4 (增量设计),直接从需求进入代码应用。
|
|
253
|
+
|
|
200
254
|
**使用现有基准报告** (跳过分析):
|
|
201
255
|
|
|
202
256
|
如果已有基准报告文件,可直接使用,跳过分析步骤:
|
|
@@ -275,7 +329,7 @@ AI:我将增量合并到新基线。
|
|
|
275
329
|
|
|
276
330
|
### 7. 归档已完成的工作
|
|
277
331
|
|
|
278
|
-
|
|
332
|
+
完成步骤 1-6 后,使用归档命令保存本轮产出:
|
|
279
333
|
|
|
280
334
|
```text
|
|
281
335
|
你:归档本轮工作流产出
|
|
@@ -304,7 +358,7 @@ $ incspec archive -y # 归档所有工作流产出,跳过确认
|
|
|
304
358
|
|
|
305
359
|
在以下情况下执行归档:
|
|
306
360
|
|
|
307
|
-
1.
|
|
361
|
+
1. **步骤 1-6 全部完成** - 代码已应用,新基线已生成
|
|
308
362
|
2. **功能已验证通过** - 确认变更符合预期
|
|
309
363
|
3. **准备开始下一轮迭代** - 需要清空当前工作区
|
|
310
364
|
|
|
@@ -352,7 +406,7 @@ incspec/
|
|
|
352
406
|
|
|
353
407
|
```
|
|
354
408
|
┌────────────────────┐
|
|
355
|
-
│ 6
|
|
409
|
+
│ 步骤 1-6 完成 │
|
|
356
410
|
│ 新基线已生成 │
|
|
357
411
|
└────────┬───────────┘
|
|
358
412
|
│
|
|
@@ -441,8 +495,9 @@ incspec help <command> # 显示特定命令帮助
|
|
|
441
495
|
|
|
442
496
|
```bash
|
|
443
497
|
# 步骤 1:分析代码流程
|
|
444
|
-
incspec analyze <source-path> [--module=name]
|
|
498
|
+
incspec analyze <source-path> [--module=name] [--quick] # 别名:a
|
|
445
499
|
incspec a src/views/Home --module=home
|
|
500
|
+
incspec analyze src/views/Home --quick # 启动快速模式
|
|
446
501
|
incspec analyze src/views/Home --complete -o baselines/home-baseline-v1.md
|
|
447
502
|
incspec analyze --baseline=home-baseline-v1.md # 使用现有基准(自动从归档恢复)
|
|
448
503
|
|
|
@@ -450,21 +505,23 @@ incspec analyze --baseline=home-baseline-v1.md # 使用现有基准(自动从
|
|
|
450
505
|
incspec collect-req # 别名:cr
|
|
451
506
|
incspec cr --complete
|
|
452
507
|
|
|
453
|
-
# 步骤 3:收集 UI
|
|
508
|
+
# 步骤 3:收集 UI 依赖(快速模式跳过)
|
|
454
509
|
incspec collect-dep # 别名:cd
|
|
455
510
|
incspec cd --complete
|
|
456
511
|
|
|
457
|
-
# 步骤 4
|
|
512
|
+
# 步骤 4:生成增量设计(快速模式跳过)
|
|
458
513
|
incspec design [--feature=name] # 别名:d
|
|
459
514
|
incspec d --feature=user-auth --complete -o increments/auth-increment-v1.md
|
|
460
515
|
|
|
461
516
|
# 步骤 5:应用代码变更
|
|
462
517
|
incspec apply [increment-path] # 别名:ap
|
|
463
518
|
incspec ap --source-dir=src/ --complete
|
|
519
|
+
# 快速模式下自动使用 requirements/structured-requirements.md 作为输入
|
|
464
520
|
|
|
465
521
|
# 步骤 6:合并到基线
|
|
466
522
|
incspec merge [increment-path] # 别名:m
|
|
467
523
|
incspec m --complete -o baselines/home-baseline-v2.md
|
|
524
|
+
# 快速模式下重新分析代码生成新基线
|
|
468
525
|
```
|
|
469
526
|
|
|
470
527
|
</details>
|
|
@@ -517,7 +574,7 @@ incspec archive -y # 跳过确认提示
|
|
|
517
574
|
|
|
518
575
|
| 特性 | incspec | OpenSpec |
|
|
519
576
|
|------|---------|----------|
|
|
520
|
-
| 工作流 |
|
|
577
|
+
| 工作流 | 7 步增量循环 | 3 阶段(proposal → apply → archive) |
|
|
521
578
|
| 侧重点 | 前端 API 流程分析 | 通用功能规范 |
|
|
522
579
|
| 编号系统 | S/D/N/C/R 多层编号 | 单一编号 |
|
|
523
580
|
| 代码生成 | 集成应用步骤 | 需手动编码 |
|
package/commands/analyze.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
startWorkflow,
|
|
15
15
|
updateStep,
|
|
16
16
|
STATUS,
|
|
17
|
+
MODE,
|
|
17
18
|
getStepInfo,
|
|
18
19
|
isWorkflowIncomplete,
|
|
19
20
|
getWorkflowProgress,
|
|
@@ -123,10 +124,12 @@ export async function analyzeCommand(ctx) {
|
|
|
123
124
|
|
|
124
125
|
// Handle workflow state
|
|
125
126
|
let workflow = readWorkflow(projectRoot);
|
|
127
|
+
const isQuick = options.quick || options.q;
|
|
126
128
|
if (!workflow?.currentWorkflow) {
|
|
127
129
|
const workflowName = typeof options.workflow === 'string' ? options.workflow : `analyze-${moduleName}`;
|
|
128
|
-
workflow = startWorkflow(projectRoot, workflowName);
|
|
129
|
-
|
|
130
|
+
workflow = startWorkflow(projectRoot, workflowName, { mode: isQuick ? MODE.QUICK : MODE.FULL });
|
|
131
|
+
const modeLabel = isQuick ? '(快速模式: 5步)' : '';
|
|
132
|
+
printSuccess(`已创建新工作流: ${workflowName} ${modeLabel}`);
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
// Mark step as completed
|
|
@@ -151,15 +154,18 @@ export async function analyzeCommand(ctx) {
|
|
|
151
154
|
|
|
152
155
|
// Get workflow state
|
|
153
156
|
let workflow = readWorkflow(projectRoot);
|
|
157
|
+
const isQuick = options.quick || options.q;
|
|
154
158
|
|
|
155
159
|
// Check if starting new workflow
|
|
156
160
|
if (!workflow?.currentWorkflow) {
|
|
157
161
|
let workflowName = typeof options.workflow === 'string' ? options.workflow : '';
|
|
158
162
|
if (!workflowName) {
|
|
159
|
-
|
|
163
|
+
const modeHint = isQuick ? '(快速模式)' : '';
|
|
164
|
+
workflowName = await prompt(`请输入工作流名称 ${modeHint}`, `analyze-${moduleName}`);
|
|
160
165
|
}
|
|
161
|
-
workflow = startWorkflow(projectRoot, workflowName);
|
|
162
|
-
|
|
166
|
+
workflow = startWorkflow(projectRoot, workflowName, { mode: isQuick ? MODE.QUICK : MODE.FULL });
|
|
167
|
+
const modeLabel = isQuick ? '(快速模式: 5步)' : '';
|
|
168
|
+
printSuccess(`已创建新工作流: ${workflowName} ${modeLabel}`);
|
|
163
169
|
} else if (isWorkflowIncomplete(workflow)) {
|
|
164
170
|
// Current workflow is incomplete, ask for confirmation
|
|
165
171
|
const progress = getWorkflowProgress(workflow);
|
|
@@ -167,19 +173,21 @@ export async function analyzeCommand(ctx) {
|
|
|
167
173
|
printWarning(`当前工作流 "${workflow.currentWorkflow}" 未完成 (${progress.completed}/${progress.total})`);
|
|
168
174
|
print(colorize(` 最后完成步骤: ${progress.lastCompletedStep || '无'}`, colors.dim));
|
|
169
175
|
print('');
|
|
170
|
-
|
|
176
|
+
|
|
171
177
|
const shouldArchive = await confirm('是否归档当前工作流并开始新工作流?');
|
|
172
|
-
|
|
178
|
+
|
|
173
179
|
if (!shouldArchive) {
|
|
174
180
|
printInfo('已取消。继续当前工作流。');
|
|
175
181
|
print('');
|
|
176
182
|
} else {
|
|
177
183
|
let workflowName = typeof options.workflow === 'string' ? options.workflow : '';
|
|
178
184
|
if (!workflowName) {
|
|
179
|
-
|
|
185
|
+
const modeHint = isQuick ? '(快速模式)' : '';
|
|
186
|
+
workflowName = await prompt(`请输入新工作流名称 ${modeHint}`, `analyze-${moduleName}`);
|
|
180
187
|
}
|
|
181
|
-
workflow = startWorkflow(projectRoot, workflowName);
|
|
182
|
-
|
|
188
|
+
workflow = startWorkflow(projectRoot, workflowName, { mode: isQuick ? MODE.QUICK : MODE.FULL });
|
|
189
|
+
const modeLabel = isQuick ? '(快速模式: 5步)' : '';
|
|
190
|
+
printSuccess(`已归档旧工作流,创建新工作流: ${workflowName} ${modeLabel}`);
|
|
183
191
|
}
|
|
184
192
|
}
|
|
185
193
|
|
|
@@ -192,6 +200,14 @@ export async function analyzeCommand(ctx) {
|
|
|
192
200
|
print(colorize('步骤 1: 代码流程分析', colors.bold, colors.cyan));
|
|
193
201
|
print(colorize('─────────────────────', colors.dim));
|
|
194
202
|
print('');
|
|
203
|
+
|
|
204
|
+
// Show quick mode info
|
|
205
|
+
if (isQuick) {
|
|
206
|
+
print(colorize('快速模式流程: 分析 -> 需求收集 -> 应用代码 -> 合并基线', colors.yellow));
|
|
207
|
+
print(colorize('(跳过 UI依赖采集 和 增量设计)', colors.dim));
|
|
208
|
+
print('');
|
|
209
|
+
}
|
|
210
|
+
|
|
195
211
|
print(colorize('配置:', colors.bold));
|
|
196
212
|
print(colorize(` 源代码路径: ${sourcePath}`, colors.dim));
|
|
197
213
|
print(colorize(` 模块名称: ${moduleName}`, colors.dim));
|
package/commands/apply.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs';
|
|
6
7
|
import {
|
|
7
8
|
ensureInitialized,
|
|
8
9
|
INCSPEC_DIR,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
readWorkflow,
|
|
14
15
|
updateStep,
|
|
15
16
|
STATUS,
|
|
17
|
+
isQuickMode,
|
|
16
18
|
} from '../lib/workflow.mjs';
|
|
17
19
|
import { listSpecs } from '../lib/spec.mjs';
|
|
18
20
|
import {
|
|
@@ -45,24 +47,7 @@ export async function applyCommand(ctx) {
|
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
let incrementPath = args[0];
|
|
50
|
-
if (!incrementPath) {
|
|
51
|
-
const increments = listSpecs(projectRoot, 'increments');
|
|
52
|
-
if (increments.length > 0) {
|
|
53
|
-
const featureName = workflow.currentWorkflow.replace(/^analyze-/, '');
|
|
54
|
-
const matched = increments.find(spec => spec.name.startsWith(`${featureName}-increment-`));
|
|
55
|
-
if (matched) {
|
|
56
|
-
incrementPath = matched.path;
|
|
57
|
-
} else {
|
|
58
|
-
incrementPath = increments[0].path;
|
|
59
|
-
printWarning(`未找到与当前工作流匹配的增量文件,已使用最近文件: ${increments[0].name}`);
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
printWarning('未找到增量设计文件。请先运行步骤 4 (design)。');
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
50
|
+
const quickMode = isQuickMode(workflow);
|
|
66
51
|
|
|
67
52
|
// Get source directory
|
|
68
53
|
const config = readProjectConfig(projectRoot);
|
|
@@ -70,12 +55,52 @@ export async function applyCommand(ctx) {
|
|
|
70
55
|
? options['source-dir']
|
|
71
56
|
: (config?.source_dir || 'src');
|
|
72
57
|
|
|
58
|
+
// Determine input based on mode
|
|
59
|
+
let inputPath;
|
|
60
|
+
let inputType;
|
|
61
|
+
|
|
62
|
+
if (quickMode) {
|
|
63
|
+
// Quick mode: use requirements document
|
|
64
|
+
const reqFile = path.join(projectRoot, INCSPEC_DIR, DIRS.requirements, 'structured-requirements.md');
|
|
65
|
+
if (!fs.existsSync(reqFile)) {
|
|
66
|
+
printWarning('未找到需求文件。请先运行步骤 2 (collect-req)。');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
inputPath = path.join(INCSPEC_DIR, DIRS.requirements, 'structured-requirements.md');
|
|
70
|
+
inputType = 'requirements';
|
|
71
|
+
} else {
|
|
72
|
+
// Full mode: use increment design file
|
|
73
|
+
let incrementPath = args[0];
|
|
74
|
+
if (!incrementPath) {
|
|
75
|
+
const increments = listSpecs(projectRoot, 'increments');
|
|
76
|
+
if (increments.length > 0) {
|
|
77
|
+
const featureName = workflow.currentWorkflow.replace(/^analyze-/, '');
|
|
78
|
+
const matched = increments.find(spec => spec.name.startsWith(`${featureName}-increment-`));
|
|
79
|
+
if (matched) {
|
|
80
|
+
incrementPath = matched.path;
|
|
81
|
+
} else {
|
|
82
|
+
incrementPath = increments[0].path;
|
|
83
|
+
printWarning(`未找到与当前工作流匹配的增量文件,已使用最近文件: ${increments[0].name}`);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
printWarning('未找到增量设计文件。请先运行步骤 4 (design)。');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
inputPath = incrementPath;
|
|
91
|
+
inputType = 'increment';
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
print('');
|
|
74
95
|
print(colorize('步骤 5: 应用代码变更', colors.bold, colors.cyan));
|
|
96
|
+
if (quickMode) {
|
|
97
|
+
print(colorize('(快速模式 - 基于需求文档)', colors.yellow));
|
|
98
|
+
}
|
|
75
99
|
print(colorize('────────────────────', colors.dim));
|
|
76
100
|
print('');
|
|
77
101
|
print(colorize(`当前工作流: ${workflow.currentWorkflow}`, colors.dim));
|
|
78
|
-
print(colorize(
|
|
102
|
+
print(colorize(`输入文件: ${inputPath}`, colors.dim));
|
|
103
|
+
print(colorize(`输入类型: ${inputType === 'requirements' ? '结构化需求' : '增量设计'}`, colors.dim));
|
|
79
104
|
print(colorize(`源代码目录: ${sourceDir}`, colors.dim));
|
|
80
105
|
print('');
|
|
81
106
|
|
|
@@ -84,21 +109,41 @@ export async function applyCommand(ctx) {
|
|
|
84
109
|
|
|
85
110
|
print(colorize('使用说明:', colors.bold));
|
|
86
111
|
print('');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
|
|
113
|
+
if (quickMode) {
|
|
114
|
+
// Quick mode instructions
|
|
115
|
+
print(colorize('快速模式下,请直接根据需求文档实现代码变更:', colors.cyan));
|
|
116
|
+
print('');
|
|
117
|
+
print(colorize('在 Cursor 中:', colors.dim));
|
|
118
|
+
print(colorize(` /incspec/inc-apply ${inputPath}`, colors.bold, colors.white));
|
|
119
|
+
print('');
|
|
120
|
+
print(colorize('在 Claude Code 中:', colors.dim));
|
|
121
|
+
print(colorize(` 请根据 ${inputPath} 中的需求描述,直接实现代码变更`, colors.dim));
|
|
122
|
+
print('');
|
|
123
|
+
print(colorize('该命令将:', colors.dim));
|
|
124
|
+
print(colorize(' 1. 解析需求文档中的功能描述', colors.dim));
|
|
125
|
+
print(colorize(' 2. 基于现有基线分析影响范围', colors.dim));
|
|
126
|
+
print(colorize(' 3. 直接执行代码新建/修改/删除', colors.dim));
|
|
127
|
+
print(colorize(' 4. 输出变更摘要', colors.dim));
|
|
128
|
+
} else {
|
|
129
|
+
// Full mode instructions
|
|
130
|
+
print(colorize('请在 Cursor 中运行以下命令:', colors.cyan));
|
|
131
|
+
print('');
|
|
132
|
+
print(colorize(` /incspec/inc-apply ${inputPath}`, colors.bold, colors.white));
|
|
133
|
+
print('');
|
|
134
|
+
print(colorize('或在 Claude Code 中使用 inc-spec-skill 技能:', colors.cyan));
|
|
135
|
+
print('');
|
|
136
|
+
print(colorize(` 请按照 ${inputPath} 的增量设计,应用代码变更到 ${path.join(projectRoot, sourceDir)}`, colors.dim));
|
|
137
|
+
print('');
|
|
138
|
+
print(colorize('该命令将:', colors.dim));
|
|
139
|
+
print(colorize(' 1. 解析增量设计文件中的变更计划', colors.dim));
|
|
140
|
+
print(colorize(' 2. 按依赖顺序排序变更', colors.dim));
|
|
141
|
+
print(colorize(' 3. 执行代码新建/修改/删除', colors.dim));
|
|
142
|
+
print(colorize(' 4. 输出变更摘要', colors.dim));
|
|
143
|
+
}
|
|
144
|
+
|
|
100
145
|
print('');
|
|
101
|
-
printWarning('
|
|
146
|
+
printWarning('请在执行前仔细审查输入文件!');
|
|
102
147
|
print('');
|
|
103
148
|
printInfo(`完成后运行 'incspec status' 查看进度`);
|
|
104
149
|
print('');
|
package/commands/archive.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
DIRS,
|
|
11
11
|
} from '../lib/config.mjs';
|
|
12
12
|
import { archiveSpec, getSpecInfo } from '../lib/spec.mjs';
|
|
13
|
-
import { archiveWorkflow, readWorkflow, STATUS } from '../lib/workflow.mjs';
|
|
13
|
+
import { archiveWorkflow, readWorkflow, updateStep, STATUS, isQuickMode, MODE } from '../lib/workflow.mjs';
|
|
14
14
|
import {
|
|
15
15
|
colors,
|
|
16
16
|
colorize,
|
|
@@ -21,7 +21,24 @@ import {
|
|
|
21
21
|
confirm,
|
|
22
22
|
} from '../lib/terminal.mjs';
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
// Full mode: archivable steps are 1, 2, 3, 4, 6 (0-based: 0, 1, 2, 3, 5)
|
|
25
|
+
// Note: Step 5 (apply) has no file output, Step 7 (archive) is this command itself
|
|
26
|
+
const FULL_MODE_ARCHIVABLE_INDEXES = [0, 1, 2, 3, 5];
|
|
27
|
+
// Quick mode: archivable steps are 1, 2, 6 (0-based: 0, 1, 5)
|
|
28
|
+
// Note: Steps 3, 4 are skipped in quick mode
|
|
29
|
+
const QUICK_MODE_ARCHIVABLE_INDEXES = [0, 1, 5];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get archivable step indexes based on workflow mode
|
|
33
|
+
* @param {Object} workflow
|
|
34
|
+
* @returns {number[]}
|
|
35
|
+
*/
|
|
36
|
+
function getArchivableStepIndexes(workflow) {
|
|
37
|
+
if (isQuickMode(workflow)) {
|
|
38
|
+
return QUICK_MODE_ARCHIVABLE_INDEXES;
|
|
39
|
+
}
|
|
40
|
+
return FULL_MODE_ARCHIVABLE_INDEXES;
|
|
41
|
+
}
|
|
25
42
|
|
|
26
43
|
function escapeRegExp(value) {
|
|
27
44
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -114,8 +131,10 @@ function getArchivableOutputs(workflow) {
|
|
|
114
131
|
return null;
|
|
115
132
|
}
|
|
116
133
|
|
|
134
|
+
const archivableIndexes = getArchivableStepIndexes(workflow);
|
|
117
135
|
const outputs = [];
|
|
118
|
-
|
|
136
|
+
|
|
137
|
+
for (const index of archivableIndexes) {
|
|
119
138
|
const step = workflow.steps[index];
|
|
120
139
|
if (!step || step.status !== STATUS.COMPLETED || !step.output || step.output === '-') {
|
|
121
140
|
return null;
|
|
@@ -260,6 +279,9 @@ export async function archiveCommand(ctx) {
|
|
|
260
279
|
printSuccess(`文件已${keepOriginal ? '复制' : '移动'}到: ${archivePath}`);
|
|
261
280
|
}
|
|
262
281
|
|
|
282
|
+
// Mark step 7 as completed
|
|
283
|
+
updateStep(projectRoot, 7, STATUS.COMPLETED);
|
|
284
|
+
|
|
263
285
|
const refreshed = readWorkflow(projectRoot);
|
|
264
286
|
if (refreshed?.currentWorkflow && shouldArchiveWorkflow(projectRoot, refreshed)) {
|
|
265
287
|
const workflowName = refreshed.currentWorkflow;
|
package/commands/collect-req.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
readWorkflow,
|
|
13
13
|
updateStep,
|
|
14
14
|
STATUS,
|
|
15
|
+
isQuickMode,
|
|
15
16
|
} from '../lib/workflow.mjs';
|
|
16
17
|
import {
|
|
17
18
|
colors,
|
|
@@ -76,5 +77,12 @@ export async function collectReqCommand(ctx) {
|
|
|
76
77
|
if (options.complete) {
|
|
77
78
|
updateStep(projectRoot, STEP_NUMBER, STATUS.COMPLETED, OUTPUT_FILE);
|
|
78
79
|
printSuccess(`步骤 2 已标记为完成: ${OUTPUT_FILE}`);
|
|
80
|
+
|
|
81
|
+
// Quick mode hint
|
|
82
|
+
if (isQuickMode(workflow)) {
|
|
83
|
+
print('');
|
|
84
|
+
printInfo('快速模式: 跳过步骤 3、4,直接进入步骤 5');
|
|
85
|
+
print(colorize(" 运行 'incspec apply' 继续", colors.cyan));
|
|
86
|
+
}
|
|
79
87
|
}
|
|
80
88
|
}
|
package/commands/help.mjs
CHANGED
|
@@ -34,12 +34,13 @@ const COMMANDS = {
|
|
|
34
34
|
description: '显示当前工作流状态',
|
|
35
35
|
},
|
|
36
36
|
analyze: {
|
|
37
|
-
usage: 'incspec analyze <source-path> [--module=name] [--baseline=file]',
|
|
37
|
+
usage: 'incspec analyze <source-path> [--module=name] [--quick] [--baseline=file]',
|
|
38
38
|
aliases: ['a'],
|
|
39
39
|
description: '步骤1: 分析代码流程,生成基线快照',
|
|
40
40
|
options: [
|
|
41
41
|
['-m, --module=<name>', '指定模块名称'],
|
|
42
42
|
['-w, --workflow=<name>', '指定工作流名称(避免交互提示)'],
|
|
43
|
+
['-q, --quick', '启动快速模式(5步流程: 分析->需求->应用->合并->归档)'],
|
|
43
44
|
['-b, --baseline=<file>', '使用现有基准报告(自动从归档恢复)'],
|
|
44
45
|
['--complete', '标记步骤完成'],
|
|
45
46
|
['-o, --output=<file>', '完成时指定输出文件'],
|
|
@@ -110,13 +111,18 @@ const COMMANDS = {
|
|
|
110
111
|
archive: {
|
|
111
112
|
usage: 'incspec archive [file-path] [--workflow] [-k|--keep] [-y|--yes]',
|
|
112
113
|
aliases: ['ar'],
|
|
113
|
-
description: '
|
|
114
|
+
description: '步骤7: 归档工作流产出 (或指定文件)',
|
|
114
115
|
options: [
|
|
115
116
|
['--workflow', '归档当前工作流全部产出文件'],
|
|
116
117
|
['-k, --keep', '复制而非移动'],
|
|
117
118
|
['-y, --yes', '跳过确认提示'],
|
|
118
119
|
],
|
|
119
120
|
},
|
|
121
|
+
reset: {
|
|
122
|
+
usage: 'incspec reset',
|
|
123
|
+
aliases: ['rs'],
|
|
124
|
+
description: '重置当前工作流,归档已有产出',
|
|
125
|
+
},
|
|
120
126
|
sync: {
|
|
121
127
|
usage: 'incspec sync [--cursor] [--claude] [--all] [--project|--global]',
|
|
122
128
|
aliases: ['s'],
|
|
@@ -183,7 +189,7 @@ function showGeneralHelp() {
|
|
|
183
189
|
print(colorize('管理命令:', colors.bold));
|
|
184
190
|
print('');
|
|
185
191
|
|
|
186
|
-
const mgmtCommands = ['init', 'update', 'status', 'list', 'validate', 'archive', 'sync', 'help'];
|
|
192
|
+
const mgmtCommands = ['init', 'update', 'status', 'list', 'validate', 'archive', 'reset', 'sync', 'help'];
|
|
187
193
|
mgmtCommands.forEach(cmd => {
|
|
188
194
|
const def = COMMANDS[cmd];
|
|
189
195
|
const aliases = def.aliases ? colorize(` (${def.aliases.join(', ')})`, colors.dim) : '';
|
package/commands/merge.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
readWorkflow,
|
|
13
13
|
updateStep,
|
|
14
14
|
STATUS,
|
|
15
|
+
isQuickMode,
|
|
15
16
|
} from '../lib/workflow.mjs';
|
|
16
17
|
import { listSpecs, getNextVersion } from '../lib/spec.mjs';
|
|
17
18
|
import {
|
|
@@ -43,24 +44,7 @@ export async function mergeCommand(ctx) {
|
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
let incrementPath = args[0];
|
|
48
|
-
if (!incrementPath) {
|
|
49
|
-
const increments = listSpecs(projectRoot, 'increments');
|
|
50
|
-
if (increments.length > 0) {
|
|
51
|
-
const featureName = workflow.currentWorkflow.replace(/^analyze-/, '');
|
|
52
|
-
const matched = increments.find(spec => spec.name.startsWith(`${featureName}-increment-`));
|
|
53
|
-
if (matched) {
|
|
54
|
-
incrementPath = matched.path;
|
|
55
|
-
} else {
|
|
56
|
-
incrementPath = increments[0].path;
|
|
57
|
-
printWarning(`未找到与当前工作流匹配的增量文件,已使用最近文件: ${increments[0].name}`);
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
printWarning('未找到增量设计文件。请先运行步骤 4 (design)。');
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
47
|
+
const quickMode = isQuickMode(workflow);
|
|
64
48
|
|
|
65
49
|
// Calculate output file
|
|
66
50
|
const moduleName = workflow.currentWorkflow.replace(/^analyze-/, '');
|
|
@@ -70,12 +54,39 @@ export async function mergeCommand(ctx) {
|
|
|
70
54
|
const outputFile = outputOverride || defaultOutputFile;
|
|
71
55
|
const outputPath = path.join(INCSPEC_DIR, DIRS.baselines, outputFile);
|
|
72
56
|
|
|
57
|
+
// Get increment file (only needed for full mode)
|
|
58
|
+
let incrementPath = null;
|
|
59
|
+
if (!quickMode) {
|
|
60
|
+
incrementPath = args[0];
|
|
61
|
+
if (!incrementPath) {
|
|
62
|
+
const increments = listSpecs(projectRoot, 'increments');
|
|
63
|
+
if (increments.length > 0) {
|
|
64
|
+
const featureName = workflow.currentWorkflow.replace(/^analyze-/, '');
|
|
65
|
+
const matched = increments.find(spec => spec.name.startsWith(`${featureName}-increment-`));
|
|
66
|
+
if (matched) {
|
|
67
|
+
incrementPath = matched.path;
|
|
68
|
+
} else {
|
|
69
|
+
incrementPath = increments[0].path;
|
|
70
|
+
printWarning(`未找到与当前工作流匹配的增量文件,已使用最近文件: ${increments[0].name}`);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
printWarning('未找到增量设计文件。请先运行步骤 4 (design)。');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
print('');
|
|
74
80
|
print(colorize('步骤 6: 合并到基线', colors.bold, colors.cyan));
|
|
81
|
+
if (quickMode) {
|
|
82
|
+
print(colorize('(快速模式 - 重新分析生成新基线)', colors.yellow));
|
|
83
|
+
}
|
|
75
84
|
print(colorize('─────────────────', colors.dim));
|
|
76
85
|
print('');
|
|
77
86
|
print(colorize(`当前工作流: ${workflow.currentWorkflow}`, colors.dim));
|
|
78
|
-
|
|
87
|
+
if (!quickMode) {
|
|
88
|
+
print(colorize(`增量设计文件: ${incrementPath}`, colors.dim));
|
|
89
|
+
}
|
|
79
90
|
print(colorize(`输出基线文件: ${outputPath}`, colors.dim));
|
|
80
91
|
print('');
|
|
81
92
|
|
|
@@ -84,20 +95,40 @@ export async function mergeCommand(ctx) {
|
|
|
84
95
|
|
|
85
96
|
print(colorize('使用说明:', colors.bold));
|
|
86
97
|
print('');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
|
|
99
|
+
if (quickMode) {
|
|
100
|
+
// Quick mode instructions
|
|
101
|
+
print(colorize('快速模式下,将重新分析当前代码生成新基线:', colors.cyan));
|
|
102
|
+
print('');
|
|
103
|
+
print(colorize('在 Cursor 中:', colors.dim));
|
|
104
|
+
print(colorize(` /incspec/inc-merge --output=${outputFile}`, colors.bold, colors.white));
|
|
105
|
+
print('');
|
|
106
|
+
print(colorize('在 Claude Code 中:', colors.dim));
|
|
107
|
+
print(colorize(` 请分析当前代码状态,生成新的基线报告到 ${outputPath}`, colors.dim));
|
|
108
|
+
print('');
|
|
109
|
+
print(colorize('该命令将:', colors.dim));
|
|
110
|
+
print(colorize(' 1. 分析当前代码的完整流程', colors.dim));
|
|
111
|
+
print(colorize(' 2. 生成 API 调用时序图', colors.dim));
|
|
112
|
+
print(colorize(' 3. 生成依赖关系图', colors.dim));
|
|
113
|
+
print(colorize(' 4. 保存为新版本基线快照', colors.dim));
|
|
114
|
+
} else {
|
|
115
|
+
// Full mode instructions
|
|
116
|
+
print(colorize('请在 Cursor 中运行以下命令:', colors.cyan));
|
|
117
|
+
print('');
|
|
118
|
+
print(colorize(` /incspec/inc-merge ${incrementPath}`, colors.bold, colors.white));
|
|
119
|
+
print('');
|
|
120
|
+
print(colorize('或在 Claude Code 中使用 inc-spec-skill 技能:', colors.cyan));
|
|
121
|
+
print('');
|
|
122
|
+
const outDir = path.join(projectRoot, INCSPEC_DIR, DIRS.baselines);
|
|
123
|
+
print(colorize(` 请将 ${incrementPath} 的增量合并到基线 ${outDir}`, colors.dim));
|
|
124
|
+
print('');
|
|
125
|
+
print(colorize('该命令将:', colors.dim));
|
|
126
|
+
print(colorize(' 1. 解析增量设计文件中的时序图和依赖图', colors.dim));
|
|
127
|
+
print(colorize(' 2. 清理增量标记', colors.dim));
|
|
128
|
+
print(colorize(' 3. 重新编号为 S1-Sxx, D1-Dxx', colors.dim));
|
|
129
|
+
print(colorize(' 4. 生成新的基线快照', colors.dim));
|
|
130
|
+
}
|
|
131
|
+
|
|
101
132
|
print('');
|
|
102
133
|
print(colorize('新基线将作为下一轮增量开发的起点。', colors.dim));
|
|
103
134
|
print('');
|