speccrew 0.6.11 → 0.6.12

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.
@@ -202,6 +202,7 @@ After IDE detection, compute and store all absolute paths as workflow context va
202
202
 
203
203
  | Variable | Derivation | Example |
204
204
  |----------|-----------|---------|
205
+ | `source_path` | Project source root (from `.speccrewrc` config or user provided) | `d:/dev/litemes` |
205
206
  | `workspace_path` | Project root + `/speccrew-workspace` | `d:/dev/litemes/speccrew-workspace` |
206
207
  | `sync_state_bizs_dir` | `{workspace_path}/knowledges/base/sync-state/knowledge-bizs` | `d:/dev/litemes/speccrew-workspace/knowledges/base/sync-state/knowledge-bizs` |
207
208
  | `iterations_dir` | `{workspace_path}/iterations` | `d:/dev/litemes/speccrew-workspace/iterations` |
@@ -429,18 +430,31 @@ No knowledge base exists. A lightweight feature inventory scan is triggered to d
429
430
  ```
430
431
 
431
432
  **Agent Tool Invocation for Path B Step 2 (Module Initializer)**:
433
+
434
+ > 🛑 **MANDATORY**: You MUST dispatch a separate Worker for EACH module in matched_modules.
435
+ > Each Worker handles ONE module on ONE platform.
436
+
437
+ For each module in matched_modules:
432
438
  ```
433
439
  Use the Agent tool to invoke speccrew-task-worker:
434
440
  - agent: speccrew-task-worker
435
- - task: Execute speccrew-pm-module-initializer skill
441
+ - task: Execute speccrew-pm-module-initializer skill for module "{module.module_name}" on platform "{module.platform_id}"
436
442
  - context:
437
443
  skill: speccrew-pm-module-initializer
444
+ source_path: {source_path}
445
+ module_name: {module.module_name}
446
+ platform_id: {module.platform_id}
447
+ platform_type: {module.platform_type}
448
+ features_file: {sync_state_bizs_dir}/features-{module.platform_id}.json
449
+ output_path: {workspace_path}/knowledges
450
+ completed_dir: {sync_state_bizs_dir}/completed
451
+ sourceFile: features-{module.platform_id}.json
452
+ language: {detected user language}
438
453
  workspace_path: {workspace_path}
