@localsummer/incspec 0.0.6 → 0.0.8

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.
Files changed (38) hide show
  1. package/README.md +76 -15
  2. package/commands/analyze.mjs +28 -12
  3. package/commands/apply.mjs +78 -33
  4. package/commands/archive.mjs +25 -3
  5. package/commands/collect-dep.mjs +2 -2
  6. package/commands/collect-req.mjs +10 -2
  7. package/commands/design.mjs +2 -2
  8. package/commands/help.mjs +20 -11
  9. package/commands/list.mjs +2 -1
  10. package/commands/merge.mjs +64 -33
  11. package/commands/reset.mjs +166 -0
  12. package/commands/status.mjs +30 -7
  13. package/commands/sync.mjs +210 -0
  14. package/commands/update.mjs +2 -1
  15. package/index.mjs +13 -6
  16. package/lib/agents.mjs +1 -1
  17. package/lib/claude.mjs +144 -0
  18. package/lib/config.mjs +13 -10
  19. package/lib/cursor.mjs +20 -5
  20. package/lib/terminal.mjs +108 -0
  21. package/lib/workflow.mjs +123 -29
  22. package/package.json +1 -1
  23. package/templates/AGENTS.md +89 -36
  24. package/templates/INCSPEC_BLOCK.md +1 -1
  25. package/templates/WORKFLOW.md +1 -0
  26. package/templates/cursor-commands/analyze-codeflow.md +12 -1
  27. package/templates/cursor-commands/apply-increment-code.md +129 -1
  28. package/templates/cursor-commands/merge-to-baseline.md +87 -1
  29. package/templates/cursor-commands/structured-requirements-collection.md +6 -0
  30. package/templates/inc-spec-skill/SKILL.md +286 -0
  31. package/templates/inc-spec-skill/references/analyze-codeflow.md +368 -0
  32. package/templates/inc-spec-skill/references/analyze-increment-codeflow.md +246 -0
  33. package/templates/inc-spec-skill/references/apply-increment-code.md +520 -0
  34. package/templates/inc-spec-skill/references/inc-archive.md +278 -0
  35. package/templates/inc-spec-skill/references/merge-to-baseline.md +415 -0
  36. package/templates/inc-spec-skill/references/structured-requirements-collection.md +129 -0
  37. package/templates/inc-spec-skill/references/ui-dependency-collection.md +143 -0
  38. package/commands/cursor-sync.mjs +0 -116
package/commands/list.mjs CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  print,
16
16
  printTable,
17
17
  printWarning,
18
+ formatLocalDateTime,
18
19
  } from '../lib/terminal.mjs';
19
20
 
