@localsummer/incspec 0.2.2 → 0.2.4
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 +8 -3
- package/package.json +5 -8
- package/{commands → src/commands}/analyze.mjs +2 -2
- package/{commands → src/commands}/apply.mjs +2 -2
- package/{commands → src/commands}/collect-dep.mjs +2 -2
- package/{commands → src/commands}/collect-req.mjs +2 -2
- package/{commands → src/commands}/design.mjs +2 -6
- package/{commands → src/commands}/help.mjs +1 -1
- package/{commands → src/commands}/merge.mjs +2 -3
- package/{commands → src/commands}/sync.mjs +45 -23
- package/{index.mjs → src/index.mjs} +1 -1
- package/{lib/cursor.mjs → src/lib/ide-sync.mjs} +151 -93
- package/lib/claude.mjs +0 -144
- package/templates/inc-spec-skill/SKILL.md +0 -325
- package/templates/inc-spec-skill/references/analyze-codeflow.md +0 -368
- package/templates/inc-spec-skill/references/analyze-increment-codeflow.md +0 -246
- package/templates/inc-spec-skill/references/apply-increment-code.md +0 -520
- package/templates/inc-spec-skill/references/inc-archive.md +0 -278
- package/templates/inc-spec-skill/references/merge-to-baseline.md +0 -415
- package/templates/inc-spec-skill/references/structured-requirements-collection.md +0 -129
- package/templates/inc-spec-skill/references/ui-dependency-collection.md +0 -143
- /package/{commands → src/commands}/archive.mjs +0 -0
- /package/{commands → src/commands}/init.mjs +0 -0
- /package/{commands → src/commands}/list.mjs +0 -0
- /package/{commands → src/commands}/reset.mjs +0 -0
- /package/{commands → src/commands}/status.mjs +0 -0
- /package/{commands → src/commands}/update.mjs +0 -0
- /package/{commands → src/commands}/validate.mjs +0 -0
- /package/{lib → src/lib}/agents.mjs +0 -0
- /package/{lib → src/lib}/config.mjs +0 -0
- /package/{lib → src/lib}/spec.mjs +0 -0
- /package/{lib → src/lib}/terminal.mjs +0 -0
- /package/{lib → src/lib}/workflow.mjs +0 -0
- /package/{templates → src/templates}/AGENTS.md +0 -0
- /package/{templates → src/templates}/INCSPEC_BLOCK.md +0 -0
- /package/{templates → src/templates}/WORKFLOW.md +0 -0
- /package/{templates → src/templates}/commands/analyze-codeflow.md +0 -0
- /package/{templates → src/templates}/commands/analyze-increment-codeflow.md +0 -0
- /package/{templates → src/templates}/commands/apply-increment-code.md +0 -0
- /package/{templates → src/templates}/commands/inc-archive.md +0 -0
- /package/{templates → src/templates}/commands/merge-to-baseline.md +0 -0
- /package/{templates → src/templates}/commands/structured-requirements-collection.md +0 -0
- /package/{templates → src/templates}/commands/ui-dependency-collection.md +0 -0
- /package/{templates → src/templates}/project.md +0 -0
package/README.md
CHANGED
|
@@ -160,7 +160,7 @@ AI 编程助手在处理复杂前端代码库时常常力不从心,因为 API
|
|
|
160
160
|
| 工具 | 命令 |
|
|
161
161
|
|------|------|
|
|
162
162
|
| **Cursor** | `/incspec/inc-analyze`、`/incspec/inc-collect-req`、`/incspec/inc-collect-dep`、`/incspec/inc-design`、`/incspec/inc-apply`、`/incspec/inc-merge`、`/incspec/inc-archive` |
|
|
163
|
-
| **Claude Code** |
|
|
163
|
+
| **Claude Code** | 同 Cursor,使用相同的 `/incspec/inc-*` 命令 |
|
|
164
164
|
|
|
165
165
|
</details>
|
|
166
166
|
|
|
@@ -226,8 +226,8 @@ incspec sync --all # 全部
|
|
|
226
226
|
|
|
227
227
|
**设置完成后:**
|
|
228
228
|
- 运行 `incspec status` 验证设置
|
|
229
|
-
- Cursor
|
|
230
|
-
-
|
|
229
|
+
- Cursor 和 Claude Code 用户都可直接使用 `/incspec/inc-*` 命令
|
|
230
|
+
- 命令生成到 `.cursor/commands/incspec/` 或 `.claude/commands/incspec/` 目录
|
|
231
231
|
|
|
232
232
|
### 创建你的第一个增量
|
|
233
233
|
|
|
@@ -470,6 +470,11 @@ incspec reset -t 3 # 短选项形式
|
|
|
470
470
|
```
|
|
471
471
|
your-project/
|
|
472
472
|
├── AGENTS.md # AI 代理指令(包含 incspec 指令块)
|
|
473
|
+
├── src/ # IncSpec 源代码
|
|
474
|
+
│ ├── index.mjs # CLI 入口
|
|
475
|
+
│ ├── commands/ # 命令实现
|
|
476
|
+
│ ├── lib/ # 核心库
|
|
477
|
+
│ └── templates/ # Markdown 模板文件
|
|
473
478
|
├── incspec/
|
|
474
479
|
│ ├── project.md # 项目配置
|
|
475
480
|
│ ├── WORKFLOW.md # 当前工作流状态
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localsummer/incspec",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "面向 AI 编程助手的增量规范驱动开发工具",
|
|
5
5
|
"bin": {
|
|
6
|
-
"incspec": "index.mjs"
|
|
6
|
+
"incspec": "src/index.mjs"
|
|
7
7
|
},
|
|
8
|
-
"main": "index.mjs",
|
|
8
|
+
"main": "src/index.mjs",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"start": "node index.mjs"
|
|
11
|
+
"start": "node src/index.mjs"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"incspec",
|
|
@@ -32,9 +32,6 @@
|
|
|
32
32
|
"node": ">=18.0.0"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
|
-
"
|
|
36
|
-
"commands/",
|
|
37
|
-
"lib/",
|
|
38
|
-
"templates/"
|
|
35
|
+
"src/"
|
|
39
36
|
]
|
|
40
37
|
}
|
|
@@ -223,9 +223,9 @@ export async function analyzeCommand(ctx) {
|
|
|
223
223
|
print('');
|
|
224
224
|
print(colorize(` /incspec/inc-analyze ${sourcePath} --module=${moduleName}`, colors.bold, colors.white));
|
|
225
225
|
print('');
|
|
226
|
-
print(colorize('或在 Claude Code
|
|
226
|
+
print(colorize('或在 Claude Code 中使用斜杠命令:', colors.cyan));
|
|
227
227
|
print('');
|
|
228
|
-
print(colorize(`
|
|
228
|
+
print(colorize(` /incspec/inc-analyze ${sourcePath} --module=${moduleName}`, colors.bold, colors.white));
|
|
229
229
|
print('');
|
|
230
230
|
printInfo(`完成后运行 'incspec status' 查看进度`);
|
|
231
231
|
print('');
|
|
@@ -159,9 +159,9 @@ export async function applyCommand(ctx) {
|
|
|
159
159
|
print('');
|
|
160
160
|
print(colorize(` /incspec/inc-apply ${inputPath}`, colors.bold, colors.white));
|
|
161
161
|
print('');
|
|
162
|
-
print(colorize('或在 Claude Code
|
|
162
|
+
print(colorize('或在 Claude Code 中使用斜杠命令:', colors.cyan));
|
|
163
163
|
print('');
|
|
164
|
-
print(colorize(`
|
|
164
|
+
print(colorize(` /incspec/inc-apply ${inputPath}`, colors.bold, colors.white));
|
|
165
165
|
print('');
|
|
166
166
|
print(colorize('该命令将:', colors.dim));
|
|
167
167
|
print(colorize(' 1. 解析增量设计文件中的变更计划', colors.dim));
|
|
@@ -76,9 +76,9 @@ export async function collectDepCommand(ctx) {
|
|
|
76
76
|
print('');
|
|
77
77
|
print(colorize(` /incspec/inc-collect-dep`, colors.bold, colors.white));
|
|
78
78
|
print('');
|
|
79
|
-
print(colorize('或在 Claude Code
|
|
79
|
+
print(colorize('或在 Claude Code 中使用斜杠命令:', colors.cyan));
|
|
80
80
|
print('');
|
|
81
|
-
print(colorize(`
|
|
81
|
+
print(colorize(` /incspec/inc-collect-dep`, colors.bold, colors.white));
|
|
82
82
|
print('');
|
|
83
83
|
print(colorize('该命令将交互式采集 6 维度 UI 依赖:', colors.dim));
|
|
84
84
|
print(colorize(' - UI组件库 (Arco/Antd)', colors.dim));
|
|
@@ -71,9 +71,9 @@ export async function collectReqCommand(ctx) {
|
|
|
71
71
|
print('');
|
|
72
72
|
print(colorize(` /incspec/inc-collect-req`, colors.bold, colors.white));
|
|
73
73
|
print('');
|
|
74
|
-
print(colorize('或在 Claude Code
|
|
74
|
+
print(colorize('或在 Claude Code 中使用斜杠命令:', colors.cyan));
|
|
75
75
|
print('');
|
|
76
|
-
print(colorize(`
|
|
76
|
+
print(colorize(` /incspec/inc-collect-req`, colors.bold, colors.white));
|
|
77
77
|
print('');
|
|
78
78
|
print(colorize('该命令将交互式收集需求,生成 5 列结构化表格:', colors.dim));
|
|
79
79
|
print(colorize(' | 新增/修改功能 | 涉及UI组件 | 触发条件 | 影响的核心状态 | 预期数据流向 |', colors.dim));
|
|
@@ -116,13 +116,9 @@ export async function designCommand(ctx) {
|
|
|
116
116
|
print('');
|
|
117
117
|
print(colorize(` /incspec/inc-design --feature=${featureName}`, colors.bold, colors.white));
|
|
118
118
|
print('');
|
|
119
|
-
print(colorize('或在 Claude Code
|
|
119
|
+
print(colorize('或在 Claude Code 中使用斜杠命令:', colors.cyan));
|
|
120
120
|
print('');
|
|
121
|
-
|
|
122
|
-
const reqPath = path.join(projectRoot, INCSPEC_DIR, DIRS.requirements, 'structured-requirements.md');
|
|
123
|
-
const depPath = path.join(projectRoot, INCSPEC_DIR, DIRS.requirements, 'ui-dependencies.md');
|
|
124
|
-
const outDir = path.join(projectRoot, INCSPEC_DIR, DIRS.increments);
|
|
125
|
-
print(colorize(` 请基于基线 ${latestBaseline} 和需求/依赖文件,设计增量方案到 ${outDir}`, colors.dim));
|
|
121
|
+
print(colorize(` /incspec/inc-design --feature=${featureName}`, colors.bold, colors.white));
|
|
126
122
|
print('');
|
|
127
123
|
print(colorize('该命令将生成包含 7 大模块的增量设计蓝图:', colors.dim));
|
|
128
124
|
print(colorize(' 1. 一句话摘要', colors.dim));
|
|
@@ -137,7 +137,7 @@ const COMMANDS = {
|
|
|
137
137
|
description: '同步集成到 IDE/AI 工具 (Cursor, Claude Code)',
|
|
138
138
|
options: [
|
|
139
139
|
['--cursor', '仅同步 Cursor 命令'],
|
|
140
|
-
['--claude', '仅同步 Claude Code
|
|
140
|
+
['--claude', '仅同步 Claude Code 命令'],
|
|
141
141
|
['--all', '同步所有目标'],
|
|
142
142
|
['--project', '同步到当前目录'],
|
|
143
143
|
['--global', '同步到全局目录'],
|
|
@@ -146,10 +146,9 @@ export async function mergeCommand(ctx) {
|
|
|
146
146
|
print('');
|
|
147
147
|
print(colorize(` /incspec/inc-merge ${incrementPath}`, colors.bold, colors.white));
|
|
148
148
|
print('');
|
|
149
|
-
print(colorize('或在 Claude Code
|
|
149
|
+
print(colorize('或在 Claude Code 中使用斜杠命令:', colors.cyan));
|
|
150
150
|
print('');
|
|
151
|
-
|
|
152
|
-
print(colorize(` 请将 ${incrementPath} 的增量合并到基线 ${outDir}`, colors.dim));
|
|
151
|
+
print(colorize(` /incspec/inc-merge ${incrementPath}`, colors.bold, colors.white));
|
|
153
152
|
print('');
|
|
154
153
|
print(colorize('该命令将:', colors.dim));
|
|
155
154
|
print(colorize(' 1. 解析增量设计文件中的时序图和依赖图', colors.dim));
|
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
syncToProject
|
|
7
|
-
syncToGlobal
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
syncToProjectClaude,
|
|
11
|
-
syncToGlobalClaude,
|
|
12
|
-
} from '../lib/claude.mjs';
|
|
6
|
+
syncToProject,
|
|
7
|
+
syncToGlobal,
|
|
8
|
+
getIDEConfig,
|
|
9
|
+
} from '../lib/ide-sync.mjs';
|
|
13
10
|
import {
|
|
14
11
|
colors,
|
|
15
12
|
colorize,
|
|
@@ -110,14 +107,15 @@ async function syncCursor(ctx) {
|
|
|
110
107
|
} else if (options.global) {
|
|
111
108
|
syncTarget = 'global';
|
|
112
109
|
} else {
|
|
110
|
+
const config = getIDEConfig('cursor');
|
|
113
111
|
const choices = [
|
|
114
112
|
{
|
|
115
|
-
name: `当前目录 (${cwd}
|
|
113
|
+
name: `当前目录 (${cwd}/${config.projectDir}/)`,
|
|
116
114
|
value: 'project',
|
|
117
115
|
description: colorize('仅对当前目录生效', colors.blue),
|
|
118
116
|
},
|
|
119
117
|
{
|
|
120
|
-
name:
|
|
118
|
+
name: `全局目录 (${config.globalDir}/)`,
|
|
121
119
|
value: 'global',
|
|
122
120
|
description: colorize('对所有项目生效', colors.blue),
|
|
123
121
|
},
|
|
@@ -131,12 +129,14 @@ async function syncCursor(ctx) {
|
|
|
131
129
|
|
|
132
130
|
// Execute sync
|
|
133
131
|
if (syncTarget === 'project') {
|
|
134
|
-
const count =
|
|
135
|
-
|
|
132
|
+
const count = syncToProject('cursor', cwd);
|
|
133
|
+
const config = getIDEConfig('cursor');
|
|
134
|
+
printSuccess(`Cursor: 已同步 ${count} 个命令到 ${config.projectDir}/`);
|
|
136
135
|
printCursorCommands();
|
|
137
136
|
} else if (syncTarget === 'global') {
|
|
138
|
-
const count =
|
|
139
|
-
|
|
137
|
+
const count = syncToGlobal('cursor');
|
|
138
|
+
const config = getIDEConfig('cursor');
|
|
139
|
+
printSuccess(`Cursor: 已同步 ${count} 个命令到 ${config.globalDir}/`);
|
|
140
140
|
printCursorCommands();
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -161,13 +161,13 @@ function printCursorCommands() {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/**
|
|
164
|
-
* Sync Claude Code
|
|
164
|
+
* Sync Claude Code commands
|
|
165
165
|
* @param {Object} ctx
|
|
166
166
|
*/
|
|
167
167
|
async function syncClaude(ctx) {
|
|
168
168
|
const { cwd, options } = ctx;
|
|
169
169
|
|
|
170
|
-
print(colorize('=== Claude Code
|
|
170
|
+
print(colorize('=== Claude Code 命令同步 ===', colors.bold));
|
|
171
171
|
print('');
|
|
172
172
|
|
|
173
173
|
// Determine sync target
|
|
@@ -178,14 +178,15 @@ async function syncClaude(ctx) {
|
|
|
178
178
|
} else if (options.global) {
|
|
179
179
|
syncTarget = 'global';
|
|
180
180
|
} else {
|
|
181
|
+
const config = getIDEConfig('claude');
|
|
181
182
|
const choices = [
|
|
182
183
|
{
|
|
183
|
-
name: `当前目录 (${cwd}
|
|
184
|
+
name: `当前目录 (${cwd}/${config.projectDir}/)`,
|
|
184
185
|
value: 'project',
|
|
185
186
|
description: colorize('仅对当前目录生效', colors.blue),
|
|
186
187
|
},
|
|
187
188
|
{
|
|
188
|
-
name:
|
|
189
|
+
name: `全局目录 (${config.globalDir}/)`,
|
|
189
190
|
value: 'global',
|
|
190
191
|
description: colorize('对所有项目生效(推荐)', colors.blue),
|
|
191
192
|
},
|
|
@@ -199,12 +200,33 @@ async function syncClaude(ctx) {
|
|
|
199
200
|
|
|
200
201
|
// Execute sync
|
|
201
202
|
if (syncTarget === 'project') {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
const count = syncToProject('claude', cwd);
|
|
204
|
+
const config = getIDEConfig('claude');
|
|
205
|
+
printSuccess(`Claude Code: 已同步 ${count} 个命令到 ${config.projectDir}/`);
|
|
206
|
+
printClaudeCommands();
|
|
205
207
|
} else if (syncTarget === 'global') {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
const count = syncToGlobal('claude');
|
|
209
|
+
const config = getIDEConfig('claude');
|
|
210
|
+
printSuccess(`Claude Code: 已同步 ${count} 个命令到 ${config.globalDir}/`);
|
|
211
|
+
printClaudeCommands();
|
|
209
212
|
}
|
|
210
213
|
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Print Claude commands list
|
|
217
|
+
*/
|
|
218
|
+
function printClaudeCommands() {
|
|
219
|
+
print('');
|
|
220
|
+
print(colorize('已创建的命令:', colors.bold));
|
|
221
|
+
print(colorize(' /incspec/inc-analyze 步骤1: 分析代码流程', colors.dim));
|
|
222
|
+
print(colorize(' /incspec/inc-collect-req 步骤2: 收集结构化需求', colors.dim));
|
|
223
|
+
print(colorize(' /incspec/inc-collect-dep 步骤3: UI依赖采集', colors.dim));
|
|
224
|
+
print(colorize(' /incspec/inc-design 步骤4: 增量设计', colors.dim));
|
|
225
|
+
print(colorize(' /incspec/inc-apply 步骤5: 应用代码变更', colors.dim));
|
|
226
|
+
print(colorize(' /incspec/inc-merge 步骤6: 合并到基线', colors.dim));
|
|
227
|
+
print(colorize(' /incspec/inc-archive 归档规范文件', colors.dim));
|
|
228
|
+
print(colorize(' /incspec/inc-status 查看工作流状态', colors.dim));
|
|
229
|
+
print(colorize(' /incspec/inc-help 显示帮助', colors.dim));
|
|
230
|
+
print('');
|
|
231
|
+
printInfo('请重启 Claude Code 以加载新命令。');
|
|
232
|
+
}
|
|
@@ -121,7 +121,7 @@ async function main() {
|
|
|
121
121
|
const { createRequire } = await import('module');
|
|
122
122
|
const require = createRequire(import.meta.url);
|
|
123
123
|
try {
|
|
124
|
-
const pkg = require('
|
|
124
|
+
const pkg = require('../package.json');
|
|
125
125
|
console.log(pkg.version);
|
|
126
126
|
} catch {
|
|
127
127
|
console.log('unknown');
|
|
@@ -1,31 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* - Generate
|
|
4
|
-
* -
|
|
2
|
+
* Unified IDE integration utilities
|
|
3
|
+
* - Generate IDE slash commands from templates
|
|
4
|
+
* - Support multiple IDEs through configuration
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import * as os from 'os';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
|
-
import { INCSPEC_DIR
|
|
12
|
-
|
|
13
|
-
/** Cursor commands directory */
|
|
14
|
-
const CURSOR_COMMANDS_DIR = '.cursor/commands/incspec';
|
|
15
|
-
|
|
16
|
-
/** Global Cursor commands directory */
|
|
17
|
-
const GLOBAL_CURSOR_DIR = path.join(os.homedir(), '.cursor', 'commands', 'incspec');
|
|
18
|
-
|
|
19
|
-
/** Claude commands source directory (user local) */
|
|
20
|
-
const CLAUDE_COMMANDS_DIR = path.join(os.homedir(), '.claude', 'commands', 'ai-increment');
|
|
11
|
+
import { INCSPEC_DIR } from './config.mjs';
|
|
21
12
|
|
|
22
13
|
/** Built-in templates directory */
|
|
23
14
|
const TEMPLATES_DIR = fileURLToPath(new URL('../templates/commands', import.meta.url));
|
|
24
15
|
|
|
25
16
|
/**
|
|
26
|
-
*
|
|
17
|
+
* IDE-specific configurations
|
|
18
|
+
*/
|
|
19
|
+
const IDE_CONFIGS = {
|
|
20
|
+
cursor: {
|
|
21
|
+
name: 'Cursor',
|
|
22
|
+
projectDir: '.cursor/commands/incspec',
|
|
23
|
+
globalDir: path.join(os.homedir(), '.cursor', 'commands', 'incspec'),
|
|
24
|
+
fallbackDirs: [
|
|
25
|
+
// Support old ai-incremental-coding commands for backward compatibility
|
|
26
|
+
path.join(os.homedir(), '.claude', 'commands', 'ai-increment'),
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
claude: {
|
|
30
|
+
name: 'Claude Code',
|
|
31
|
+
projectDir: '.claude/commands/incspec',
|
|
32
|
+
globalDir: path.join(os.homedir(), '.claude', 'commands', 'incspec'),
|
|
33
|
+
fallbackDirs: [],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Command mapping (shared across all IDEs)
|
|
27
39
|
*/
|
|
28
|
-
const COMMAND_MAP = [
|
|
40
|
+
export const COMMAND_MAP = [
|
|
29
41
|
{
|
|
30
42
|
source: 'analyze-codeflow.md',
|
|
31
43
|
target: 'inc-analyze.md',
|
|
@@ -78,80 +90,38 @@ const COMMAND_MAP = [
|
|
|
78
90
|
];
|
|
79
91
|
|
|
80
92
|
/**
|
|
81
|
-
*
|
|
82
|
-
* Templates already contain complete Cursor command format with CLI sync instructions,
|
|
83
|
-
* so we just pass through the source content directly.
|
|
84
|
-
* @param {Object} cmd - Command definition
|
|
85
|
-
* @param {string} sourceContent - Original template content (already complete)
|
|
86
|
-
* @param {string} projectRoot - Project root path (optional, unused)
|
|
87
|
-
* @returns {string}
|
|
88
|
-
*/
|
|
89
|
-
function generateCursorCommand(cmd, sourceContent, projectRoot = null) {
|
|
90
|
-
// Templates are already complete Cursor commands, pass through directly
|
|
91
|
-
return sourceContent;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Get source file path with fallback to user local commands
|
|
93
|
+
* Get source file path with optional fallback directories
|
|
96
94
|
* @param {string} fileName - Source file name
|
|
95
|
+
* @param {Array<string>} fallbackDirs - Optional fallback directories to search
|
|
97
96
|
* @returns {string|null} Path to source file or null if not found
|
|
98
97
|
*/
|
|
99
|
-
function getSourcePath(fileName) {
|
|
98
|
+
function getSourcePath(fileName, fallbackDirs = []) {
|
|
100
99
|
// Try built-in templates first (preferred)
|
|
101
100
|
const templatePath = path.join(TEMPLATES_DIR, fileName);
|
|
102
101
|
if (fs.existsSync(templatePath)) {
|
|
103
102
|
return templatePath;
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
//
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
// Try fallback directories
|
|
106
|
+
for (const dir of fallbackDirs) {
|
|
107
|
+
const fallbackPath = path.join(dir, fileName);
|
|
108
|
+
if (fs.existsSync(fallbackPath)) {
|
|
109
|
+
return fallbackPath;
|
|
110
|
+
}
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
return null;
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
/**
|
|
116
|
-
* Generate
|
|
117
|
-
* @param {string} projectRoot - Optional project root for customization
|
|
117
|
+
* Generate utility commands content
|
|
118
118
|
* @returns {Array<{name: string, content: string}>}
|
|
119
119
|
*/
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
let content;
|
|
127
|
-
if (sourcePath) {
|
|
128
|
-
const sourceContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
129
|
-
content = generateCursorCommand(cmd, sourceContent, projectRoot);
|
|
130
|
-
} else {
|
|
131
|
-
// Generate placeholder if source doesn't exist
|
|
132
|
-
content = `---
|
|
133
|
-
description: ${cmd.description}
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
# ${cmd.label}
|
|
137
|
-
|
|
138
|
-
> 源命令文件不存在: ${cmd.source}
|
|
139
|
-
> 请确保已正确安装 incspec-cli 或 ai-incremental-coding 技能。
|
|
140
|
-
|
|
141
|
-
请参考 incspec 文档手动执行此步骤。
|
|
142
|
-
`;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
commands.push({
|
|
146
|
-
name: cmd.target,
|
|
147
|
-
content,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Add utility commands
|
|
152
|
-
commands.push({
|
|
153
|
-
name: 'inc-status.md',
|
|
154
|
-
content: `---
|
|
120
|
+
function generateUtilityCommands() {
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
name: 'inc-status.md',
|
|
124
|
+
content: `---
|
|
155
125
|
description: [incspec] 查看当前工作流状态
|
|
156
126
|
---
|
|
157
127
|
|
|
@@ -169,11 +139,10 @@ incspec status
|
|
|
169
139
|
cat ${INCSPEC_DIR}/WORKFLOW.md
|
|
170
140
|
\`\`\`
|
|
171
141
|
`,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
content: `---
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'inc-help.md',
|
|
145
|
+
content: `---
|
|
177
146
|
description: [incspec] 显示帮助信息
|
|
178
147
|
---
|
|
179
148
|
|
|
@@ -225,25 +194,79 @@ ${INCSPEC_DIR}/
|
|
|
225
194
|
└── archives/ # 历史归档 (YYYY-MM/{module}/)
|
|
226
195
|
\`\`\`
|
|
227
196
|
`,
|
|
228
|
-
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Generate all IDE commands
|
|
203
|
+
* @param {string} ide - IDE type ('cursor' or 'claude')
|
|
204
|
+
* @param {Object} options - Additional options
|
|
205
|
+
* @param {string} options.projectRoot - Optional project root for customization
|
|
206
|
+
* @returns {Array<{name: string, content: string}>}
|
|
207
|
+
*/
|
|
208
|
+
export function generateCommands(ide, { projectRoot = null } = {}) {
|
|
209
|
+
const config = IDE_CONFIGS[ide];
|
|
210
|
+
if (!config) {
|
|
211
|
+
throw new Error(`Unknown IDE: ${ide}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const commands = [];
|
|
215
|
+
|
|
216
|
+
// Generate workflow commands
|
|
217
|
+
for (const cmd of COMMAND_MAP) {
|
|
218
|
+
const sourcePath = getSourcePath(cmd.source, config.fallbackDirs);
|
|
219
|
+
|
|
220
|
+
let content;
|
|
221
|
+
if (sourcePath) {
|
|
222
|
+
content = fs.readFileSync(sourcePath, 'utf-8');
|
|
223
|
+
} else {
|
|
224
|
+
// Generate placeholder if source doesn't exist
|
|
225
|
+
content = `---
|
|
226
|
+
description: ${cmd.description}
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
# ${cmd.label}
|
|
230
|
+
|
|
231
|
+
> 源命令文件不存在: ${cmd.source}
|
|
232
|
+
> 请确保已正确安装 incspec-cli。
|
|
233
|
+
|
|
234
|
+
请参考 incspec 文档手动执行此步骤。
|
|
235
|
+
`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
commands.push({
|
|
239
|
+
name: cmd.target,
|
|
240
|
+
content,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Add utility commands
|
|
245
|
+
commands.push(...generateUtilityCommands());
|
|
229
246
|
|
|
230
247
|
return commands;
|
|
231
248
|
}
|
|
232
249
|
|
|
233
250
|
/**
|
|
234
251
|
* Sync commands to project directory
|
|
235
|
-
* @param {string}
|
|
252
|
+
* @param {string} ide - IDE type ('cursor' or 'claude')
|
|
253
|
+
* @param {string} projectRoot - Project root path
|
|
236
254
|
* @returns {number} Number of files written
|
|
237
255
|
*/
|
|
238
|
-
export function syncToProject(projectRoot) {
|
|
239
|
-
const
|
|
256
|
+
export function syncToProject(ide, projectRoot) {
|
|
257
|
+
const config = IDE_CONFIGS[ide];
|
|
258
|
+
if (!config) {
|
|
259
|
+
throw new Error(`Unknown IDE: ${ide}`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const targetDir = path.join(projectRoot, config.projectDir);
|
|
240
263
|
|
|
241
264
|
// Create directory
|
|
242
265
|
if (!fs.existsSync(targetDir)) {
|
|
243
266
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
244
267
|
}
|
|
245
268
|
|
|
246
|
-
const commands =
|
|
269
|
+
const commands = generateCommands(ide, { projectRoot });
|
|
247
270
|
|
|
248
271
|
for (const cmd of commands) {
|
|
249
272
|
const filePath = path.join(targetDir, cmd.name);
|
|
@@ -254,19 +277,27 @@ export function syncToProject(projectRoot) {
|
|
|
254
277
|
}
|
|
255
278
|
|
|
256
279
|
/**
|
|
257
|
-
* Sync commands to global
|
|
280
|
+
* Sync commands to global directory
|
|
281
|
+
* @param {string} ide - IDE type ('cursor' or 'claude')
|
|
258
282
|
* @returns {number} Number of files written
|
|
259
283
|
*/
|
|
260
|
-
export function syncToGlobal() {
|
|
284
|
+
export function syncToGlobal(ide) {
|
|
285
|
+
const config = IDE_CONFIGS[ide];
|
|
286
|
+
if (!config) {
|
|
287
|
+
throw new Error(`Unknown IDE: ${ide}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const targetDir = config.globalDir;
|
|
291
|
+
|
|
261
292
|
// Create directory
|
|
262
|
-
if (!fs.existsSync(
|
|
263
|
-
fs.mkdirSync(
|
|
293
|
+
if (!fs.existsSync(targetDir)) {
|
|
294
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
264
295
|
}
|
|
265
296
|
|
|
266
|
-
const commands =
|
|
297
|
+
const commands = generateCommands(ide);
|
|
267
298
|
|
|
268
299
|
for (const cmd of commands) {
|
|
269
|
-
const filePath = path.join(
|
|
300
|
+
const filePath = path.join(targetDir, cmd.name);
|
|
270
301
|
fs.writeFileSync(filePath, cmd.content, 'utf-8');
|
|
271
302
|
}
|
|
272
303
|
|
|
@@ -274,16 +305,43 @@ export function syncToGlobal() {
|
|
|
274
305
|
}
|
|
275
306
|
|
|
276
307
|
/**
|
|
277
|
-
* Check if
|
|
278
|
-
* @param {string}
|
|
308
|
+
* Check if IDE commands exist
|
|
309
|
+
* @param {string} ide - IDE type ('cursor' or 'claude')
|
|
310
|
+
* @param {string} projectRoot - Project root path
|
|
279
311
|
* @returns {{project: boolean, global: boolean}}
|
|
280
312
|
*/
|
|
281
|
-
export function
|
|
282
|
-
const
|
|
283
|
-
|
|
313
|
+
export function checkCommands(ide, projectRoot) {
|
|
314
|
+
const config = IDE_CONFIGS[ide];
|
|
315
|
+
if (!config) {
|
|
316
|
+
throw new Error(`Unknown IDE: ${ide}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const projectDir = path.join(projectRoot, config.projectDir);
|
|
320
|
+
const globalDir = config.globalDir;
|
|
284
321
|
|
|
285
322
|
return {
|
|
286
323
|
project: fs.existsSync(projectDir) && fs.readdirSync(projectDir).length > 0,
|
|
287
324
|
global: fs.existsSync(globalDir) && fs.readdirSync(globalDir).length > 0,
|
|
288
325
|
};
|
|
289
326
|
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Get IDE configuration
|
|
330
|
+
* @param {string} ide - IDE type ('cursor' or 'claude')
|
|
331
|
+
* @returns {Object} IDE configuration
|
|
332
|
+
*/
|
|
333
|
+
export function getIDEConfig(ide) {
|
|
334
|
+
const config = IDE_CONFIGS[ide];
|
|
335
|
+
if (!config) {
|
|
336
|
+
throw new Error(`Unknown IDE: ${ide}`);
|
|
337
|
+
}
|
|
338
|
+
return config;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Get list of supported IDEs
|
|
343
|
+
* @returns {Array<string>}
|
|
344
|
+
*/
|
|
345
|
+
export function getSupportedIDEs() {
|
|
346
|
+
return Object.keys(IDE_CONFIGS);
|
|
347
|
+
}
|