439
- matched_modules: <modules from matcher result>
440
- sync_state_bizs_dir: {sync_state_bizs_dir}
441
- ide_skills_dir: {ide_skills_dir}
442
454
  ```
443
455
 
456
+ Wait for ALL module-initializer Workers to complete before proceeding to Phase 2.
457
+
444
458
  4. **IF feature inventory fails**:
445
459
  - Report to user: "Project structure scan encountered issues: [specific error]. Continuing without knowledge base context."
446
460
  - Log the error details for debugging
@@ -852,21 +852,21 @@ function main() {
852
852
  }
853
853
  }
854
854
 
855
- // 使用目录路径构建唯一 ID,避免同名文件碰撞
856
- // 例: mail/account/index.vue → mail-account-index
857
- // 例: mail/template/index.vue → mail-template-index
858
- // 例: dict/index.vue → dict-index (无嵌套时保持兼容)
855
+ // Use directory path to build unique ID, avoid filename collisions
856
+ // Example: mail/account/index.vue → mail-account-index
857
+ // Example: mail/template/index.vue → mail-template-index
858
+ // Example: dict/index.vue → dict-index (keep compatible when no nesting)
859
859
  let dirSegments = file.directory
860
860
  ? file.directory.replace(/[\/\\]/g, '-').replace(/^-+|-+$/g, '').replace(/-+/g, '-')
861
861
  : '';
862
862
 
863
- // 顶层文件(directory = ".")不应引入 "."
863
+ // Top-level files (directory = ".") should not include "."
864
864
  if (dirSegments === '.' || dirSegments === './') {
865
865
  dirSegments = '';
866
866
  }
867
867
 
868
- // 如果 dirSegments 已包含 moduleName 前缀,去除避免重复
869
- // 例: directory='mail/account', moduleName='mail' → dirSegments='account'
868
+ // If dirSegments already contains moduleName prefix, remove to avoid duplication
869
+ // Example: directory='mail/account', moduleName='mail' → dirSegments='account'
870
870
  if (moduleName && dirSegments.startsWith(moduleName + '-')) {
871
871
  dirSegments = dirSegments.slice(moduleName.length + 1);
872
872
  } else if (moduleName && dirSegments === moduleName) {
@@ -142,7 +142,7 @@ function main() {
142
142
  reclassifiedCount++;
143
143
  reclassifiedModules.add(oldModule);
144
144
 
145
- // Rebuild documentPath(使用 fileName 而非 feature.id,避免文件名过长)
145
+ // Rebuild documentPath (use fileName instead of feature.id to avoid long filenames)
146
146
  const newDocumentPath = `speccrew-workspace/knowledges/bizs/${platformId}/${newModule}/${feature.fileName}.md`;
147
147
 
148
148
  return {
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * reindex-modules.js - 确定性脚本,用更新后的 exclude_dirs 对已有 features JSON 重新提取 module
3
+ * reindex-modules.js - Deterministic script to re-extract module names from existing features JSON with updated exclude_dirs
4
4
  *
5
- * 调用方式:
5
+ * Usage:
6
6
  * node reindex-modules.js --featuresFile "path/to/features-backend-system.json" --projectRoot "d:\dev\ruoyi-vue-pro"
7
7
  *
8
- * 可选参数:
9
- * --platformType "backend" - 如果不提供,从 features JSON platformType 字段读取
10
- * --techIdentifier "spring" - 如果不提供,尝试从 features JSON 推断(如 techStack 数组)
11
- * --excludeDirs "controller,admin,api,service" - 如果不提供,从 tech-stack-mappings.json 加载
8
+ * Optional parameters:
9
+ * --platformType "backend" - If not provided, read from features JSON's platformType field
10
+ * --techIdentifier "spring" - If not provided, try to infer from features JSON (e.g., techStack array)
11
+ * --excludeDirs "controller,admin,api,service" - If not provided, load from tech-stack-mappings.json
12
12
  */
13
13
 
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
16
 
17
- // === 工具函数(从 generate-inventory.js 复用) ===
17
+ // === Utility functions (reused from generate-inventory.js) ===
18
18
 
19
19
  function normalizePath(filePath) {
20
20
  if (!filePath) return '';
@@ -32,7 +32,7 @@ function parseArrayParam(value) {
32
32
  return trimmed.split(',').map(s => s.trim()).filter(Boolean);
33
33
  }
34
34
 
35
- // Java/Kotlin 标准源码路径前缀
35
+ // Java/Kotlin standard source path prefixes
36
36
  const STANDARD_SOURCE_PREFIXES = [
37
37
  'src/main/java',
38
38
  'src/main/kotlin',
@@ -77,7 +77,7 @@ function getModuleName(dirPath, excludeDirs, fallbackModuleName) {
77
77
  return '_root';
78
78
  }
79
79
 
80
- // === 辅助函数 ===
80
+ // === Helper functions ===
81
81
 
82
82
  function parseArgs(argv) {
83
83
  const args = {};
@@ -113,10 +113,10 @@ function findProjectRoot(startPath) {
113
113
  function loadExcludeDirs(projectRoot, platformType, techIdentifier, featuresData) {
114
114
  console.log(`Loading exclude_dirs: platformType="${platformType}", techIdentifier="${techIdentifier || '(auto)'}"`);
115
115
 
116
- // 尝试推断 techIdentifier
116
+ // Try to infer techIdentifier
117
117
  if (!techIdentifier) {
118
118
  const techStack = featuresData.techStack || [];
119
- // 常见映射
119
+ // Common mappings
120
120
  const techMap = {
121
121
  'spring': 'spring', 'spring-boot': 'spring', 'java': 'spring',
122
122
  'vue': 'vue', 'vue3': 'vue', 'vue2': 'vue',
@@ -147,7 +147,7 @@ function loadExcludeDirs(projectRoot, platformType, techIdentifier, featuresData
147
147
  // Get fallback dirs for this platformType
148
148
  const fallback = FALLBACK_EXCLUDE_DIRS[platformType] || [];
149
149
 
150
- // 查找 tech-stack-mappings.json
150
+ // Find tech-stack-mappings.json
151
151
  const configPaths = [
152
152
  path.join(projectRoot, 'speccrew-workspace', 'docs', 'configs', 'tech-stack-mappings.json'),
153
153
  path.join(projectRoot, 'docs', 'configs', 'tech-stack-mappings.json'),
@@ -207,25 +207,25 @@ function loadExcludeDirs(projectRoot, platformType, techIdentifier, featuresData
207
207
  }
208
208
 
209
209
  function extractPlatformId(featuresData) {
210
- // 从第一个 feature documentPath 提取 platformId
211
- // 格式: "speccrew-workspace/knowledges/bizs/{platformId}/..."
210
+ // Extract platformId from first feature's documentPath
211
+ // Format: "speccrew-workspace/knowledges/bizs/{platformId}/..."
212
212
  for (const feature of (featuresData.features || [])) {
213
213
  if (feature.documentPath) {
214
214
  const match = normalizePath(feature.documentPath).match(/knowledges\/bizs\/([^/]+)\//);
215
215
  if (match) return match[1];
216
216
  }
217
217
  }
218
- // 如果无法提取,用 platformType-platformSubtype 构建
218
+ // If unable to extract, build from platformType-platformSubtype
219
219
  if (featuresData.platformType && featuresData.platformSubtype) {
220
220
  return `${featuresData.platformType}-${featuresData.platformSubtype}`;
221
221
  }
222
222
  return null;
223
223
  }
224
224
 
225
- // === 主逻辑 ===
225
+ // === Main logic ===
226
226
 
227
227
  function main() {
228
- // 1. 解析命令行参数
228
+ // 1. Parse command line arguments
229
229
  const args = parseArgs(process.argv.slice(2));
230
230
  const featuresFile = args.featuresFile;
231
231
 
@@ -234,7 +234,7 @@ function main() {
234
234
  process.exit(1);
235
235
  }
236
236
 
237
- // 2. 读取 features JSON
237
+ // 2. Read features JSON
238
238
  let featuresData;
239
239
  try {
240
240
  featuresData = JSON.parse(fs.readFileSync(featuresFile, 'utf8'));
@@ -250,16 +250,16 @@ function main() {
250
250
  const inventorySourcePath = normalizePath(featuresData.sourcePath || '');
251
251
  const platformType = args.platformType || featuresData.platformType || '';
252
252
 
253
- // 3. 加载 exclude_dirs
253
+ // 3. Load exclude_dirs
254
254
  let excludeDirs = [];
255
255
  let stripModulePrefixes = [];
256
256
  if (args.excludeDirs) {
257
257
  excludeDirs = parseArrayParam(args.excludeDirs);
258
258
  } else {
259
- // tech-stack-mappings.json 加载,按优先级确定 techIdentifier
260
- // 1. 命令行参数 --techIdentifier
261
- // 2. features JSON 中的 techIdentifier
262
- // 3. features JSON 中的 platformSubtype (兼容旧数据)
259
+ // Load from tech-stack-mappings.json, determine techIdentifier by priority:
260
+ // 1. Command line parameter --techIdentifier
261
+ // 2. techIdentifier in features JSON
262
+ // 3. platformSubtype in features JSON (backward compatibility)
263
263
  const techId = args.techIdentifier || featuresData.techIdentifier || featuresData.platformSubtype;
264
264
  const config = loadExcludeDirs(projectRoot, platformType, techId, featuresData);
265
265
  excludeDirs = config.excludeDirs;
@@ -274,29 +274,29 @@ function main() {
274
274
  console.log(`Exclude dirs (${excludeDirs.length}): ${excludeDirs.join(', ')}`);
275
275
  console.log(`Total features: ${featuresData.features.length}`);
276
276
 
277
- // 4. 重新计算每个 feature module
277
+ // 4. Recalculate module for each feature
278
278
  const modulesBefore = [...new Set(featuresData.features.map(f => f.module))].sort();
279
279
  let reclassifiedCount = 0;
280
280
 
281
- // features JSON 获取 platformId(用于重建 documentPath
282
- // platformId 格式: "{platformType}-{platformSubtype}"
283
- // 从现有 documentPath 提取: "speccrew-workspace/knowledges/bizs/{platformId}/..."
281
+ // Get platformId from features JSON (for rebuilding documentPath)
282
+ // platformId format: "{platformType}-{platformSubtype}"
283
+ // Extract from existing documentPath: "speccrew-workspace/knowledges/bizs/{platformId}/..."
284
284
  const platformId = extractPlatformId(featuresData);
285
285
 
286
286
  featuresData.features.forEach(feature => {
287
- // 计算 feature 源文件相对于 inventorySourcePath 的路径
287
+ // Calculate feature source file path relative to inventorySourcePath
288
288
  let relativePath = normalizePath(feature.sourcePath || '');
289
289
 
290
- // 如果 feature.sourcePath 是绝对路径或相对于项目根,需要去掉 inventorySourcePath 前缀
290
+ // If feature.sourcePath is absolute or relative to project root, remove inventorySourcePath prefix
291
291
  if (inventorySourcePath && relativePath.startsWith(inventorySourcePath)) {
292
292
  relativePath = relativePath.slice(inventorySourcePath.length);
293
293
  if (relativePath.startsWith('/')) relativePath = relativePath.slice(1);
294
294
  }
295
- // 也可能 inventorySourcePath 只是部分匹配
295
+ // inventorySourcePath may only partially match
296
296
  else if (inventorySourcePath) {
297
297
  const invParts = inventorySourcePath.split('/');
298
298
  const relParts = relativePath.split('/');
299
- // 找到重叠部分
299
+ // Find overlapping part
300
300
  let startIdx = 0;
301
301
  for (let i = 0; i < relParts.length; i++) {
302
302
  if (relParts.slice(i, i + invParts.length).join('/') === inventorySourcePath) {
@@ -309,14 +309,14 @@ function main() {
309
309
  }
310
310
  }
311
311
 
312
- // 取目录部分(去掉文件名)
312
+ // Get directory part (remove filename)
313
313
  const dirPath = path.dirname(relativePath).replace(/\\/g, '/');
314
314
 
315
- // getModuleName 重新提取模块名
315
+ // Re-extract module name using getModuleName
316
316
  const fallback = feature.module || '_root';
317
317
  let newModule = getModuleName(dirPath, excludeDirs, fallback);
318
318
 
319
- // 应用 strip_module_prefixes 前缀去除
319
+ // Apply strip_module_prefixes prefix removal
320
320
  for (const prefix of stripModulePrefixes) {
321
321
  if (newModule.startsWith(prefix)) {
322
322
  newModule = newModule.substring(prefix.length);
@@ -328,21 +328,21 @@ function main() {
328
328
  reclassifiedCount++;
329
329
  feature.module = newModule;
330
330
 
331
- // 重建 documentPath(使用 fileName 而非 feature.id,避免文件名过长)
331
+ // Rebuild documentPath (use fileName instead of feature.id to avoid long filenames)
332
332
  if (platformId) {
333
333
  feature.documentPath = `speccrew-workspace/knowledges/bizs/${platformId}/${newModule}/${feature.fileName}.md`;
334
334
  }
335
335
  }
336
336
  });
337
337
 
338
- // 5. 更新 modules 数组
338
+ // 5. Update modules array
339
339
  const modulesAfter = [...new Set(featuresData.features.map(f => f.module))].sort();
340
340
  featuresData.modules = modulesAfter;
341
341
 
342
- // 6. 写回文件
342
+ // 6. Write back to file
343
343
  fs.writeFileSync(featuresFile, JSON.stringify(featuresData, null, 2), 'utf8');
344
344
 
345
- // 7. 输出结果
345
+ // 7. Output results
346
346
  const result = {
347
347
  status: 'success',
348
348
  modules_before: modulesBefore,
@@ -8,11 +8,14 @@ tools: Read, Write, Skill, Bash
8
8
 
9
9
  Initialize knowledge base for a single business module by analyzing its features. Dispatches api-analyze or ui-analyze based on platform type, then generates module summary. Used by Worker Agent, invoked by PM Agent for on-demand module initialization.
10
10
 
11
+ > **Positioning**: Lightweight knowledge base initializer for PM phase, processes only modules matched by PM matcher.
12
+ > Difference from bizs-dispatch: module-initializer processes single modules on-demand, does not generate graph data, output is for PRD authoring reference only.
13
+
11
14
  ## Language Adaptation
12
15
 
13
16
  **CRITICAL**: Generate all content in the language specified by the `language` parameter.
14
17
 
15
- - `language: "zh"` → Generate all content in 中文
18
+ - `language: "zh"` → Generate all content in Chinese
16
19
  - `language: "en"` → Generate all content in English
17
20
  - Other languages → Use the specified language
18
21
 
@@ -29,15 +32,17 @@ Initialize knowledge base for a single business module by analyzing its features
29
32
 
30
33
  | Parameter | Type | Required | Description |
31
34
  |-----------|------|----------|-------------|
32
- | `source_path` | string | Yes | 项目源码根路径 |
33
- | `module_name` | string | Yes | 模块名称 |
34
- | `platform_id` | string | Yes | 平台 ID (e.g., "web-vue3", "admin-api") |
35
+ | `source_path` | string | Yes | Project source code root path |
36
+ | `module_name` | string | Yes | Module name |
37
+ | `platform_id` | string | Yes | Platform ID (e.g., "web-vue3", "admin-api") |
35
38
  | `platform_type` | string | Yes | web / mobile / backend / desktop |
36
39
  | `platform_subtype` | string | No | Platform subtype (e.g., "vue", "spring-boot") |
37
40
  | `tech_stack` | array | No | Platform tech stack (e.g., ["java", "spring-boot"]) |
38
- | `features_file` | string | Yes | 该平台的 features-{platform}.json 路径 |
39
- | `output_path` | string | Yes | 知识库输出根路径 (e.g., speccrew-workspace/knowledges) |
40
- | `language` | string | Yes | 输出语言 (zh / en) |
41
+ | `features_file` | string | Yes | Path to the platform's features-{platform}.json file |
42
+ | `output_path` | string | Yes | Knowledge base output root path (e.g., speccrew-workspace/knowledges) |
43
+ | `completed_dir` | string | Yes | Marker file output directory for api-analyze .done.json markers. Value from PM Agent: `{sync_state_bizs_dir}/completed` |
44
+ | `sourceFile` | string | Yes | Features JSON filename (e.g., "features-backend-system.json"), used for api-analyze marking |
45
+ | `language` | string | Yes | Output language (zh / en) |
41
46
 
42
47
  ## Output JSON
43
48
 
@@ -100,38 +105,67 @@ Based on `platform_type`, select the appropriate analyzer Skill:
100
105
 
101
106
  **Output**: "Step 2 Status: ✅ COMPLETED - Selected analyzer: {skill_name}"
102
107
 
103
- ### Step 3: Analyze Each Pending Feature
108
+ ### Step 3: Analyze Each Pending Feature (Sequential)
104
109
 
105
110
  **Step 3 Status: 🔄 IN PROGRESS**
106
111
 
107
- For each pending feature, invoke the selected analyzer Skill.
112
+ For each pending feature from Step 1, call the appropriate analyzer skill.
113
+
114
+ > 🛑 **FORBIDDEN**: DO NOT write custom scripts. You MUST call the analyzer skill directly using the Agent tool or execute it as defined in the skill's SKILL.md.
108
115
 
109
- **Important**: Process features sequentially (not parallel) since this Skill runs inside a single Worker Agent. The PM Agent handles parallelism at the module level (multiple Workers for different modules).
116
+ #### For backend modules (api-analyze):
110
117
 
111
- #### Input Variables Preparation
118
+ For each pending feature, invoke the `speccrew-knowledge-bizs-api-analyze` skill with these parameters:
112
119
 
113
- Prepare input variables matching the analyzer Skill's Input Variables format:
120
+ ```
121
+ Use Agent tool to invoke speccrew-task-worker:
122
+ - agent: speccrew-task-worker
123
+ - task: Execute speccrew-knowledge-bizs-api-analyze skill
124
+ - context:
125
+ skill: speccrew-knowledge-bizs-api-analyze
126
+ fileName: {feature.fileName}
127
+ sourcePath: {source_path}/{feature.sourcePath}
128
+ documentPath: {output_path}/bizs/{platform_id}/{feature.module}/features
129
+ module: {feature.module}
130
+ analyzed: false
131
+ platform_type: backend
132
+ platform_subtype: {platform_subtype}
133
+ tech_stack: {tech_stack}
134
+ language: {language}
135
+ completed_dir: {completed_dir}
136
+ sourceFile: {sourceFile}
137
+ ```
114
138
 
115
- | Variable | Value Source |
116
- |----------|--------------|
117
- | `feature` | The complete feature object from features.json |
118
- | `fileName` | feature's fileName field |
119
- | `sourcePath` | feature's sourcePath field |
120
- | `documentPath` | Computed as `{output_path}/bizs/{platform_id}/{module_name}/{fileName}.md` |
121
- | `module` | module_name |
122
- | `analyzed` | false |
123
- | `platform_type` | From input |
124
- | `platform_subtype` | From input (if available) |
125
- | `tech_stack` | From input (if available) |
126
- | `language` | From input |
127
- | `completed_dir` | `{output_path}/base/sync-state/knowledge-bizs/completed/` |
128
- | `sourceFile` | Features JSON filename (e.g., "features-web-vue3.json") |
139
+ #### For web/mobile/desktop modules (ui-analyze):
129
140
 
130
- #### Execution for Each Feature
141
+ For each pending feature, invoke the `speccrew-knowledge-bizs-ui-analyze` skill with these parameters:
131
142
 
132
- 1. Invoke the selected analyzer Skill with prepared parameters
133
- 2. Track success/failure count
134
- 3. On success, update the feature's `analyzed` field to `true` in features_file
143
+ ```
144
+ Use Agent tool to invoke speccrew-task-worker:
145
+ - agent: speccrew-task-worker
146
+ - task: Execute speccrew-knowledge-bizs-ui-analyze skill
147
+ - context:
148
+ skill: speccrew-knowledge-bizs-ui-analyze
149
+ fileName: {feature.fileName}
150
+ sourcePath: {source_path}/{feature.sourcePath}
151
+ documentPath: {output_path}/bizs/{platform_id}/{feature.module}/features
152
+ module: {feature.module}
153
+ analyzed: false
154
+ platform_type: {platform_type}
155
+ platform_subtype: {platform_subtype}
156
+ tech_stack: {tech_stack}
157
+ language: {language}
158
+ ```
159
+
160
+ > **NOTE**: UI Analyzer does NOT write .done.json marker files (handled by ui-graph skill in bizs-dispatch).
161
+
162
+ #### After each successful analysis:
163
+ Update the feature's `analyzed` field to `true` in the features JSON file.
164
+
165
+ #### Error handling:
166
+ - If an analyzer fails, log the error, increment `features_failed` counter
167
+ - Continue with next feature (do NOT abort the entire module)
168
+ - A module with partial analysis is better than no analysis
135
169
 
136
170
  **Output**: "Step 3 Status: ✅ COMPLETED - Analyzed {success} features, {failed} failed"
137
171
 
@@ -139,15 +173,25 @@ Prepare input variables matching the analyzer Skill's Input Variables format:
139
173
 
140
174
  **Step 4 Status: 🔄 IN PROGRESS**
141
175
 
142
- Invoke `speccrew-knowledge-module-summarize` Skill to generate/update the module overview document.
143
-
144
- **Input Parameters**:
145
-
146
- | Variable | Value |
147
- |----------|-------|
148
- | `module_name` | module_name |
149
- | `module_path` | `{output_path}/bizs/{platform_id}/{module_name}/` |
150
- | `language` | From input |
176
+ **Pre-check**: Verify that feature documents exist at `{output_path}/bizs/{platform_id}/{module_name}/features/`.
177
+
178
+ IF no feature documents exist AND features_analyzed == 0:
179
+ - Set status to "partial"
180
+ - Skip module-summarize
181
+ - Proceed to Step 5
182
+
183
+ IF feature documents exist:
184
+ Invoke `speccrew-knowledge-module-summarize` skill:
185
+ ```
186
+ Use Agent tool to invoke speccrew-task-worker:
187
+ - agent: speccrew-task-worker
188
+ - task: Execute speccrew-knowledge-module-summarize skill
189
+ - context:
190
+ skill: speccrew-knowledge-module-summarize
191
+ module_name: {module_name}
192
+ module_path: {output_path}/bizs/{platform_id}/{module_name}
193
+ language: {language}
194
+ ```
151
195
 
152
196
  **Output**: "Step 4 Status: ✅ COMPLETED - Module overview generated at {module_path}/{module_name}-overview.md"
153
197
 
@@ -8,7 +8,7 @@ function run(projectRoot, args) {
8
8
 
9
9
  const results = [];
10
10
 
11
- // 1. Node.js 版本检查
11
+ // 1. Node.js version check
12
12
  const nodeVersion = process.version;
13
13
  const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0], 10);
14
14
  if (majorVersion >= 16) {
@@ -17,7 +17,7 @@ function run(projectRoot, args) {
17
17
  results.push({ status: 'FAIL', message: `Node.js ${nodeVersion} (< 16.0.0)` });
18
18
  }
19
19
 
20
- // 2. SpecCrew 安装状态
20
+ // 2. SpecCrew installation status
21
21
  const rc = readSpeccrewRC(projectRoot);
22
22
  const version = getPackageVersion();
23
23
  if (rc) {
@@ -26,7 +26,7 @@ function run(projectRoot, args) {
26
26
  results.push({ status: 'WARN', message: 'Not initialized, run speccrew init' });
27
27
  }
28
28
 
29
- // 3. IDE 目录检查
29
+ // 3. IDE directory check
30
30
  const detectedIDEs = detectIDE(projectRoot);
31
31
  if (detectedIDEs.length > 0) {
32
32
  const ideNames = detectedIDEs.map(ide => `${ide.name} (${ide.baseDir}/)`).join(', ');
@@ -35,7 +35,7 @@ function run(projectRoot, args) {
35
35
  results.push({ status: 'WARN', message: 'No supported IDE detected' });
36
36
  }
37
37
 
38
- // 4. Agents 完整性检查
38
+ // 4. Agents integrity check
39
39
  const sourceRoot = getSourceRoot();
40
40
  const sourceAgentsDir = path.join(sourceRoot, 'agents');
41
41
  let sourceAgentCount = 0;
@@ -70,7 +70,7 @@ function run(projectRoot, args) {
70
70
  results.push({ status: 'WARN', message: 'Agents: source not found' });
71
71
  }
72
72
 
73
- // 5. Skills 完整性检查
73
+ // 5. Skills integrity check
74
74
  const sourceSkillsDir = path.join(sourceRoot, 'skills');
75
75
  let sourceSkillCount = 0;
76
76
  let installedSkillCount = 0;
@@ -103,7 +103,7 @@ function run(projectRoot, args) {
103
103
  results.push({ status: 'WARN', message: 'Skills: source not found' });
104
104
  }
105
105
 
106
- // 6. Workspace 目录检查
106
+ // 6. Workspace directory check
107
107
  const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
108
108
  const docsDir = path.join(workspaceDir, 'docs');
109
109
  if (fs.existsSync(workspaceDir) && fs.existsSync(docsDir)) {
@@ -114,7 +114,7 @@ function run(projectRoot, args) {
114
114
  results.push({ status: 'WARN', message: 'Workspace: speccrew-workspace/ not found' });
115
115
  }
116
116
 
117
- // 输出结果
117
+ // Output results
118
118
  let passCount = 0;
119
119
  let warnCount = 0;
120
120
  let failCount = 0;