@localsummer/incspec 0.0.4 → 0.0.5
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 +19 -0
- package/commands/analyze.mjs +95 -0
- package/commands/help.mjs +2 -1
- package/package.json +1 -1
- package/templates/AGENTS.md +5 -2
- package/templates/cursor-commands/analyze-codeflow.md +16 -0
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
# IncSpec
|
|
12
12
|
|
|
13
|
+

|
|
14
|
+
|
|
13
15
|
IncSpec 通过**增量规范驱动开发**让人类与 AI 编程助手保持一致 - 这是一个 6+1 步工作流(6 步开发 + 归档),在修改代码前先捕获代码流程基线。**无需 API 密钥。**
|
|
14
16
|
|
|
15
17
|
## 为什么选择 IncSpec?
|
|
@@ -89,6 +91,8 @@ AI 编程助手在处理复杂前端代码库时常常力不从心,因为 API
|
|
|
89
91
|
└───────────┘
|
|
90
92
|
```
|
|
91
93
|
|
|
94
|
+
> **提示**: 如果已有基准报告,可使用 `incspec analyze --baseline=<file>` 直接跳过分析步骤,快速进入后续工作流。
|
|
95
|
+
|
|
92
96
|
## 快速开始
|
|
93
97
|
|
|
94
98
|
### 支持的 AI 工具
|
|
@@ -124,6 +128,9 @@ AI 编程助手在处理复杂前端代码库时常常力不从心,因为 API
|
|
|
124
128
|
#### 步骤 1:安装 CLI
|
|
125
129
|
|
|
126
130
|
```bash
|
|
131
|
+
# 从 Npm 安装(推荐)
|
|
132
|
+
npm install -g @localsummer/incspec
|
|
133
|
+
|
|
127
134
|
# 从 GitHub 克隆并安装
|
|
128
135
|
git clone https://github.com/localSummer/IncSpec.git
|
|
129
136
|
cd IncSpec
|
|
@@ -186,6 +193,17 @@ $ incspec status # 检查当前工作流状态
|
|
|
186
193
|
$ incspec analyze src/views/Home --complete # 标记步骤 1 完成
|
|
187
194
|
```
|
|
188
195
|
|
|
196
|
+
**使用现有基准报告** (跳过分析):
|
|
197
|
+
|
|
198
|
+
如果已有基准报告文件,可直接使用,跳过分析步骤:
|
|
199
|
+
```bash
|
|
200
|
+
$ incspec analyze --baseline=home-baseline-v1.md # 直接使用现有基准报告
|
|
201
|
+
```
|
|
202
|
+
- 自动搜索 `baselines/` 和 `archives/` 目录
|
|
203
|
+
- 若文件在归档目录,自动移动到 `baselines/` 目录
|
|
204
|
+
- 模块名自动从文件名推断 (`xxx-baseline-vN.md` -> `xxx`)
|
|
205
|
+
- 可通过 `--module` 覆盖模块名
|
|
206
|
+
|
|
189
207
|
#### 2. 收集结构化需求
|
|
190
208
|
|
|
191
209
|
定义你想要的变更:
|
|
@@ -422,6 +440,7 @@ incspec help <command> # 显示特定命令帮助
|
|
|
422
440
|
incspec analyze <source-path> [--module=name] # 别名:a
|
|
423
441
|
incspec a src/views/Home --module=home
|
|
424
442
|
incspec analyze src/views/Home --complete -o baselines/home-baseline-v1.md
|
|
443
|
+
incspec analyze --baseline=home-baseline-v1.md # 使用现有基准(自动从归档恢复)
|
|
425
444
|
|
|
426
445
|
# 步骤 2:收集结构化需求
|
|
427
446
|
incspec collect-req # 别名:cr
|
package/commands/analyze.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs';
|
|
6
7
|
import {
|
|
7
8
|
ensureInitialized,
|
|
8
9
|
INCSPEC_DIR,
|
|
@@ -32,6 +33,49 @@ import {
|
|
|
32
33
|
|
|
33
34
|
const STEP_NUMBER = 1;
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Search for a file in the archives directory
|
|
38
|
+
* @param {string} projectRoot - Project root path
|
|
39
|
+
* @param {string} fileName - File name to search
|
|
40
|
+
* @returns {string|null} - Full path if found, null otherwise
|
|
41
|
+
*/
|
|
42
|
+
function findInArchives(projectRoot, fileName) {
|
|
43
|
+
const archivesDir = path.join(projectRoot, INCSPEC_DIR, DIRS.archives);
|
|
44
|
+
if (!fs.existsSync(archivesDir)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Recursive search: archives/ -> YYYY-MM/ -> [module/] -> files
|
|
49
|
+
const monthDirs = fs.readdirSync(archivesDir, { withFileTypes: true })
|
|
50
|
+
.filter(d => d.isDirectory())
|
|
51
|
+
.map(d => d.name)
|
|
52
|
+
.sort()
|
|
53
|
+
.reverse(); // Prioritize recent months
|
|
54
|
+
|
|
55
|
+
for (const month of monthDirs) {
|
|
56
|
+
const monthPath = path.join(archivesDir, month);
|
|
57
|
+
|
|
58
|
+
// Check files directly in month directory
|
|
59
|
+
const directPath = path.join(monthPath, fileName);
|
|
60
|
+
if (fs.existsSync(directPath)) {
|
|
61
|
+
return directPath;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check module subdirectories
|
|
65
|
+
const subDirs = fs.readdirSync(monthPath, { withFileTypes: true })
|
|
66
|
+
.filter(d => d.isDirectory());
|
|
67
|
+
|
|
68
|
+
for (const subDir of subDirs) {
|
|
69
|
+
const subPath = path.join(monthPath, subDir.name, fileName);
|
|
70
|
+
if (fs.existsSync(subPath)) {
|
|
71
|
+
return subPath;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
35
79
|
/**
|
|
36
80
|
* Execute analyze command
|
|
37
81
|
* @param {Object} ctx - Command context
|
|
@@ -42,6 +86,57 @@ export async function analyzeCommand(ctx) {
|
|
|
42
86
|
// Ensure initialized
|
|
43
87
|
const projectRoot = ensureInitialized(cwd);
|
|
44
88
|
|
|
89
|
+
// Handle --baseline option: use existing baseline report
|
|
90
|
+
if (options.baseline) {
|
|
91
|
+
const baselineFile = typeof options.baseline === 'string' ? options.baseline : '';
|
|
92
|
+
if (!baselineFile) {
|
|
93
|
+
printError('请指定基准报告文件名');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const baselinesDir = path.join(projectRoot, INCSPEC_DIR, DIRS.baselines);
|
|
98
|
+
const baselinePath = path.join(baselinesDir, baselineFile);
|
|
99
|
+
let fromArchive = false;
|
|
100
|
+
|
|
101
|
+
// 1. First check baselines directory
|
|
102
|
+
if (!fs.existsSync(baselinePath)) {
|
|
103
|
+
// 2. Search in archives directory
|
|
104
|
+
const archivePath = findInArchives(projectRoot, baselineFile);
|
|
105
|
+
if (!archivePath) {
|
|
106
|
+
printError(`基准文件不存在: ${baselineFile}`);
|
|
107
|
+
printInfo('已搜索 baselines/ 和 archives/ 目录');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 3. Move to baselines directory
|
|
112
|
+
fs.renameSync(archivePath, baselinePath);
|
|
113
|
+
fromArchive = true;
|
|
114
|
+
printInfo(`已从归档恢复: ${path.relative(projectRoot, archivePath)}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Infer module name from filename (xxx-baseline-vN.md -> xxx)
|
|
118
|
+
let moduleName = typeof options.module === 'string' ? options.module : '';
|
|
119
|
+
if (!moduleName) {
|
|
120
|
+
const match = baselineFile.match(/^(.+)-baseline-v\d+\.md$/);
|
|
121
|
+
moduleName = match ? match[1] : path.basename(baselineFile, '.md');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Handle workflow state
|
|
125
|
+
let workflow = readWorkflow(projectRoot);
|
|
126
|
+
if (!workflow?.currentWorkflow) {
|
|
127
|
+
const workflowName = typeof options.workflow === 'string' ? options.workflow : `analyze-${moduleName}`;
|
|
128
|
+
workflow = startWorkflow(projectRoot, workflowName);
|
|
129
|
+
printSuccess(`已创建新工作流: ${workflowName}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Mark step as completed
|
|
133
|
+
updateStep(projectRoot, STEP_NUMBER, STATUS.COMPLETED, baselineFile);
|
|
134
|
+
print('');
|
|
135
|
+
printSuccess(`已使用现有基准报告: ${baselineFile}`);
|
|
136
|
+
printInfo(`运行 'incspec status' 查看进度`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
45
140
|
// Get source path
|
|
46
141
|
let sourcePath = args[0];
|
|
47
142
|
if (!sourcePath) {
|
package/commands/help.mjs
CHANGED
|
@@ -34,12 +34,13 @@ const COMMANDS = {
|
|
|
34
34
|
description: '显示当前工作流状态',
|
|
35
35
|
},
|
|
36
36
|
analyze: {
|
|
37
|
-
usage: 'incspec analyze <source-path> [--module=name]',
|
|
37
|
+
usage: 'incspec analyze <source-path> [--module=name] [--baseline=file]',
|
|
38
38
|
aliases: ['a'],
|
|
39
39
|
description: '步骤1: 分析代码流程,生成基线快照',
|
|
40
40
|
options: [
|
|
41
41
|
['-m, --module=<name>', '指定模块名称'],
|
|
42
42
|
['-w, --workflow=<name>', '指定工作流名称(避免交互提示)'],
|
|
43
|
+
['-b, --baseline=<file>', '使用现有基准报告(自动从归档恢复)'],
|
|
43
44
|
['--complete', '标记步骤完成'],
|
|
44
45
|
['-o, --output=<file>', '完成时指定输出文件'],
|
|
45
46
|
],
|
package/package.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -41,10 +41,13 @@ AI 编码助手使用 IncSpec 进行增量规格驱动开发的操作指南。
|
|
|
41
41
|
|
|
42
42
|
### 步骤 1: 分析代码工作流
|
|
43
43
|
|
|
44
|
-
**命令**: `incspec analyze <source-path> [--module=name]`
|
|
44
|
+
**命令**: `incspec analyze <source-path> [--module=name] [--baseline=file]`
|
|
45
45
|
|
|
46
46
|
**目的**: 生成包含 API 调用时序图和依赖关系图的基线快照。
|
|
47
47
|
|
|
48
|
+
**选项**:
|
|
49
|
+
- `--baseline=<file>`: 使用现有基准报告,自动从 baselines/ 或 archives/ 目录恢复
|
|
50
|
+
|
|
48
51
|
**输出**: `incspec/baselines/{module}-baseline-v{n}.md`
|
|
49
52
|
|
|
50
53
|
**关键交付物**:
|
|
@@ -215,7 +218,7 @@ incspec list -l # 长格式(含时间戳)
|
|
|
215
218
|
incspec list -a # 包含归档
|
|
216
219
|
|
|
217
220
|
# 6步工作流
|
|
218
|
-
incspec analyze <path> [--module=name]
|
|
221
|
+
incspec analyze <path> [--module=name] [--baseline=file] # 步骤1: 基线分析
|
|
219
222
|
incspec collect-req / cr # 步骤2: 需求收集
|
|
220
223
|
incspec collect-dep / cd # 步骤3: 依赖收集
|
|
221
224
|
incspec design [--feature=name] / d # 步骤4: 增量设计
|
|
@@ -18,6 +18,22 @@ incspec analyze <source_path> --module=<module> --workflow=analyze-<module>
|
|
|
18
18
|
incspec analyze <source_path> --module=<module> --workflow=analyze-<module> --complete --output=<output-file>
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
**使用现有基准报告** (跳过分析):
|
|
22
|
+
|
|
23
|
+
若已有基准报告文件,可直接使用:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
incspec analyze --baseline=<baseline-file> [--module=<module>] [--workflow=<workflow>]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
说明:
|
|
30
|
+
- `<baseline-file>` 文件名,自动搜索 baselines/ 和 archives/ 目录
|
|
31
|
+
- 若文件在归档目录,自动移动到 baselines/ 目录
|
|
32
|
+
- `<module>` 默认从文件名推断 (xxx-baseline-vN.md -> xxx)
|
|
33
|
+
- `<workflow>` 默认为 analyze-<module>
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
21
37
|
说明:
|
|
22
38
|
- `<module>` 默认为 source_path 最后一级目录名,若用户显式指定模块名则使用该值
|
|
23
39
|
- `<output-file>` 必须与最终写入的文件名一致
|