@localsummer/incspec 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,7 @@ import {
15
15
  updateStep,
16
16
  STATUS,
17
17
  isQuickMode,
18
+ getMissingPrereqs,
18
19
  } from '../lib/workflow.mjs';
19
20
  import { listSpecs } from '../lib/spec.mjs';
20
21
  import {
@@ -29,6 +30,20 @@ import {
29
30
 
30
31
  const STEP_NUMBER = 5;
31
32
 
33
+ function resolveIncrementPath(projectRoot, candidate) {
34
+ if (!candidate || typeof candidate !== 'string') {
35
+ return null;
36
+ }
37
+
38
+ const possiblePaths = [
39
+ candidate,
40
+ path.join(projectRoot, candidate),
41
+ path.join(projectRoot, INCSPEC_DIR, DIRS.increments, candidate),
42
+ ];
43
+
44
+ return possiblePaths.find(p => fs.existsSync(p)) || null;
45
+ }
46
+
32
47
  /**
33
48
  * Execute apply command
34
49
  * @param {Object} ctx - Command context
@@ -48,6 +63,12 @@ export async function applyCommand(ctx) {
48
63
  }
49
64
 
50
65
  const quickMode = isQuickMode(workflow);
66
+ const missingSteps = getMissingPrereqs(workflow, STEP_NUMBER);
67
+ if (missingSteps && missingSteps.length > 0 && !options.force) {
68
+ printWarning(`请先完成步骤 ${missingSteps.join(', ')} 后再继续。`);
69
+ printInfo('如需强制执行,请添加 --force。');
70
+ return;
71
+ }
51
72
 
52
73
  // Get source directory
53
74
  const config = readProjectConfig(projectRoot);
@@ -86,6 +107,13 @@ export async function applyCommand(ctx) {
86
107
  printWarning('未找到增量设计文件。请先运行步骤 4 (design)。');
87
108
  return;
88
109
  }
110
+ } else {
111
+ const resolved = resolveIncrementPath(projectRoot, incrementPath);
112
+ if (!resolved) {
113
+ printWarning(`增量设计文件不存在: ${incrementPath}`);
114
+ return;
115
+ }
116
+ incrementPath = resolved;
89
117
  }
90
118
  inputPath = incrementPath;
91
119
  inputType = 'increment';
@@ -12,6 +12,8 @@ import {
12
12
  readWorkflow,
13
13
  updateStep,
14
14
  STATUS,
15
+ isStepAllowed,
16
+ getMissingPrereqs,
15
17
  } from '../lib/workflow.mjs';
16
18
  import {
17
19
  colors,
@@ -43,6 +45,18 @@ export async function collectDepCommand(ctx) {
43
45
  return;
44
46
  }
45
47
 
48
+ if (!isStepAllowed(STEP_NUMBER, workflow.mode)) {
49
+ printWarning('当前工作流为快速模式,步骤 3 已跳过。');
50
+ return;
51
+ }
52
+
53
+ const missingSteps = getMissingPrereqs(workflow, STEP_NUMBER);
54
+ if (missingSteps && missingSteps.length > 0 && !options.force) {
55
+ printWarning(`请先完成步骤 ${missingSteps.join(', ')} 后再继续。`);
56
+ printInfo('如需强制执行,请添加 --force。');
57
+ return;
58
+ }
59
+
46
60
  const outputPath = path.join(INCSPEC_DIR, DIRS.requirements, OUTPUT_FILE);
47
61
 
48
62
  print('');
@@ -13,6 +13,7 @@ import {
13
13
  updateStep,
14
14
  STATUS,
15
15
  isQuickMode,
16
+ getMissingPrereqs,
16
17
  } from '../lib/workflow.mjs';
17
18
  import {
18
19
  colors,
@@ -44,6 +45,13 @@ export async function collectReqCommand(ctx) {
44
45
  return;
45
46
  }
46
47
 
48
+ const missingSteps = getMissingPrereqs(workflow, STEP_NUMBER);
49
+ if (missingSteps && missingSteps.length > 0 && !options.force) {
50
+ printWarning(`请先完成步骤 ${missingSteps.join(', ')} 后再继续。`);
51
+ printInfo('如需强制执行,请添加 --force。');
52
+ return;
53
+ }
54
+
47
55
  const outputPath = path.join(INCSPEC_DIR, DIRS.requirements, OUTPUT_FILE);
48
56
 
49
57
  print('');
@@ -13,6 +13,8 @@ import {
13
13
  readWorkflow,
14
14
  updateStep,
15
15
  STATUS,
16
+ isStepAllowed,
17
+ getMissingPrereqs,
16
18
  } from '../lib/workflow.mjs';
17
19
  import { getNextVersion, listSpecs } from '../lib/spec.mjs';
18
20
  import {
@@ -45,6 +47,18 @@ export async function designCommand(ctx) {
45
47
  return;
46
48
  }
47
49
 
50
+ if (!isStepAllowed(STEP_NUMBER, workflow.mode)) {
51
+ printWarning('当前工作流为快速模式,步骤 4 已跳过。');
52
+ return;
53
+ }
54
+
55
+ const missingSteps = getMissingPrereqs(workflow, STEP_NUMBER);
56
+ if (missingSteps && missingSteps.length > 0 && !options.force) {
57
+ printWarning(`请先完成步骤 ${missingSteps.join(', ')} 后再继续。`);
58
+ printInfo('如需强制执行,请添加 --force。');
59
+ return;
60
+ }
61
+
48
62
  // Get feature name
49
63
  let featureName = typeof options.feature === 'string' ? options.feature : '';
50
64
  if (!featureName) {
package/commands/help.mjs CHANGED
@@ -52,6 +52,7 @@ const COMMANDS = {
52
52
  description: '步骤2: 收集结构化需求',
53
53
  options: [
54
54
  ['--complete', '标记步骤完成'],
55
+ ['--force', '忽略前置步骤检查'],
55
56
  ],
56
57
  },
57
58
  'collect-dep': {
@@ -60,6 +61,7 @@ const COMMANDS = {
60
61
  description: '步骤3: 采集 UI 依赖',
61
62
  options: [
62
63
  ['--complete', '标记步骤完成'],
64
+ ['--force', '忽略前置步骤检查'],
63
65
  ],
64
66
  },
65
67
  design: {
@@ -70,6 +72,7 @@ const COMMANDS = {
70
72
  ['-f, --feature=<name>', '指定功能名称'],
71
73
  ['--complete', '标记步骤完成'],
72
74
  ['-o, --output=<file>', '完成时指定输出文件'],
75
+ ['--force', '忽略前置步骤检查'],
73
76
  ],
74
77
  },
75
78
  apply: {
@@ -79,6 +82,7 @@ const COMMANDS = {
79
82
  options: [
80
83
  ['-s, --source-dir=<path>', '指定源代码目录'],
81
84
  ['--complete', '标记步骤完成'],
85
+ ['--force', '忽略前置步骤检查'],
82
86
  ],
83
87
  },
84
88
  merge: {
@@ -88,6 +92,7 @@ const COMMANDS = {
88
92
  options: [
89
93
  ['--complete', '标记步骤完成'],
90
94
  ['-o, --output=<file>', '完成时指定输出文件'],
95
+ ['--force', '忽略前置步骤检查'],
91
96
  ],
92
97
  },
93
98
  list: {
@@ -145,18 +150,26 @@ const COMMANDS = {
145
150
  },
146
151
  };
147
152
 
153
+ const ALIAS_MAP = Object.entries(COMMANDS).reduce((map, [name, def]) => {
154
+ (def.aliases || []).forEach((alias) => {
155
+ map[alias] = name;
156
+ });
157
+ return map;
158
+ }, {});
159
+
148
160
  /**
149
161
  * Execute help command
150
162
  * @param {Object} ctx - Command context
151
163
  */
152
164
  export async function helpCommand(ctx = {}) {
153
165
  const { command } = ctx;
166
+ const resolvedCommand = command && !COMMANDS[command] ? ALIAS_MAP[command] : command;
154
167
 
155
168
  print('');
156
169
 
157
- if (command && COMMANDS[command]) {
170
+ if (resolvedCommand && COMMANDS[resolvedCommand]) {
158
171
  // Show specific command help
159
- showCommandHelp(command);
172
+ showCommandHelp(resolvedCommand);
160
173
  } else if (command) {
161
174
  print(colorize(`未知命令: ${command}`, colors.red));
162
175
  print('');
package/commands/list.mjs CHANGED
@@ -39,6 +39,9 @@ export async function listCommand(ctx) {
39
39
  const types = type ? [type] : ['baselines', 'requirements', 'increments'];
40
40
 
41
41
  for (const t of types) {
42
+ if (t === 'archives') {
43
+ continue;
44
+ }
42
45
  const specs = listSpecs(projectRoot, t);
43
46
 
44
47
  print(colorize(`${DIRS[t] || t}/`, colors.bold, colors.yellow));
@@ -62,7 +65,8 @@ export async function listCommand(ctx) {
62
65
  }
63
66
 
64
67
  // Show archives if requested
65
- if (type === 'archives' || options.all || options.a) {
68
+ const shouldShowArchives = type === 'archives' || options.all || options.a;
69
+ if (shouldShowArchives) {
66
70
  const archivePath = path.join(projectRoot, INCSPEC_DIR, DIRS.archives);
67
71
  print(colorize(`${DIRS.archives}/`, colors.bold, colors.yellow));
68
72
 
@@ -2,6 +2,7 @@
2
2
  * merge command - Step 6: Merge to baseline
3
3
  */
4
4
 
5
+ import * as fs from 'fs';
5
6
  import * as path from 'path';
6
7
  import {
7
8
  ensureInitialized,
@@ -13,6 +14,7 @@ import {
13
14
  updateStep,
14
15
  STATUS,
15
16
  isQuickMode,
17
+ getMissingPrereqs,
16
18
  } from '../lib/workflow.mjs';
17
19
  import { listSpecs, getNextVersion } from '../lib/spec.mjs';
18
20
  import {
@@ -26,6 +28,20 @@ import {
26
28
 
27
29
  const STEP_NUMBER = 6;
28
30
 
31
+ function resolveIncrementPath(projectRoot, candidate) {
32
+ if (!candidate || typeof candidate !== 'string') {
33
+ return null;
34
+ }
35
+
36
+ const possiblePaths = [
37
+ candidate,
38
+ path.join(projectRoot, candidate),
39
+ path.join(projectRoot, INCSPEC_DIR, DIRS.increments, candidate),
40
+ ];
41
+
42
+ return possiblePaths.find(p => fs.existsSync(p)) || null;
43
+ }
44
+
29
45
  /**
30
46
  * Execute merge command
31
47
  * @param {Object} ctx - Command context
@@ -45,6 +61,12 @@ export async function mergeCommand(ctx) {
45
61
  }
46
62
 
47
63
  const quickMode = isQuickMode(workflow);
64
+ const missingSteps = getMissingPrereqs(workflow, STEP_NUMBER);
65
+ if (missingSteps && missingSteps.length > 0 && !options.force) {
66
+ printWarning(`请先完成步骤 ${missingSteps.join(', ')} 后再继续。`);
67
+ printInfo('如需强制执行,请添加 --force。');
68
+ return;
69
+ }
48
70
 
49
71
  // Calculate output file
50
72
  const moduleName = workflow.currentWorkflow.replace(/^analyze-/, '');
@@ -73,6 +95,13 @@ export async function mergeCommand(ctx) {
73
95
  printWarning('未找到增量设计文件。请先运行步骤 4 (design)。');
74
96
  return;
75
97
  }
98
+ } else {
99
+ const resolved = resolveIncrementPath(projectRoot, incrementPath);
100
+ if (!resolved) {
101
+ printWarning(`增量设计文件不存在: ${incrementPath}`);
102
+ return;
103
+ }
104
+ incrementPath = resolved;
76
105
  }
77
106
  }
78
107
 
@@ -98,15 +98,25 @@ export async function validateCommand(ctx) {
98
98
  if (!body.includes('## 1.') && !body.includes('# ')) {
99
99
  warnings.push(`${spec.name}: 可能缺少标准章节结构`);
100
100
  }
101
- if (!body.includes('sequenceDiagram') && !body.includes('graph ')) {
102
- warnings.push(`${spec.name}: 未检测到 Mermaid 图表`);
101
+ const hasSequenceDiagram = /sequenceDiagram/.test(body);
102
+ const hasGraph = /\bgraph\s+/.test(body);
103
+ if (!hasSequenceDiagram) {
104
+ warnings.push(`${spec.name}: 未检测到 Mermaid sequenceDiagram`);
105
+ }
106
+ if (!hasGraph) {
107
+ warnings.push(`${spec.name}: 未检测到 Mermaid graph`);
103
108
  }
104
109
  }
105
110
 
106
111
  // Check for required sections in increments
107
112
  if (type === 'increments') {
108
- const requiredSections = ['模块1', '模块2', '模块3', '模块4', '模块5'];
109
- const missingSections = requiredSections.filter(s => !body.includes(s));
113
+ const requiredSections = Array.from({ length: 7 }, (_, i) => {
114
+ const num = i + 1;
115
+ return new RegExp(`^##\\s*(?:模块\\s*)?${num}(?:[.::\\s]|$)`, 'm');
116
+ });
117
+ const missingSections = requiredSections
118
+ .map((pattern, index) => (pattern.test(body) ? null : index + 1))
119
+ .filter(Boolean);
110
120
  if (missingSections.length > 0) {
111
121
  warnings.push(`${spec.name}: 可能缺少模块 ${missingSections.join(', ')}`);
112
122
  }
package/lib/cursor.mjs CHANGED
@@ -227,39 +227,6 @@ ${INCSPEC_DIR}/
227
227
  `,
228
228
  });
229
229
 
230
- // Add archive command from template
231
- const archiveSourcePath = getSourcePath('inc-archive.md');
232
- if (archiveSourcePath) {
233
- const archiveContent = fs.readFileSync(archiveSourcePath, 'utf-8');
234
- commands.push({
235
- name: 'inc-archive.md',
236
- content: archiveContent,
237
- });
238
- } else {
239
- commands.push({
240
- name: 'inc-archive.md',
241
- content: `---
242
- description: [incspec] 归档规范文件到 archives 目录
243
- ---
244
-
245
- # 归档规范文件
246
-
247
- 请运行以下命令归档规范文件:
248
-
249
- \`\`\`bash
250
- # 归档当前工作流全部产出文件(默认模式)
251
- incspec archive --yes
252
-
253
- # 归档指定文件(默认移动模式,删除原文件)
254
- incspec archive <file-path> --yes
255
-
256
- # 归档并保留原文件(复制模式)
257
- incspec archive <file-path> --keep --yes
258
- \`\`\`
259
- `,
260
- });
261
- }
262
-
263
230
  return commands;
264
231
  }
265
232
 
package/lib/spec.mjs CHANGED
@@ -13,6 +13,17 @@ function escapeRegExp(value) {
13
13
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
14
14
  }
15
15
 
16
+ function getVersionPattern(type, prefix) {
17
+ const safePrefix = escapeRegExp(prefix);
18
+ if (type === 'baselines') {
19
+ return new RegExp(`^${safePrefix}-baseline-v(\\d+)\\.md$`);
20
+ }
21
+ if (type === 'increments') {
22
+ return new RegExp(`^${safePrefix}-increment-v(\\d+)\\.md$`);
23
+ }
24
+ return new RegExp(`^${safePrefix}.*-v(\\d+)\\.md$`);
25
+ }
26
+
16
27
  function ensureUniqueArchivePath(filePath) {
17
28
  if (!fs.existsSync(filePath)) {
18
29
  return filePath;
@@ -69,8 +80,7 @@ export function listSpecs(projectRoot, type) {
69
80
  */
70
81
  export function getNextVersion(projectRoot, type, prefix) {
71
82
  const specs = listSpecs(projectRoot, type);
72
- const safePrefix = escapeRegExp(prefix);
73
- const pattern = new RegExp(`^${safePrefix}.*-v(\\d+)\\.md$`);
83
+ const pattern = getVersionPattern(type, prefix);
74
84
 
75
85
  const versions = specs
76
86
  .map(s => {
@@ -91,8 +101,7 @@ export function getNextVersion(projectRoot, type, prefix) {
91
101
  */
92
102
  export function getLatestSpec(projectRoot, type, prefix) {
93
103
  const specs = listSpecs(projectRoot, type);
94
- const safePrefix = escapeRegExp(prefix);
95
- const pattern = new RegExp(`^${safePrefix}.*-v(\\d+)\\.md$`);
104
+ const pattern = getVersionPattern(type, prefix);
96
105
 
97
106
  const versioned = specs
98
107
  .map(s => {
package/lib/terminal.mjs CHANGED
@@ -174,6 +174,12 @@ export async function prompt(message, defaultValue = '') {
174
174
  * @returns {Promise<any>}
175
175
  */
176
176
  export async function select({ message, choices }) {
177
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
178
+ const fallback = choices[0]?.value ?? null;
179
+ printWarning('当前环境不支持交互选择,已使用默认选项。');
180
+ return fallback;
181
+ }
182
+
177
183
  return new Promise((resolve) => {
178
184
  let cursor = 0;
179
185
 
@@ -249,6 +255,12 @@ export async function select({ message, choices }) {
249
255
  * @returns {Promise<any[]>}
250
256
  */
251
257
  export async function checkbox({ message, choices }) {
258
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
259
+ const selected = choices.filter(c => c.checked).map(c => c.value);
260
+ printWarning('当前环境不支持交互多选,已使用默认选项。');
261
+ return selected;
262
+ }
263
+
252
264
  return new Promise((resolve) => {
253
265
  let cursor = 0;
254
266
  const selected = choices.map(c => c.checked || false);
package/lib/workflow.mjs CHANGED
@@ -222,9 +222,6 @@ export function readWorkflow(projectRoot) {
222
222
 
223
223
  const content = fs.readFileSync(workflowPath, 'utf-8');
224
224
  const normalized = normalizeWorkflowContent(content);
225
- if (normalized.updated) {
226
- fs.writeFileSync(workflowPath, normalized.content, 'utf-8');
227
- }
228
225
  return parseWorkflow(normalized.content);
229
226
  }
230
227
 
@@ -730,3 +727,51 @@ export function getNextStep(currentStep, mode) {
730
727
  export function shouldSkipStep(stepNumber, mode) {
731
728
  return mode === MODE.QUICK && QUICK_MODE_SKIPPED.includes(stepNumber);
732
729
  }
730
+
731
+ /**
732
+ * Check if a step is allowed in the current mode
733
+ * @param {number} stepNumber - Step number (1-based)
734
+ * @param {string} mode - Workflow mode
735
+ * @returns {boolean}
736
+ */
737
+ export function isStepAllowed(stepNumber, mode = MODE.FULL) {
738
+ return !shouldSkipStep(stepNumber, mode);
739
+ }
740
+
741
+ /**
742
+ * Get prerequisite steps for a given step
743
+ * @param {number} stepNumber - Step number (1-based)
744
+ * @param {string} mode - Workflow mode
745
+ * @returns {number[]|null} Null if step is not allowed in the mode
746
+ */
747
+ export function getPrerequisiteSteps(stepNumber, mode = MODE.FULL) {
748
+ if (stepNumber <= 1) {
749
+ return [];
750
+ }
751
+
752
+ if (mode === MODE.QUICK) {
753
+ if (!QUICK_MODE_STEPS.includes(stepNumber)) {
754
+ return null;
755
+ }
756
+ return QUICK_MODE_STEPS.filter(step => step < stepNumber);
757
+ }
758
+
759
+ return Array.from({ length: stepNumber - 1 }, (_, i) => i + 1);
760
+ }
761
+
762
+ /**
763
+ * Get missing prerequisite steps for a given step
764
+ * @param {Object} workflow
765
+ * @param {number} stepNumber - Step number (1-based)
766
+ * @returns {number[]|null} Null if step is not allowed in the mode
767
+ */
768
+ export function getMissingPrereqs(workflow, stepNumber) {
769
+ const mode = workflow?.mode || MODE.FULL;
770
+ const requiredSteps = getPrerequisiteSteps(stepNumber, mode);
771
+ if (requiredSteps === null) {
772
+ return null;
773
+ }
774
+
775
+ const steps = workflow?.steps || [];
776
+ return requiredSteps.filter(step => steps[step - 1]?.status !== STATUS.COMPLETED);
777
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@localsummer/incspec",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "面向 AI 编程助手的增量规范驱动开发工具",
5
5
  "bin": {
6
6
  "incspec": "index.mjs"
@@ -10,8 +10,14 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
10
10
 
11
11
  **快速模式 (5步)**:
12
12
  - 启动: `incspec analyze <path> --quick`
13
- - 按顺序执行: analyze → collect-req → apply → merge → archive
14
- - 跳过步骤 3 (UI依赖采集) 和步骤 4 (增量设计)
13
+ - 按顺序执行:
14
+ - 步骤1: analyze (代码分析)
15
+ - 步骤2: collect-req (需求收集)
16
+ - [跳过步骤3: UI依赖采集]
17
+ - [跳过步骤4: 增量设计]
18
+ - 步骤5: apply (应用代码)
19
+ - 步骤6: merge (合并基线)
20
+ - 步骤7: archive (归档)
15
21
  - 适用于: Bug 修复、简单功能、不涉及复杂 UI 依赖的变更
16
22
 
17
23
  **核心约定**:
@@ -25,7 +31,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
25
31
 
26
32
  ```
27
33
  完整模式: [1分析] → [2需求] → [3UI依赖] → [4设计] → [5应用] → [6合并] → [7归档] → 循环
28
- 快速模式: [1分析] → [2需求] ─────────────────────→ [5应用] → [6合并] → [7归档] → 循环
34
+ 快速模式: [1分析] → [2需求] ─(跳过3,4)─→ [5应用] → [6合并] → [7归档] → 循环
29
35
  ```
30
36
 
31
37
  ### 步骤 1: 分析代码工作流
@@ -42,7 +48,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
42
48
 
43
49
  ### 步骤 2: 收集结构化需求
44
50
 
45
- **命令**: `incspec collect-req` (别名: `cr`)
51
+ **命令**: `incspec collect-req [--force]` (别名: `cr`)
46
52
 
47
53
  **目的**: 交互式需求收集,转换为 5 列格式。
48
54
 
@@ -52,7 +58,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
52
58
 
53
59
  ### 步骤 3: 收集 UI 依赖
54
60
 
55
- **命令**: `incspec collect-dep` (别名: `cd`)
61
+ **命令**: `incspec collect-dep [--force]` (别名: `cd`)
56
62
 
57
63
  **目的**: 映射新增/修改 UI 组件的所有上下文依赖。
58
64
 
@@ -62,7 +68,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
62
68
 
63
69
  ### 步骤 4: 设计增量
64
70
 
65
- **命令**: `incspec design [--feature=name]` (别名: `d`)
71
+ **命令**: `incspec design [--feature=name] [--force]` (别名: `d`)
66
72
 
67
73
  **目的**: 创建全面的增量设计蓝图。
68
74
 
@@ -81,7 +87,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
81
87
 
82
88
  ### 步骤 5: 应用代码变更
83
89
 
84
- **命令**: `incspec apply [increment-path]` (别名: `ap`)
90
+ **命令**: `incspec apply [increment-path] [--force]` (别名: `ap`)
85
91
 
86
92
  **目的**: 根据增量蓝图执行代码生成和修改。
87
93
 
@@ -91,7 +97,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
91
97
 
92
98
  ### 步骤 6: 合并到基线
93
99
 
94
- **命令**: `incspec merge [increment-path]` (别名: `m`)
100
+ **命令**: `incspec merge [increment-path] [--force]` (别名: `m`)
95
101
 
96
102
  **目的**: 将增量整合到新的基线快照中。
97
103
 
@@ -105,7 +111,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
105
111
 
106
112
  **目的**: 将已完成的工作流产出归档到 `incspec/archives/YYYY-MM/{module}/`
107
113
 
108
- **验证**: 归档前执行 `incspec validate` 确保项目健康,归档后再次验证确保文件移动正确
114
+ **验证**: 归档前执行 `incspec validate` 确保项目健康。归档操作本身会验证文件移动正确性。
109
115
 
110
116
  ## 目录结构
111
117
 
@@ -139,11 +145,11 @@ incspec list / ls [-l] [-a] # 列出规格文件
139
145
 
140
146
  # 7步工作流
141
147
  incspec analyze <path> [--quick] [--module=name] [--baseline=file] # 步骤1
142
- incspec collect-req / cr # 步骤2
143
- incspec collect-dep / cd # 步骤3 (快速模式跳过)
144
- incspec design / d [--feature=name] # 步骤4 (快速模式跳过)
145
- incspec apply / ap [path] # 步骤5
146
- incspec merge / m [path] # 步骤6
148
+ incspec collect-req / cr [--force] # 步骤2 (--force 跳过前置检查)
149
+ incspec collect-dep / cd [--force] # 步骤3 (快速模式跳过)
150
+ incspec design / d [--feature=name] [--force] # 步骤4 (快速模式跳过)
151
+ incspec apply / ap [path] [--force] # 步骤5
152
+ incspec merge / m [path] [--force] # 步骤6
147
153
  incspec archive [--yes] [--keep] # 步骤7
148
154
 
149
155
  # 验证与同步
@@ -217,10 +223,15 @@ baseline: home-baseline-v1
217
223
  |------|----------|
218
224
  | Workflow not initialized | 运行 `incspec init` |
219
225
  | No baseline found | 先完成步骤 1 (analyze) |
220
- | Previous step not completed | 运行 `incspec status` 检查进度 |
226
+ | Previous step not completed | 运行 `incspec status` 检查进度,或添加 `--force` 跳过前置检查 |
221
227
  | Validation failed | 检查文件格式和编号序列 |
222
228
  | 工作流卡住或状态异常 | `incspec reset` 完全重置,或 `incspec reset --to=N` 回退到步骤N |
223
229
 
230
+ **前置步骤检查**:
231
+ - 步骤 2-6 执行前会自动检查前置步骤是否完成
232
+ - 若前置步骤未完成,命令会提示并阻止执行
233
+ - 添加 `--force` 可跳过此检查,强制执行当前步骤
234
+
224
235
  **工作流重置**:
225
236
  - 完全重置: `incspec reset` - 归档所有产出,回到初始状态
226
237
  - 部分回退: `incspec reset --to=N` - 保留步骤1-N,重置N+1至7(示例:`--to=3` 保留1-3,重置4-7)
@@ -31,20 +31,27 @@ description: 设计优先的前端功能增量编码工作流。适用于实现
31
31
  ```
32
32
  1. 分析现有代码 → incspec analyze <path> --quick
33
33
  2. 收集需求 → incspec collect-req
34
- 3. 应用代码变更 → incspec apply (基于需求文档)
35
- 4. 合并到基线 → incspec merge (重新分析生成)
36
- 5. 归档工作流 → incspec archive --yes
34
+ [跳过 3: 依赖收集 - 简单变更无需此步]
35
+ [跳过 4: 增量设计 - 直接基于需求实现]
36
+ 5. 应用代码变更 → incspec apply (基于需求文档)
37
+ 6. 合并到基线 → incspec merge (重新分析生成)
38
+ 7. 归档工作流 → incspec archive --yes
37
39
  ```
38
40
 
39
41
  首次使用前初始化: `incspec init`
40
42
 
41
43
  ## 执行规则
42
44
 
43
- 当用户请求满足以下条件时,**直接开始执行**,无需搜索确认:
45
+ 当用户请求满足以下条件时,**立即启动工作流**,无需额外搜索或确认启动意图:
44
46
  - 明确指定了模式(完整/快速)
45
47
  - 提供了目标路径或基线文件
46
48
  - 意图清晰无歧义
47
49
 
50
+ **重要说明**:
51
+ - "立即启动工作流"指无需确认是否开始执行,但**不跳过**工作流中定义的人工确认点
52
+ - 步骤2(需求收集)和步骤4(增量设计)仍然**必须**等待用户确认后才能继续
53
+ - 步骤7(归档)建议向用户确认归档范围
54
+
48
55
  **默认行为**:
49
56
  - 未指定模式时,默认使用完整模式
50
57
  - 提供 `--baseline=<file>` 时,跳过步骤1的代码分析,直接使用已有基线
@@ -87,15 +94,17 @@ description: 设计优先的前端功能增量编码工作流。适用于实现
87
94
 
88
95
  每个步骤与 `incspec` CLI同步以跟踪工作流状态:
89
96
 
90
- | 步骤 | 开始 | 完成 |
91
- |------|------|------|
92
- | 分析 | `incspec analyze <path> --module=<m>` 或 `--baseline=<file>` | `--complete --output=<file>` |
93
- | 需求 | `incspec collect-req` | `--complete` |
94
- | 依赖 | `incspec collect-dep` | `--complete` |
95
- | 设计 | `incspec design --feature=<f>` | `--complete --output=<file>` |
96
- | 应用 | `incspec apply <report>` | `--complete` |
97
- | 合并 | `incspec merge <report>` | `--complete --output=<file>` |
98
- | 归档 | `incspec archive --yes` | - |
97
+ | 步骤 | 模式 | 开始 | 完成 |
98
+ |------|------|------|------|
99
+ | 分析 | 完整/快速 | `incspec analyze <path> --module=<m>` 或 `--baseline=<file>` | `--complete --output=<file>` |
100
+ | 需求 | 完整/快速 | `incspec collect-req` | `--complete` |
101
+ | 依赖 | 仅完整 | `incspec collect-dep` | `--complete` |
102
+ | 设计 | 仅完整 | `incspec design --feature=<f>` | `--complete --output=<file>` |
103
+ | 应用 | 完整/快速 | `incspec apply <report>` | `--complete` |
104
+ | 合并 | 完整/快速 | `incspec merge <report>` | `--complete --output=<file>` |
105
+ | 归档 | 完整/快速 | `incspec archive --yes` | - |
106
+
107
+ **前置步骤检查**: 步骤 2-6 执行前会自动检查前置步骤是否完成。若前置步骤未完成,命令会提示并阻止执行。添加 `--force` 可跳过此检查。
99
108
 
100
109
  管理命令:
101
110
  - `incspec status` - 查看工作流状态
@@ -266,6 +275,7 @@ incspec archive <file> --keep # 复制而非移动
266
275
  | 跳过基线合并 | 对当前系统状态产生混淆 | 每次增量后必须执行步骤6 |
267
276
  | 忽略归档 | 工作区杂乱,历史丢失 | 工作流完成后立即执行步骤7 |
268
277
  | 工作流状态不一致 | CLI命令执行失败 | 使用 `incspec status` 检查状态,必要时使用 `incspec reset` 修复 |
278
+ | 前置步骤未完成 | 命令被阻止执行 | 按顺序完成前置步骤,或添加 `--force` 强制执行 |
269
279
 
270
280
  ## 参考资源
271
281
 
@@ -281,7 +291,7 @@ incspec archive <file> --keep # 复制而非移动
281
291
 
282
292
  ## 使用示例
283
293
 
284
- **任务:** 为产品仪表板添加筛选功能
294
+ **完整任务模式:** 为产品仪表板添加筛选功能
285
295
 
286
296
  ```
287
297
  步骤1: 分析产品仪表板代码流程