20
21
  /**
@@ -47,7 +48,7 @@ export async function listCommand(ctx) {
47
48
  } else {
48
49
  specs.forEach(spec => {
49
50
  const info = getSpecInfo(spec.path);
50
- const mtime = spec.mtime.toISOString().replace('T', ' ').slice(0, 16);
51
+ const mtime = formatLocalDateTime(spec.mtime);
51
52
  const versionStr = info.version ? colorize(`v${info.version}`, colors.cyan) : '';
52
53
 
53
54
  print(` ${colorize(spec.name, colors.white)} ${versionStr}`);
@@ -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
- // Get increment file
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
- print(colorize(`增量设计文件: ${incrementPath}`, colors.dim));
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
- print(colorize('请在 Cursor 中运行以下命令:', colors.cyan));
88
- print('');
89
- print(colorize(` /incspec/inc-merge ${incrementPath}`, colors.bold, colors.white));
90
- print('');
91
- print(colorize('或使用 Claude Code 命令:', colors.cyan));
92
- print('');
93
- const outDir = path.join(projectRoot, INCSPEC_DIR, DIRS.baselines);
94
- print(colorize(` /ai-increment:merge-to-baseline ${incrementPath} ${outDir}`, colors.bold, colors.white));
95
- print('');
96
- print(colorize('该命令将:', colors.dim));
97
- print(colorize(' 1. 解析增量设计文件中的时序图和依赖图', colors.dim));
98
- print(colorize(' 2. 清理增量标记 (🆕/✏️/❌)', colors.dim));
99
- print(colorize(' 3. 重新编号为 S1-Sxx, D1-Dxx', colors.dim));
100
- print(colorize(' 4. 生成新的基线快照', colors.dim));
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('');
@@ -0,0 +1,166 @@
1
+ /**
2
+ * reset command - Reset current workflow
3
+ */
4
+
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import {
8
+ ensureInitialized,
9
+ INCSPEC_DIR,
10
+ DIRS,
11
+ } from '../lib/config.mjs';
12
+ import {
13
+ readWorkflow,
14
+ archiveWorkflow,
15
+ STATUS,
16
+ } from '../lib/workflow.mjs';
17
+ import { archiveSpec } from '../lib/spec.mjs';
18
+ import {
19
+ colors,
20
+ colorize,
21
+ print,
22
+ printSuccess,
23
+ printInfo,
24
+ printWarning,
25
+ } from '../lib/terminal.mjs';
26
+
27
+ /**
28
+ * Extract module name from workflow name
29
+ * e.g., "analyze-incspec-cli" -> "incspec-cli"
30
+ * @param {string} workflowName
31
+ * @returns {string}
32
+ */
33
+ function extractModuleName(workflowName) {
34
+ if (!workflowName) {
35
+ return 'unknown';
36
+ }
37
+
38
+ // Remove common prefixes like "analyze-"
39
+ const prefixes = ['analyze-', 'feature-', 'fix-', 'refactor-'];
40
+ let moduleName = workflowName;
41
+
42
+ for (const prefix of prefixes) {
43
+ if (moduleName.startsWith(prefix)) {
44
+ moduleName = moduleName.slice(prefix.length);
45
+ break;
46
+ }
47
+ }
48
+
49
+ return moduleName || workflowName;
50
+ }
51
+
52
+ /**
53
+ * Get output directory for a step index
54
+ * @param {number} stepIndex - 0-based step index
55
+ * @returns {string|null}
56
+ */
57
+ function getOutputDirForStepIndex(stepIndex) {
58
+ // Step 1 (index 0) and Step 6 (index 5) -> baselines
59
+ if (stepIndex === 0 || stepIndex === 5) {
60
+ return DIRS.baselines;
61
+ }
62
+
63
+ // Step 2-3 (index 1-2) -> requirements
64
+ if (stepIndex === 1 || stepIndex === 2) {
65
+ return DIRS.requirements;
66
+ }
67
+
68
+ // Step 4 (index 3) -> increments
69
+ if (stepIndex === 3) {
70
+ return DIRS.increments;
71
+ }
72
+
73
+ return null;
74
+ }
75
+
76
+ /**
77
+ * Collect output files from workflow steps
78
+ * @param {string} projectRoot
79
+ * @param {Object} workflow
80
+ * @returns {Array<{path: string, name: string}>}
81
+ */
82
+ function collectWorkflowOutputs(projectRoot, workflow) {
83
+ const outputs = [];
84
+
85
+ if (!workflow?.steps) {
86
+ return outputs;
87
+ }
88
+
89
+ workflow.steps.forEach((step, index) => {
90
+ // Only collect completed steps with output
91
+ if (step.status !== STATUS.COMPLETED || !step.output || step.output === '-') {
92
+ return;
93
+ }
94
+
95
+ const dir = getOutputDirForStepIndex(index);
96
+ if (!dir) {
97
+ return;
98
+ }
99
+
100
+ const outputPath = path.join(projectRoot, INCSPEC_DIR, dir, step.output);
101
+ if (fs.existsSync(outputPath)) {
102
+ outputs.push({
103
+ path: outputPath,
104
+ name: step.output,
105
+ });
106
+ }
107
+ });
108
+
109
+ return outputs;
110
+ }
111
+
112
+ /**
113
+ * Execute reset command
114
+ * @param {Object} ctx - Command context
115
+ */
116
+ export async function resetCommand(ctx) {
117
+ const { cwd } = ctx;
118
+
119
+ // Ensure initialized
120
+ const projectRoot = ensureInitialized(cwd);
121
+
122
+ // Read current workflow
123
+ const workflow = readWorkflow(projectRoot);
124
+
125
+ if (!workflow?.currentWorkflow) {
126
+ print('');
127
+ printInfo('当前无活跃工作流,无需重置');
128
+ return;
129
+ }
130
+
131
+ const workflowName = workflow.currentWorkflow;
132
+ const moduleName = extractModuleName(workflowName);
133
+
134
+ print('');
135
+ print(colorize(' incspec 工作流重置', colors.bold, colors.cyan));
136
+ print(colorize(' ─────────────────', colors.dim));
137
+ print('');
138
+ print(colorize(`工作流: ${workflowName}`, colors.dim));
139
+ print('');
140
+
141
+ // Collect output files
142
+ const outputs = collectWorkflowOutputs(projectRoot, workflow);
143
+
144
+ // Archive output files
145
+ if (outputs.length > 0) {
146
+ print(colorize('归档产出文件:', colors.dim));
147
+
148
+ for (const output of outputs) {
149
+ try {
150
+ const archivePath = archiveSpec(projectRoot, output.path, true, moduleName);
151
+ print(colorize(` - ${output.name}`, colors.dim));
152
+ } catch (e) {
153
+ printWarning(`归档失败: ${output.name} - ${e.message}`);
154
+ }
155
+ }
156
+
157
+ print('');
158
+ printSuccess(`已归档 ${outputs.length} 个产出文件到 archives/${new Date().toISOString().slice(0, 7)}/${moduleName}/`);
159
+ } else {
160
+ printInfo('无产出文件需要归档');
161
+ }
162
+
163
+ // Reset workflow state
164
+ archiveWorkflow(projectRoot);
165
+ printSuccess('工作流已重置');
166
+ }
@@ -10,6 +10,9 @@ import {
10
10
  readWorkflow,
11
11
  STEPS,
12
12
  STATUS,
13
+ MODE,
14
+ QUICK_MODE_SKIPPED,
15
+ isQuickMode,
13
16
  } from '../lib/workflow.mjs';
