@localsummer/incspec 0.1.3 → 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.3",
3
+ "version": "0.2.0",
4
4
  "description": "面向 AI 编程助手的增量规范驱动开发工具",
5
5
  "bin": {
6
6
  "incspec": "index.mjs"
@@ -48,7 +48,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
48
48
 
49
49
  ### 步骤 2: 收集结构化需求
50
50
 
51
- **命令**: `incspec collect-req` (别名: `cr`)
51
+ **命令**: `incspec collect-req [--force]` (别名: `cr`)
52
52
 
53
53
  **目的**: 交互式需求收集,转换为 5 列格式。
54
54
 
@@ -58,7 +58,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
58
58
 
59
59
  ### 步骤 3: 收集 UI 依赖
60
60
 
61
- **命令**: `incspec collect-dep` (别名: `cd`)
61
+ **命令**: `incspec collect-dep [--force]` (别名: `cd`)
62
62
 
63
63
  **目的**: 映射新增/修改 UI 组件的所有上下文依赖。
64
64
 
@@ -68,7 +68,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
68
68
 
69
69
  ### 步骤 4: 设计增量
70
70
 
71
- **命令**: `incspec design [--feature=name]` (别名: `d`)
71
+ **命令**: `incspec design [--feature=name] [--force]` (别名: `d`)
72
72
 
73
73
  **目的**: 创建全面的增量设计蓝图。
74
74
 
@@ -87,7 +87,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
87
87
 
88
88
  ### 步骤 5: 应用代码变更
89
89
 
90
- **命令**: `incspec apply [increment-path]` (别名: `ap`)
90
+ **命令**: `incspec apply [increment-path] [--force]` (别名: `ap`)
91
91
 
92
92
  **目的**: 根据增量蓝图执行代码生成和修改。
93
93
 
@@ -97,7 +97,7 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
97
97
 
98
98
  ### 步骤 6: 合并到基线
99
99
 
100
- **命令**: `incspec merge [increment-path]` (别名: `m`)
100
+ **命令**: `incspec merge [increment-path] [--force]` (别名: `m`)
101
101
 
102
102
  **目的**: 将增量整合到新的基线快照中。
103
103
 
@@ -145,11 +145,11 @@ incspec list / ls [-l] [-a] # 列出规格文件
145
145
 
146
146
  # 7步工作流
147
147
  incspec analyze <path> [--quick] [--module=name] [--baseline=file] # 步骤1
148
- incspec collect-req / cr # 步骤2
149
- incspec collect-dep / cd # 步骤3 (快速模式跳过)
150
- incspec design / d [--feature=name] # 步骤4 (快速模式跳过)
151
- incspec apply / ap [path] # 步骤5
152
- 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
153
153
  incspec archive [--yes] [--keep] # 步骤7
154
154
 
155
155
  # 验证与同步
@@ -223,10 +223,15 @@ baseline: home-baseline-v1
223
223
  |------|----------|
224
224
  | Workflow not initialized | 运行 `incspec init` |
225
225
  | No baseline found | 先完成步骤 1 (analyze) |
226
- | Previous step not completed | 运行 `incspec status` 检查进度 |
226
+ | Previous step not completed | 运行 `incspec status` 检查进度,或添加 `--force` 跳过前置检查 |
227
227
  | Validation failed | 检查文件格式和编号序列 |
228
228
  | 工作流卡住或状态异常 | `incspec reset` 完全重置,或 `incspec reset --to=N` 回退到步骤N |
229
229
 
230
+ **前置步骤检查**:
231
+ - 步骤 2-6 执行前会自动检查前置步骤是否完成
232
+ - 若前置步骤未完成,命令会提示并阻止执行
233
+ - 添加 `--force` 可跳过此检查,强制执行当前步骤
234
+
230
235
  **工作流重置**:
231
236
  - 完全重置: `incspec reset` - 归档所有产出,回到初始状态
232
237
  - 部分回退: `incspec reset --to=N` - 保留步骤1-N,重置N+1至7(示例:`--to=3` 保留1-3,重置4-7)
@@ -104,6 +104,8 @@ description: 设计优先的前端功能增量编码工作流。适用于实现
104
104
  | 合并 | 完整/快速 | `incspec merge <report>` | `--complete --output=<file>` |
105
105
  | 归档 | 完整/快速 | `incspec archive --yes` | - |
106
106
 
107
+ **前置步骤检查**: 步骤 2-6 执行前会自动检查前置步骤是否完成。若前置步骤未完成,命令会提示并阻止执行。添加 `--force` 可跳过此检查。
108
+
107
109
  管理命令:
108
110
  - `incspec status` - 查看工作流状态
109
111
  - `incspec list` - 列出规范文件
@@ -273,6 +275,7 @@ incspec archive <file> --keep # 复制而非移动
273
275
  | 跳过基线合并 | 对当前系统状态产生混淆 | 每次增量后必须执行步骤6 |
274
276
  | 忽略归档 | 工作区杂乱,历史丢失 | 工作流完成后立即执行步骤7 |
275
277
  | 工作流状态不一致 | CLI命令执行失败 | 使用 `incspec status` 检查状态,必要时使用 `incspec reset` 修复 |
278
+ | 前置步骤未完成 | 命令被阻止执行 | 按顺序完成前置步骤,或添加 `--force` 强制执行 |
276
279
 
277
280
  ## 参考资源
278
281