jvibe 1.0.7 → 1.0.9

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/lib/migrate.js CHANGED
@@ -6,6 +6,7 @@
6
6
  const fs = require('fs-extra');
7
7
  const path = require('path');
8
8
  const chalk = require('chalk');
9
+ const pkg = require('../package.json');
9
10
 
10
11
  // 尝试加载迁移配置(可能不存在)
11
12
  let migrationsConfig = null;
@@ -24,6 +25,231 @@ const CORE_DOC_RENAMES = [
24
25
  { from: '附加材料.md', to: 'Appendix.md' }
25
26
  ];
26
27
 
28
+ async function listMarkdownFiles(dir) {
29
+ const entries = await fs.readdir(dir, { withFileTypes: true });
30
+ const files = [];
31
+ for (const entry of entries) {
32
+ const fullPath = path.join(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ files.push(...await listMarkdownFiles(fullPath));
35
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
36
+ files.push(fullPath);
37
+ }
38
+ }
39
+ return files;
40
+ }
41
+
42
+ function escapeRegExp(value) {
43
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
44
+ }
45
+
46
+ function normalizeHeadingText(text) {
47
+ return text
48
+ .replace(/^[\d.\-、()]+\s*/g, '')
49
+ .replace(/\s*[((][^))]*[))]\s*$/g, '')
50
+ .replace(/^[\u{1F300}-\u{1FAFF}\u{2600}-\u{26FF}]+\s*/gu, '')
51
+ .replace(/`/g, '')
52
+ .replace(/\s+/g, ' ')
53
+ .trim()
54
+ .toLowerCase();
55
+ }
56
+
57
+ function extractHeadings(content, levels) {
58
+ const headings = new Set();
59
+ const lines = content.split(/\r?\n/);
60
+ for (const line of lines) {
61
+ const match = line.match(/^(#{2,6})\s+(.+)$/);
62
+ if (!match) {
63
+ continue;
64
+ }
65
+ const level = match[1].length;
66
+ if (!levels.includes(level)) {
67
+ continue;
68
+ }
69
+ const normalized = normalizeHeadingText(match[2]);
70
+ if (normalized) {
71
+ headings.add(normalized);
72
+ }
73
+ }
74
+ return headings;
75
+ }
76
+
77
+ function extractFeatureFieldsFromTemplate(content) {
78
+ const lines = content.split(/\r?\n/);
79
+ let startIndex = -1;
80
+ for (let i = 0; i < lines.length; i += 1) {
81
+ if (/^##\s+F-\d+/m.test(lines[i])) {
82
+ startIndex = i;
83
+ break;
84
+ }
85
+ }
86
+ if (startIndex === -1) {
87
+ return [];
88
+ }
89
+ let endIndex = lines.length;
90
+ for (let i = startIndex + 1; i < lines.length; i += 1) {
91
+ if (/^##\s+F-\d+/m.test(lines[i])) {
92
+ endIndex = i;
93
+ break;
94
+ }
95
+ }
96
+ const fields = new Set();
97
+ for (const line of lines.slice(startIndex, endIndex)) {
98
+ const match = line.match(/^\*\*(.+?)\*\*/);
99
+ if (match) {
100
+ const fieldName = match[1].trim();
101
+ if (fieldName) {
102
+ fields.add(fieldName);
103
+ }
104
+ }
105
+ }
106
+ return [...fields];
107
+ }
108
+
109
+ function extractFeatureBlocks(content) {
110
+ const lines = content.split(/\r?\n/);
111
+ const blocks = [];
112
+ let startIndex = -1;
113
+ for (let i = 0; i < lines.length; i += 1) {
114
+ if (/^##\s+F-\d+/m.test(lines[i])) {
115
+ if (startIndex >= 0) {
116
+ blocks.push(lines.slice(startIndex, i).join('\n'));
117
+ }
118
+ startIndex = i;
119
+ }
120
+ }
121
+ if (startIndex >= 0) {
122
+ blocks.push(lines.slice(startIndex).join('\n'));
123
+ }
124
+ return blocks;
125
+ }
126
+
127
+ async function findFirstExistingFile(projectDir, candidates) {
128
+ for (const candidate of candidates) {
129
+ const fullPath = path.join(projectDir, candidate);
130
+ if (await fs.pathExists(fullPath)) {
131
+ return fullPath;
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+
137
+ async function compareDocsToTemplate(projectDir) {
138
+ const result = {
139
+ applied: false,
140
+ required: false,
141
+ files: [],
142
+ missingFields: [],
143
+ changes: []
144
+ };
145
+
146
+ const templateCoreDir = path.resolve(__dirname, '..', 'template', 'docs', 'core');
147
+ if (!await fs.pathExists(templateCoreDir)) {
148
+ return result;
149
+ }
150
+ result.applied = true;
151
+
152
+ const docCandidates = {
153
+ 'Standards.md': [
154
+ 'docs/core/Standards.md',
155
+ 'docs/Standards.md',
156
+ 'docs/core/规范文档.md',
157
+ 'docs/规范文档.md'
158
+ ],
159
+ 'Project.md': [
160
+ 'docs/core/Project.md',
161
+ 'docs/Project.md',
162
+ 'docs/core/项目文档.md',
163
+ 'docs/项目文档.md'
164
+ ],
165
+ 'Feature-List.md': [
166
+ 'docs/core/Feature-List.md',
167
+ 'docs/Feature-List.md',
168
+ 'docs/core/功能清单.md',
169
+ 'docs/功能清单.md'
170
+ ],
171
+ 'Appendix.md': [
172
+ 'docs/core/Appendix.md',
173
+ 'docs/Appendix.md',
174
+ 'docs/core/附加材料.md',
175
+ 'docs/附加材料.md'
176
+ ]
177
+ };
178
+
179
+ for (const [templateName, candidates] of Object.entries(docCandidates)) {
180
+ const templatePath = path.join(templateCoreDir, templateName);
181
+ if (!await fs.pathExists(templatePath)) {
182
+ continue;
183
+ }
184
+
185
+ const projectPath = await findFirstExistingFile(projectDir, candidates);
186
+ if (!projectPath) {
187
+ continue;
188
+ }
189
+
190
+ const templateContent = await fs.readFile(templatePath, 'utf-8');
191
+ const projectContent = await fs.readFile(projectPath, 'utf-8');
192
+
193
+ if (templateName === 'Feature-List.md') {
194
+ const requiredFields = extractFeatureFieldsFromTemplate(templateContent);
195
+ if (requiredFields.length === 0) {
196
+ continue;
197
+ }
198
+ const featureBlocks = extractFeatureBlocks(projectContent);
199
+ if (featureBlocks.length === 0) {
200
+ continue;
201
+ }
202
+ const missingFields = new Set();
203
+ for (const block of featureBlocks) {
204
+ for (const field of requiredFields) {
205
+ const pattern = new RegExp(`\\*\\*${escapeRegExp(field)}\\*\\*\\s*(:|:)?`, 'm');
206
+ if (!pattern.test(block)) {
207
+ missingFields.add(field);
208
+ }
209
+ }
210
+ }
211
+ if (missingFields.size > 0) {
212
+ result.required = true;
213
+ result.files.push(projectPath);
214
+ const missingList = [...missingFields];
215
+ result.missingFields.push(...missingList);
216
+ result.changes.push({
217
+ file: path.relative(projectDir, projectPath),
218
+ type: 'missing_fields',
219
+ fields: missingList,
220
+ description: `功能清单与模板字段不一致,缺少:${missingList.join('、')}`
221
+ });
222
+ }
223
+ continue;
224
+ }
225
+
226
+ const templateHeadings = extractHeadings(templateContent, [2]);
227
+ const projectHeadings = extractHeadings(projectContent, [2]);
228
+ const missingSections = [];
229
+ for (const heading of templateHeadings) {
230
+ if (!projectHeadings.has(heading)) {
231
+ missingSections.push(heading);
232
+ }
233
+ }
234
+ if (missingSections.length > 0) {
235
+ result.required = true;
236
+ result.files.push(projectPath);
237
+ result.changes.push({
238
+ file: path.relative(projectDir, projectPath),
239
+ type: 'missing_sections',
240
+ sections: missingSections,
241
+ description: `文档缺少模板中的章节:${missingSections.join('、')}`
242
+ });
243
+ }
244
+ }
245
+
246
+ if (result.files.length > 0) {
247
+ result.files = [...new Set(result.files)];
248
+ }
249
+
250
+ return result;
251
+ }
252
+
27
253
  /**
28
254
  * 版本检测结果
29
255
  * @typedef {Object} VersionInfo
@@ -222,96 +448,56 @@ async function checkContentMigration(projectDir, currentVersion) {
222
448
  changes: []
223
449
  };
224
450
 
225
- // 检查功能清单是否缺少新版本字段
226
- const featureListPaths = [
227
- path.join(projectDir, 'docs/core/Feature-List.md'),
228
- path.join(projectDir, 'docs/Feature-List.md'),
229
- path.join(projectDir, 'docs/core/功能清单.md'),
230
- path.join(projectDir, 'docs/功能清单.md')
231
- ];
232
-
233
- for (const featurePath of featureListPaths) {
234
- if (!await fs.pathExists(featurePath)) {
235
- continue;
236
- }
237
-
238
- const content = await fs.readFile(featurePath, 'utf-8');
239
-
240
- // 检查是否有功能条目
241
- const hasFeatures = /^## F-\d+/m.test(content);
242
- if (!hasFeatures) {
243
- continue;
244
- }
245
-
246
- // 检查新版本字段是否存在
247
- const newFields = [
248
- { field: '优先级', pattern: /\*\*优先级\*\*:/m },
249
- { field: '预估工时', pattern: /\*\*预估工时\*\*:/m },
250
- { field: '关联模块', pattern: /\*\*关联模块\*\*:/m }
251
- ];
252
-
253
- const missingFields = [];
254
- for (const { field, pattern } of newFields) {
255
- if (!pattern.test(content)) {
256
- missingFields.push(field);
451
+ // 检查是否存在旧的核心文档名称引用(需要 AI 更新链接/引用)
452
+ const docsDir = path.join(projectDir, 'docs');
453
+ if (await fs.pathExists(docsDir)) {
454
+ const mdFiles = await listMarkdownFiles(docsDir);
455
+ const filesWithLegacyRefs = [];
456
+
457
+ for (const filePath of mdFiles) {
458
+ const content = await fs.readFile(filePath, 'utf-8');
459
+ const hasLegacyRef = LEGACY_CORE_DOCS.some(name => content.includes(name));
460
+ if (hasLegacyRef) {
461
+ filesWithLegacyRefs.push(filePath);
257
462
  }
258
463
  }
259
464
 
260
- if (missingFields.length > 0) {
465
+ if (filesWithLegacyRefs.length > 0) {
261
466
  result.required = true;
262
- result.files.push(featurePath);
263
- result.missingFields.push(...missingFields);
467
+ for (const filePath of filesWithLegacyRefs) {
468
+ if (!result.files.includes(filePath)) {
469
+ result.files.push(filePath);
470
+ }
471
+ }
264
472
  result.changes.push({
265
- file: path.relative(projectDir, featurePath),
266
- type: 'missing_fields',
267
- fields: missingFields,
268
- description: `功能清单缺少新版本字段:${missingFields.join('、')}`
473
+ file: 'docs/**',
474
+ type: 'legacy_doc_refs',
475
+ description: '文档内引用仍使用旧中文名称,需更新为英文命名(规范文档/项目文档/功能清单/附加材料)'
269
476
  });
270
477
  }
271
478
  }
272
479
 
273
- // 检查项目文档是否缺少新章节
274
- const projectDocPaths = [
275
- path.join(projectDir, 'docs/core/Project.md'),
276
- path.join(projectDir, 'docs/Project.md'),
277
- path.join(projectDir, 'docs/core/项目文档.md'),
278
- path.join(projectDir, 'docs/项目文档.md')
279
- ];
280
-
281
- for (const docPath of projectDocPaths) {
282
- if (!await fs.pathExists(docPath)) {
283
- continue;
284
- }
285
-
286
- const content = await fs.readFile(docPath, 'utf-8');
287
-
288
- // 检查新版本章节是否存在
289
- const newSections = [
290
- { section: '环境配置', pattern: /^## \d+\. 环境配置/m }
291
- ];
292
-
293
- const missingSections = [];
294
- for (const { section, pattern } of newSections) {
295
- if (!pattern.test(content)) {
296
- missingSections.push(section);
297
- }
480
+ const templateComparison = await compareDocsToTemplate(projectDir);
481
+ if (templateComparison.applied) {
482
+ if (templateComparison.required) {
483
+ result.required = true;
484
+ result.files = [...new Set([...result.files, ...templateComparison.files])];
485
+ result.missingFields.push(...templateComparison.missingFields);
486
+ result.changes.push(...templateComparison.changes);
298
487
  }
299
-
300
- if (missingSections.length > 0) {
488
+ const latestVersion = pkg.version || null;
489
+ if (latestVersion && currentVersion && currentVersion !== latestVersion) {
301
490
  result.required = true;
302
- if (!result.files.includes(docPath)) {
303
- result.files.push(docPath);
304
- }
305
491
  result.changes.push({
306
- file: path.relative(projectDir, docPath),
307
- type: 'missing_sections',
308
- sections: missingSections,
309
- description: `项目文档缺少新版本章节:${missingSections.join('、')}`
492
+ file: 'docs/core/*.md',
493
+ type: 'rebuild',
494
+ description: '核心文档需要强制重构(以 template/docs/core 为准)'
310
495
  });
311
496
  }
497
+ return result;
312
498
  }
313
499
 
314
- // 使用迁移配置(如果存在)
500
+ // 模板不可用时,退回版本配置
315
501
  if (migrationsConfig && currentVersion) {
316
502
  const configResult = migrationsConfig.checkAIMigrationRequired(currentVersion);
317
503
  if (configResult.required) {
@@ -323,6 +509,18 @@ async function checkContentMigration(projectDir, currentVersion) {
323
509
  field: c.field,
324
510
  description: c.description
325
511
  })));
512
+ result.changes.push(...configResult.changes.modified.map(c => ({
513
+ file: c.file,
514
+ type: 'modified_field',
515
+ field: c.field || c.section,
516
+ description: c.description
517
+ })));
518
+ result.changes.push(...configResult.changes.renamed.map(c => ({
519
+ file: c.file,
520
+ type: 'renamed',
521
+ field: c.field,
522
+ description: c.description
523
+ })));
326
524
  }
327
525
  }
328
526
 
@@ -47,6 +47,28 @@ const MIGRATIONS = [
47
47
  },
48
48
  aiMigrationRequired: []
49
49
  },
50
+ {
51
+ version: '1.0.7',
52
+ description: '核心文档英文命名与引用更新',
53
+ changes: {
54
+ added: [],
55
+ modified: [],
56
+ removed: [],
57
+ renamed: [
58
+ {
59
+ file: 'docs/core/Standards.md',
60
+ field: 'core-docs-rename',
61
+ description: '核心文档更名为英文(Standards/Project/Feature-List/Appendix)并更新文档内引用'
62
+ }
63
+ ]
64
+ },
65
+ aiMigrationRequired: [
66
+ 'docs/core/Standards.md',
67
+ 'docs/core/Project.md',
68
+ 'docs/core/Feature-List.md',
69
+ 'docs/core/Appendix.md'
70
+ ]
71
+ },
50
72
  {
51
73
  version: '1.1.0',
52
74
  description: '文档格式增强',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jvibe",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "\u6587\u6863\u9a71\u52a8\u7684 AI \u8f85\u52a9\u5f00\u53d1\u7cfb\u7edf - Doc-driven AI-assisted development system for Claude Code",
5
5
  "main": "bin/jvibe.js",
6
6
  "bin": {
@@ -1,60 +1,94 @@
1
1
  # /JVibe:migrate - 智能文档迁移
2
2
 
3
- > 将旧版本 JVibe 文档智能迁移到新版本格式
3
+ > 以模板为唯一权威,对核心文档执行强制重构
4
4
 
5
5
  ## 触发场景
6
6
 
7
7
  - 用户运行 `jvibe upgrade` 后提示需要 AI 迁移
8
8
  - 用户主动运行 `/JVibe:migrate`
9
- - 检测到文档格式与当前模板不匹配
9
+ - 检测到版本升级或模板规则变更
10
10
 
11
11
  ## 工作流程
12
12
 
13
- ### 阶段 1:版本检测
13
+ ### 阶段 1:版本检测(仅参考)
14
14
 
15
- 1. 读取 `.claude/settings.json` 获取当前版本
16
- 2. 读取 `lib/migrations/index.js` 获取迁移配置(如果在 jvibe 包目录)
17
- 3. 或者读取以下文件判断版本特征:
15
+ 1. 读取 `.claude/settings.json` 获取当前版本(仅参考)
16
+ 2. 读取 `template/docs/core/*.md` 作为迁移模板
17
+ 3. 读取以下文件判断当前文档结构:
18
18
  - `docs/core/Feature-List.md` 或 `docs/Feature-List.md`
19
19
  - `docs/core/Project.md` 或 `docs/Project.md`
20
20
 
21
- ### 阶段 2:变更分析
22
-
23
- 根据版本差异,识别需要迁移的内容:
24
-
25
- #### 文档结构迁移(自动)
26
- - `docs/*.md` → `docs/core/*.md`
27
-
28
- #### 内容格式迁移(AI 介入)
29
- 检查以下变更:
30
-
31
- **Feature-List.md 可能的变更**:
32
- | 版本 | 变更 | 处理方式 |
33
- |------|------|----------|
34
- | 1.1.0 | 新增「优先级」字段 | AI 根据功能描述推断优先级 |
35
- | 1.1.0 | 新增「预估工时」字段 | AI 根据 TODO 复杂度估算 |
36
- | 1.1.0 | 新增「关联模块」字段 | AI 从项目文档中匹配模块 |
37
-
38
- **Project.md 可能的变更**:
39
- | 版本 | 变更 | 处理方式 |
40
- |------|------|----------|
41
- | 1.1.0 | 新增「环境配置」章节 | AI 扫描项目生成配置说明 |
21
+ ### 阶段 2:强制重构规划
22
+
23
+ 以 `template/docs/core/*.md` 为唯一权威,执行全量重构(覆盖 core 文档),不做局部补丁。
24
+
25
+ #### 重构策略(代码化)
26
+ ```yaml
27
+ mode: rebuild
28
+ template_root: template/docs/core
29
+ targets:
30
+ - docs/core/Standards.md
31
+ - docs/core/Project.md
32
+ - docs/core/Feature-List.md
33
+ - docs/core/Appendix.md
34
+ overwrite: true
35
+ preserve:
36
+ project_facts:
37
+ - architecture
38
+ - tech_stack
39
+ - modules
40
+ - endpoints
41
+ - data_models
42
+ - dependencies
43
+ - code_locations
44
+ features:
45
+ - id
46
+ - status
47
+ - name
48
+ - description
49
+ - todos
50
+ - existing_fields
51
+ appendix_entries:
52
+ - all_ids
53
+ conflict_policy:
54
+ rules: template_wins
55
+ data: project_wins
56
+ unmapped_content: append_to_last_section
57
+ placeholders: "[待填写]"
58
+ ```
42
59
 
43
- ### 阶段 3:执行迁移
60
+ ### 阶段 3:执行重构
44
61
 
45
62
  #### 3.1 备份原文件
46
63
 
47
64
  ```bash
48
65
  # 创建备份
66
+ cp docs/core/Standards.md docs/core/Standards.md.bak
49
67
  cp docs/core/Feature-List.md docs/core/Feature-List.md.bak
50
68
  cp docs/core/Project.md docs/core/Project.md.bak
69
+ cp docs/core/Appendix.md docs/core/Appendix.md.bak
51
70
  ```
52
71
 
53
- #### 3.2 功能清单迁移
72
+ #### 3.2 抽取并归一化现有内容
73
+
74
+ 输出结构化快照,供重构阶段复用:
75
+
76
+ ```yaml
77
+ project:
78
+ name: "[项目名称]"
79
+ architecture: []
80
+ tech_stack: []
81
+ modules: []
82
+ features: []
83
+ appendix:
84
+ entries: []
85
+ ```
86
+
87
+ #### 3.3 重构 Feature-List
54
88
 
55
89
  对每个功能条目:
56
90
 
57
- 1. **读取原有内容**:
91
+ 1. **读取原有内容**(保留状态、描述、TODO、已有字段):
58
92
  ```markdown
59
93
  ## F-001 ✅ 用户注册
60
94
 
@@ -66,19 +100,17 @@ cp docs/core/Project.md docs/core/Project.md.bak
66
100
  ...
67
101
  ```
68
102
 
69
- 2. **智能补充新字段**:
70
- - **优先级**:根据功能描述判断(核心功能=P0,重要功能=P1,一般功能=P2,优化类=P3)
71
- - **预估工时**:根据 TODO 数量和复杂度估算
72
- - **关联模块**:从项目文档的模块清单中匹配
103
+ 2. **模板字段补齐**:
104
+ - 模板字段由示例功能条目自动提取
105
+ - 仅补齐模板要求的字段,不新增额外字段
73
106
 
74
- 3. **生成新格式**:
107
+ 3. **生成新格式**(完全按模板布局):
75
108
  ```markdown
76
109
  ## F-001 ✅ 用户注册
77
110
 
78
111
  **描述**:允许新用户通过邮箱和密码创建账户...
79
- **优先级**:P0
80
- **预估工时**:8h
81
- **关联模块**:AuthModule
112
+ **(模板字段 1)**:...
113
+ **(模板字段 2)**:...
82
114
 
83
115
  **TODO**
84
116
  - [x] 设计数据库users表结构
@@ -86,32 +118,49 @@ cp docs/core/Project.md docs/core/Project.md.bak
86
118
  ...
87
119
  ```
88
120
 
89
- #### 3.3 项目文档迁移
121
+ #### 3.4 重构 Project
90
122
 
91
- 1. **保留原有章节**
92
- 2. **新增缺失章节**(如「环境配置」)
123
+ 1. **按模板章节顺序重建**
124
+ 2. **填充项目事实**(架构、模块、接口、数据模型、代码落点)
93
125
  3. **更新模块功能统计**(从功能清单重新计算)
94
126
 
127
+ #### 3.5 重构 Standards / Appendix
128
+
129
+ - 以模板文本为准覆盖规则/约束类内容
130
+ - 追加保留的项目自定义条目(无法映射则放在末尾)
131
+
132
+ #### 3.6 文档引用更新
133
+
134
+ - 将文档中的旧中文文件名引用统一替换为英文:
135
+ - `规范文档.md` → `Standards.md`
136
+ - `项目文档.md` → `Project.md`
137
+ - `功能清单.md` → `Feature-List.md`
138
+ - `附加材料.md` → `Appendix.md`
139
+
95
140
  ### 阶段 4:验证与确认
96
141
 
97
- 1. 展示迁移摘要:
142
+ 1. 展示重构摘要:
98
143
  ```
99
- 📋 迁移摘要
144
+ 📋 重构摘要
100
145
 
101
146
  Feature-List.md:
102
- - 更新了 15 个功能条目
103
- - 新增字段:优先级、预估工时、关联模块
147
+ - 重构 15 个功能条目
148
+ - 模板字段已补齐
104
149
 
105
150
  Project.md:
106
- - 新增章节:环境配置
151
+ - 按模板重建章节
107
152
  - 更新了模块功能统计
108
153
 
154
+ Standards.md / Appendix.md:
155
+ - 规则文本已对齐模板
156
+ - 项目自定义条目已保留
157
+
109
158
  备份位置:docs/core/*.md.bak
110
159
  ```
111
160
 
112
161
  2. 询问用户确认
113
162
 
114
- ## 优先级推断规则
163
+ ## 字段推断规则(仅当模板要求该字段时)
115
164
 
116
165
  | 特征 | 优先级 |
117
166
  |------|--------|
@@ -120,7 +169,7 @@ cp docs/core/Project.md docs/core/Project.md.bak
120
169
  | 辅助功能、增强体验 | P2 |
121
170
  | 优化、重构、技术债务 | P3 |
122
171
 
123
- ## 工时估算规则
172
+ ## 工时估算规则(仅当模板要求该字段时)
124
173
 
125
174
  | TODO 数量 | 复杂度特征 | 预估工时 |
126
175
  |-----------|-----------|----------|
@@ -129,7 +178,7 @@ cp docs/core/Project.md docs/core/Project.md.bak
129
178
  | 7-10 个 | 包含集成测试 | 8h |
130
179
  | 10+ 个 | 复杂功能 | 16h+ |
131
180
 
132
- ## 模块匹配规则
181
+ ## 模块匹配规则(仅当模板要求该字段时)
133
182
 
134
183
  1. 从功能编号范围推断(如 F-001~F-005 属于 AuthModule)
135
184
  2. 从功能描述关键词匹配(如「认证」「登录」→ AuthModule)
@@ -139,33 +188,34 @@ cp docs/core/Project.md docs/core/Project.md.bak
139
188
 
140
189
  - 如果无法确定某个字段的值,使用占位符 `[待填写]`
141
190
  - 如果文档格式无法识别,提示用户手动调整
191
+ - 无法映射的内容追加在对应文档末尾
142
192
  - 保留所有备份文件,便于回滚
143
193
 
144
194
  ## 输出格式
145
195
 
146
- 迁移完成后输出:
196
+ 重构完成后输出:
147
197
 
148
198
  ```
149
- ✅ JVibe 文档迁移完成
199
+ ✅ JVibe 文档重构完成
150
200
 
151
- 📊 迁移统计:
152
- - 功能清单:15 个功能条目已更新
153
- - 项目文档:1 个新章节已添加
201
+ 📊 重构统计:
202
+ - 功能清单:15 个功能条目已重构
203
+ - 项目文档:章节已按模板重建
154
204
 
155
205
  📝 新增字段:
156
- - 优先级:15/15 已推断
157
- - 预估工时:15/15 已估算
158
- - 关联模块:15/15 已匹配
206
+ - 模板字段:15/15 已补齐
159
207
 
160
208
  💾 备份文件:
161
209
  - docs/core/Feature-List.md.bak
162
210
  - docs/core/Project.md.bak
211
+ - docs/core/Standards.md.bak
212
+ - docs/core/Appendix.md.bak
163
213
 
164
214
  ⚠️ 请检查以下需要手动确认的项目:
165
- - F-007 用户资料编辑 - 优先级待确认
166
- - F-015 在线状态显示 - 工时可能需要调整
215
+ - F-007 用户资料编辑 - 字段待确认
216
+ - F-015 在线状态显示 - 字段可能需要调整
167
217
 
168
- 运行 jvibe validate 验证迁移结果
218
+ 运行 jvibe validate 验证重构结果
169
219
  ```
170
220
 
171
221
  ## 权限
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": ".claude/hooks/load-context.sh",
9
+ "command": "sh -c 'hook=\"load-context.sh\"; dir=\"$PWD\"; while true; do path=\"$dir/.claude/hooks/$hook\"; if [ -f \"$path\" ]; then /bin/bash \"$path\"; exit 0; fi; if [ \"$dir\" = \"/\" ]; then break; fi; dir=\"$(dirname \"$dir\")\"; done; exit 0'",
10
10
  "timeout": 5000,
11
11
  "description": "加载项目上下文,显示功能状态统计"
12
12
  }
@@ -19,7 +19,7 @@
19
19
  "hooks": [
20
20
  {
21
21
  "type": "command",
22
- "command": ".claude/hooks/sync-feature-status.sh",
22
+ "command": "sh -c 'hook=\"sync-feature-status.sh\"; dir=\"$PWD\"; while true; do path=\"$dir/.claude/hooks/$hook\"; if [ -f \"$path\" ]; then /bin/bash \"$path\"; exit 0; fi; if [ \"$dir\" = \"/\" ]; then break; fi; dir=\"$(dirname \"$dir\")\"; done; exit 0'",
23
23
  "timeout": 10000,
24
24
  "description": "当功能清单被修改时,自动推导功能状态"
25
25
  }
@@ -32,13 +32,13 @@
32
32
  "hooks": [
33
33
  {
34
34
  "type": "command",
35
- "command": ".claude/hooks/guard-output.sh",
35
+ "command": "sh -c 'hook=\"guard-output.sh\"; dir=\"$PWD\"; while true; do path=\"$dir/.claude/hooks/$hook\"; if [ -f \"$path\" ]; then /bin/bash \"$path\"; exit 0; fi; if [ \"$dir\" = \"/\" ]; then break; fi; dir=\"$(dirname \"$dir\")\"; done; exit 0'",
36
36
  "timeout": 3000,
37
37
  "description": "Warn on long outputs without structured blocks"
38
38
  },
39
39
  {
40
40
  "type": "command",
41
- "command": ".claude/hooks/sync-stats.sh",
41
+ "command": "sh -c 'hook=\"sync-stats.sh\"; dir=\"$PWD\"; while true; do path=\"$dir/.claude/hooks/$hook\"; if [ -f \"$path\" ]; then /bin/bash \"$path\"; exit 0; fi; if [ \"$dir\" = \"/\" ]; then break; fi; dir=\"$(dirname \"$dir\")\"; done; exit 0'",
42
42
  "timeout": 5000,
43
43
  "description": "Agent 完成后输出项目统计信息"
44
44
  }