14
17
  import {
15
18
  colors,
@@ -49,7 +52,11 @@ export async function statusCommand(ctx) {
49
52
 
50
53
  // Current workflow
51
54
  if (workflow?.currentWorkflow) {
55
+ const quickMode = isQuickMode(workflow);
56
+ const modeLabel = quickMode ? '快速模式 (5步)' : '完整模式 (7步)';
57
+
52
58
  print(colorize(`当前工作流: `, colors.bold) + colorize(workflow.currentWorkflow, colors.cyan));
59
+ print(colorize(`工作流模式: ${modeLabel}`, colors.dim));
53
60
  print(colorize(`开始时间: ${workflow.startTime || '-'}`, colors.dim));
54
61
  print(colorize(`最后更新: ${workflow.lastUpdate || '-'}`, colors.dim));
55
62
  print('');
@@ -60,8 +67,14 @@ export async function statusCommand(ctx) {
60
67
 
61
68
  STEPS.forEach((step, index) => {
62
69
  const stepData = workflow.steps[index] || {};
63
- const status = stepData.status || STATUS.PENDING;
70
+ let status = stepData.status || STATUS.PENDING;
64
71
  const isCurrent = workflow.currentStep === step.id;
72
+ const isSkipped = quickMode && QUICK_MODE_SKIPPED.includes(step.id);
73
+
74
+ // Quick mode: mark skipped steps
75
+ if (isSkipped && status !== STATUS.SKIPPED) {
76
+ status = STATUS.SKIPPED;
77
+ }
65
78
 
66
79
  // Determine display status
67
80
  let displayStatus = status;
@@ -69,7 +82,12 @@ export async function statusCommand(ctx) {
69
82
  displayStatus = STATUS.IN_PROGRESS;
70
83
  }
71
84
 
72
- printStep(step.id, step.label, displayStatus);
85
+ // Use dimmed style for skipped steps
86
+ if (status === STATUS.SKIPPED) {
87
+ print(colorize(` [-] ${step.id}. ${step.label}`, colors.dim) + colorize(' (已跳过)', colors.dim));
88
+ } else {
89
+ printStep(step.id, step.label, displayStatus);
90
+ }
73
91
 
74
92
  // Show output file if completed
75
93
  if (stepData.output && status === STATUS.COMPLETED) {
@@ -80,9 +98,14 @@ export async function statusCommand(ctx) {
80
98
  print('');
81
99
 
82
100
  // Next step hint
83
- const nextStepIndex = workflow.steps.findIndex(
84
- step => (step?.status || STATUS.PENDING) !== STATUS.COMPLETED
85
- );
101
+ const nextStepIndex = workflow.steps.findIndex((step, index) => {
102
+ const stepNumber = index + 1;
103
+ // Skip excluded steps in quick mode
104
+ if (quickMode && QUICK_MODE_SKIPPED.includes(stepNumber)) {
105
+ return false;
106
+ }
107
+ return (step?.status || STATUS.PENDING) !== STATUS.COMPLETED;
108
+ });
86
109
  if (nextStepIndex >= 0) {
87
110
  const nextStep = STEPS[nextStepIndex];
88
111
  print(colorize('下一步:', colors.bold));
@@ -107,8 +130,8 @@ export async function statusCommand(ctx) {
107
130
 
108
131
  const recentHistory = workflow.history.slice(0, 5);
109
132
  recentHistory.forEach(item => {
110
- const statusIcon = item.status === 'completed'
111
- ? colorize('✓', colors.green)
133
+ const statusIcon = item.status === 'completed'
134
+ ? colorize('✓', colors.green)
112
135
  : colorize('○', colors.dim);
113
136
  print(` ${statusIcon} ${item.name} (${item.startTime || '-'})`);
114
137
  });
@@ -0,0 +1,210 @@
1
+ /**
2
+ * sync command - Sync incspec integrations to IDEs/AI tools
3
+ */
4
+
5
+ import {
6
+ syncToProject as syncCursorToProject,
7
+ syncToGlobal as syncCursorToGlobal,
8
+ } from '../lib/cursor.mjs';
9
+ import {
10
+ syncToProjectClaude,
11
+ syncToGlobalClaude,
12
+ } from '../lib/claude.mjs';
13
+ import {
14
+ colors,
15
+ colorize,
16
+ print,
17
+ printSuccess,
18
+ printWarning,
19
+ printInfo,
20
+ select,
21
+ checkbox,
22
+ } from '../lib/terminal.mjs';
23
+
24
+ /** Sync target definitions */
25
+ const SYNC_TARGETS = {
26
+ cursor: {
27
+ name: 'Cursor',
28
+ value: 'cursor',
29
+ },
30
+ claude: {
31
+ name: 'Claude Code',
32
+ value: 'claude',
33
+ },
34
+ };
35
+
36
+ /**
37
+ * Execute sync command
38
+ * @param {Object} ctx - Command context
39
+ */
40
+ export async function syncCommand(ctx) {
41
+ const { cwd, options } = ctx;
42
+
43
+ print('');
44
+ print(colorize(' IncSpec 集成同步', colors.bold, colors.cyan));
45
+ print(colorize(' ────────────────', colors.dim));
46
+ print('');
47
+
48
+ // Determine sync targets
49
+ let targets = [];
50
+
51
+ // Command line args take priority
52
+ if (options.cursor) {
53
+ targets.push('cursor');
54
+ }
55
+ if (options.claude) {
56
+ targets.push('claude');
57
+ }
58
+ if (options.all) {
59
+ targets = ['cursor', 'claude'];
60
+ }
61
+
62
+ // If no args specified, use interactive checkbox
63
+ if (targets.length === 0) {
64
+ targets = await checkbox({
65
+ message: '选择要同步的目标:',
66
+ choices: [
67
+ { ...SYNC_TARGETS.cursor, checked: true },
68
+ { ...SYNC_TARGETS.claude, checked: false },
69
+ ],
70
+ });
71
+
72
+ if (targets.length === 0) {
73
+ printWarning('未选择任何同步目标。');
74
+ return;
75
+ }
76
+ }
77
+
78
+ print(colorize(`已选择: ${targets.map(t => SYNC_TARGETS[t].name).join(', ')}`, colors.dim));
79
+ print('');
80
+
81
+ // Execute sync for each target
82
+ for (const target of targets) {
83
+ if (target === 'cursor') {
84
+ await syncCursor(ctx);
85
+ } else if (target === 'claude') {
86
+ await syncClaude(ctx);
87
+ }
88
+ print('');
89
+ }
90
+
91
+ printInfo('同步完成。');
92
+ print('');
93
+ }
94
+
95
+ /**
96
+ * Sync Cursor commands
97
+ * @param {Object} ctx
98
+ */
99
+ async function syncCursor(ctx) {
100
+ const { cwd, options } = ctx;
101
+
102
+ print(colorize('=== Cursor 命令同步 ===', colors.bold));
103
+ print('');
104
+
105
+ // Determine sync target
106
+ let syncTarget = null;
107
+
108
+ if (options.project) {
109
+ syncTarget = 'project';
110
+ } else if (options.global) {
111
+ syncTarget = 'global';
112
+ } else {
113
+ const choices = [
114
+ {
115
+ name: `当前目录 (${cwd}/.cursor/commands/incspec/)`,
116
+ value: 'project',
117
+ description: '仅对当前目录生效',
118
+ },
119
+ {
120
+ name: '全局目录 (~/.cursor/commands/incspec/)',
121
+ value: 'global',
122
+ description: '对所有项目生效',
123
+ },
124
+ ];
125
+
126
+ syncTarget = await select({
127
+ message: 'Cursor - 选择同步目标:',
128
+ choices,
129
+ });
130
+ }
131
+
132
+ // Execute sync
133
+ if (syncTarget === 'project') {
134
+ const count = syncCursorToProject(cwd);
135
+ printSuccess(`Cursor: 已同步 ${count} 个命令到 .cursor/commands/incspec/`);
136
+ printCursorCommands();
137
+ } else if (syncTarget === 'global') {
138
+ const count = syncCursorToGlobal();
139
+ printSuccess(`Cursor: 已同步 ${count} 个命令到 ~/.cursor/commands/incspec/`);
140
+ printCursorCommands();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Print Cursor commands list
146
+ */
147
+ function printCursorCommands() {
148
+ print('');
149
+ print(colorize('已创建的命令:', colors.bold));
150
+ print(colorize(' /incspec/inc-analyze 步骤1: 分析代码流程', colors.dim));
151
+ print(colorize(' /incspec/inc-collect-req 步骤2: 收集结构化需求', colors.dim));
152
+ print(colorize(' /incspec/inc-collect-dep 步骤3: UI依赖采集', colors.dim));
153
+ print(colorize(' /incspec/inc-design 步骤4: 增量设计', colors.dim));
154
+ print(colorize(' /incspec/inc-apply 步骤5: 应用代码变更', colors.dim));
155
+ print(colorize(' /incspec/inc-merge 步骤6: 合并到基线', colors.dim));
156
+ print(colorize(' /incspec/inc-archive 归档规范文件', colors.dim));
157
+ print(colorize(' /incspec/inc-status 查看工作流状态', colors.dim));
158
+ print(colorize(' /incspec/inc-help 显示帮助', colors.dim));
159
+ print('');
160
+ printInfo('请重启 Cursor 以加载新命令。');
161
+ }
162
+
163
+ /**
164
+ * Sync Claude Code skill
165
+ * @param {Object} ctx
166
+ */
167
+ async function syncClaude(ctx) {
168
+ const { cwd, options } = ctx;
169
+
170
+ print(colorize('=== Claude Code Skill 同步 ===', colors.bold));
171
+ print('');
172
+
173
+ // Determine sync target
174
+ let syncTarget = null;
175
+
176
+ if (options.project) {
177
+ syncTarget = 'project';
178
+ } else if (options.global) {
179
+ syncTarget = 'global';
180
+ } else {
181
+ const choices = [
182
+ {
183
+ name: `当前目录 (${cwd}/.claude/skills/inc-spec-skill/)`,
184
+ value: 'project',
185
+ description: '仅对当前目录生效',
186
+ },
187
+ {
188
+ name: '全局目录 (~/.claude/skills/inc-spec-skill/)',
189
+ value: 'global',
190
+ description: '对所有项目生效(推荐)',
191
+ },
192
+ ];
193
+
194
+ syncTarget = await select({
195
+ message: 'Claude Code - 选择同步目标:',
196
+ choices,
197
+ });
198
+ }
199
+
200
+ // Execute sync
201
+ if (syncTarget === 'project') {
202
+ const { count } = syncToProjectClaude(cwd);
203
+ printSuccess(`Claude Code: 已同步 ${count} 个文件到 .claude/skills/inc-spec-skill/`);
204
+ print(colorize(' 包含: SKILL.md + references/', colors.dim));
205
+ } else if (syncTarget === 'global') {
206
+ const { count } = syncToGlobalClaude();
207
+ printSuccess(`Claude Code: 已同步 ${count} 个文件到 ~/.claude/skills/inc-spec-skill/`);
208
+ print(colorize(' 包含: SKILL.md + references/', colors.dim));
209
+ }
210
+ }
@@ -20,6 +20,7 @@ import {
20
20
  printWarning,
21
21
  printInfo,
22
22
  confirm,
23
+ formatLocalDateTime,
23
24
  } from '../lib/terminal.mjs';
24
25
 
25
26
  /**
@@ -71,7 +72,7 @@ function updateIncspecWorkflow(projectRoot) {
71
72
  return { updated: false, path: targetPath, reason: '保留用户工作流数据' };
72
73
  }
73
74
 
74
- const now = new Date().toISOString().replace('T', ' ').slice(0, 16);
75
+ const now = formatLocalDateTime(new Date());
75
76
  let templateContent = fs.readFileSync(templatePath, 'utf-8');
76
77
  templateContent = templateContent.replace(/\{\{last_update\}\}/g, now);
77
78
 
package/index.mjs CHANGED
@@ -17,7 +17,8 @@ import { mergeCommand } from './commands/merge.mjs';
17
17
  import { listCommand } from './commands/list.mjs';
18
18
  import { validateCommand } from './commands/validate.mjs';
19
19
  import { archiveCommand } from './commands/archive.mjs';
20
- import { cursorSyncCommand } from './commands/cursor-sync.mjs';
20
+ import { resetCommand } from './commands/reset.mjs';
21
+ import { syncCommand } from './commands/sync.mjs';
21
22
  import { helpCommand } from './commands/help.mjs';
22
23
  import { colors, colorize } from './lib/terminal.mjs';
23
24
 
@@ -156,7 +157,7 @@ async function main() {
156
157
  await statusCommand(commandContext);
157
158
  break;
158
159
 
159
- // Workflow commands (Step 1-6)
160
+ // Workflow commands (Step 1-6, Step 7 archive is separate)
160
161
  case 'analyze':
161
162
  case 'a':
162
163
  await analyzeCommand(commandContext);
@@ -203,10 +204,16 @@ async function main() {
203
204
  await archiveCommand(commandContext);
204
205
  break;
205
206
 
206
- // Cursor integration
207
- case 'cursor-sync':
208
- case 'cs':
209
- await cursorSyncCommand(commandContext);
207
+ // Reset workflow
208
+ case 'reset':
209
+ case 'rs':
210
+ await resetCommand(commandContext);
211
+ break;
212
+
213
+ // Sync integrations
214
+ case 'sync':
215
+ case 's':
216
+ await syncCommand(commandContext);
210
217
  break;
211
218
 
212
219
  // Help
package/lib/agents.mjs CHANGED
@@ -43,7 +43,7 @@ export function getIncspecBlockTemplate() {
43
43
  - 请求含义模糊,需要先了解规范工作流再编码
44
44
 
45
45
  通过 \`@/incspec/AGENTS.md\` 可以了解:
46
- - 如何使用 6 步增量编码工作流
46
+ - 如何使用 7 步增量编码工作流
47
47
  - 规范格式与约定
48
48
  - 项目结构与指南
49
49