sillyspec 3.7.27 → 3.7.28

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.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: sillyspec:doctor
3
+ description: 项目自检 — 检查 CLI、配置、构建环境和外部依赖
4
+ ---
5
+
6
+ ## 前置检查
7
+
8
+ **在执行任何检查之前,先确认 SillySpec CLI 是否可用:**
9
+
10
+ 1. 运行 `sillyspec --version`
11
+ 2. 如果失败,运行 `npx sillyspec --version`
12
+ 3. 如果都失败:
13
+ - 输出:❌ SillySpec CLI 未安装,后续检查无法执行
14
+ - 给出安装命令:`npm install -g sillyspec`(生产环境)或 `npx sillyspec`(临时使用)
15
+ - 停止,不要继续后续步骤
16
+
17
+ ## 执行
18
+
19
+ CLI 可用后,运行 `sillyspec run doctor`,按提示逐步执行。
20
+ 每步完成后运行 `sillyspec run doctor --done --output "摘要"`。
21
+
22
+ ## 用户指令
23
+ $ARGUMENTS
@@ -0,0 +1,7 @@
1
+ # SillySpec 工作区配置
2
+ # 修改此文件后运行 /sillyspec:workspace 更新
3
+
4
+ projects:
5
+ # 运行 /sillyspec:workspace add 添加子项目
6
+
7
+ shared: []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sillyspec",
3
- "version": "3.7.27",
3
+ "version": "3.7.28",
4
4
  "description": "SillySpec CLI — 流程状态机,让 AI 严格按步骤来",
5
5
  "icon": "logo.jpg",
6
6
  "homepage": "https://sillyspec.ppdmq.top/",
@@ -311,7 +311,7 @@ export function parseDocsTree(projectPath) {
311
311
 
312
312
  if (files.length > 0) {
313
313
  groups.push({
314
- key: group.key,
314
+ key: `${projName}::${group.key}`,
315
315
  label: group.label,
316
316
  project: projName,
317
317
  files
@@ -81,7 +81,7 @@
81
81
  <!-- Output -->
82
82
  <div v-if="activeStep.output || activeStep.files" class="px-4 py-3" style="border-bottom: 1px solid #F0F0F3;">
83
83
  <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-1.5 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">输出</h4>
84
- <n-code v-if="activeStep.output" :code="activeStep.output" language="text" word-wrap style="max-height: 160px; overflow-y: auto;" />
84
+ <n-code v-if="activeStep.output" :code="activeStep.output" language="text" word-wrap style="max-height: 300px; overflow-y: auto; padding: 8px; border-radius: 4px; background: #F5F5F7;" />
85
85
  <div v-if="activeStep.files" class="mt-2 space-y-1">
86
86
  <div v-for="(file, i) in activeStep.files" :key="i" class="flex items-center gap-2 text-[10px]" style="color: #6B7280;">
87
87
  <svg class="w-3 h-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -39,7 +39,7 @@ const groupIcons = {
39
39
  const treeData = computed(() => {
40
40
  return props.groups.map(group => ({
41
41
  key: `group-${group.key}`,
42
- label: group.label,
42
+ label: group.project ? `${group.label} (${group.project})` : group.label,
43
43
  prefix: () => groupIcons[group.label] || '📄',
44
44
  children: group.files.map(file => ({
45
45
  key: file.path,
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- :class="['group relative rounded-md cursor-pointer overflow-hidden transition-all duration-200']"
3
+ :class="['group relative rounded-md cursor-pointer transition-all duration-200']"
4
4
  :style="cardStyle"
5
5
  @mouseenter="hovered = true"
6
6
  @mouseleave="hovered = false"
@@ -9,7 +9,7 @@
9
9
  <!-- Left accent bar -->
10
10
  <div class="absolute left-0 top-0 bottom-0 w-[2px]" :style="{ background: barColor }" />
11
11
 
12
- <div class="pl-3.5 pr-3 py-2.5">
12
+ <div class="pl-4 pr-4 py-3">
13
13
  <div class="flex items-center gap-2">
14
14
  <h3 class="text-[12px] font-medium transition-colors duration-150" :style="{ color: isActive ? '#D97706' : '#1C1C1E', fontFamily: '\'JetBrains Mono\', monospace' }">
15
15
  {{ step.title || step.name }}
@@ -20,8 +20,8 @@
20
20
  <!-- Hover summary -->
21
21
  <div
22
22
  v-if="step.summary || step.description"
23
- class="overflow-hidden transition-all duration-200"
24
- :style="{ maxHeight: hovered ? '60px' : '0', opacity: hovered ? 1 : 0 }"
23
+ class="transition-all duration-200"
24
+ :style="{ maxHeight: hovered ? '80px' : '0', opacity: hovered ? 1 : 0, overflow: 'hidden' }"
25
25
  >
26
26
  <p class="mt-1.5 text-[11px] line-clamp-2" style="color: #636366;">
27
27
  {{ step.summary || step.description }}
package/src/index.js CHANGED
@@ -20,7 +20,6 @@ SillySpec CLI — 规范驱动开发工具包
20
20
  用法:
21
21
  sillyspec init 初始化(零交互,自动检测工具)
22
22
  [--tool <name>] 只安装指定工具
23
- [--workspace] 工作区模式
24
23
  [--interactive] 交互式引导
25
24
  [--dir <path>] 指定目录
26
25
  sillyspec setup [--list] 安装推荐 MCP 工具
@@ -47,7 +46,7 @@ SillySpec CLI — 规范驱动开发工具包
47
46
  示例:
48
47
  sillyspec init
49
48
  sillyspec init --tool claude
50
- sillyspec init --workspace
49
+
51
50
  sillyspec setup
52
51
  sillyspec setup --list
53
52
  sillyspec dashboard
@@ -72,7 +71,6 @@ async function main() {
72
71
  let json = false;
73
72
  let targetDir = process.cwd();
74
73
  let tool = null;
75
- let workspace = false;
76
74
  let interactive = false;
77
75
  const filteredArgs = [];
78
76
 
@@ -85,8 +83,6 @@ async function main() {
85
83
  } else if (args[i] === '--tool' && args[i + 1]) {
86
84
  tool = args[i + 1];
87
85
  i++;
88
- } else if (args[i] === '--workspace' || args[i] === '-w') {
89
- workspace = true;
90
86
  } else if (args[i] === '--interactive' || args[i] === '-i') {
91
87
  interactive = true;
92
88
  } else if (args[i] === '--list' || args[i] === '-l') {
@@ -111,7 +107,7 @@ async function main() {
111
107
 
112
108
  switch (command) {
113
109
  case 'init':
114
- await cmdInit(dir, { tool, workspace, interactive });
110
+ await cmdInit(dir, { tool, interactive });
115
111
  break;
116
112
  case 'setup':
117
113
  const setupList = filteredArgs.includes('--list') || filteredArgs.includes('-l');
package/src/init.js CHANGED
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSy
2
2
  import { join, resolve, dirname, basename } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { homedir } from 'os';
5
- import { checkbox, select, confirm, input } from '@inquirer/prompts';
5
+ import { checkbox, confirm, input } from '@inquirer/prompts';
6
6
  import { ProgressManager } from './progress.js';
7
7
  import chalk from 'chalk';
8
8
 
@@ -106,7 +106,7 @@ function isTTY() {
106
106
 
107
107
  // ── 核心安装逻辑 ──
108
108
 
109
- async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
109
+ async function doInstall(projectDir, tools, subprojects = []) {
110
110
  // 创建基础目录
111
111
  // .sillyspec/projects/ → 项目注册表
112
112
  // .sillyspec/docs/<name>/ → 统一文档中心
@@ -128,13 +128,11 @@ async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
128
128
  const gitkeepPath = join(scanDir, '.gitkeep');
129
129
  if (!existsSync(gitkeepPath)) writeFileSync(gitkeepPath, '');
130
130
 
131
- // 兼容:保留旧的 codebase/ changes/ quicklog/ 目录(如果已存在不删除)
132
- if (isWorkspace) {
133
- mkdirSync(join(projectDir, '.sillyspec', 'shared'), { recursive: true });
134
- mkdirSync(join(projectDir, '.sillyspec', 'workspace'), { recursive: true });
135
- }
131
+ // 创建 shared/workspace 目录
132
+ mkdirSync(join(projectDir, '.sillyspec', 'shared'), { recursive: true });
133
+ mkdirSync(join(projectDir, '.sillyspec', 'workspace'), { recursive: true });
136
134
 
137
- // 创建知识库骨架(所有模式)
135
+ // 创建知识库骨架
138
136
  const knowledgeDir = join(projectDir, '.sillyspec', 'knowledge');
139
137
  mkdirSync(knowledgeDir, { recursive: true });
140
138
  const indexPath = join(knowledgeDir, 'INDEX.md');
@@ -207,19 +205,17 @@ async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
207
205
  console.log(chalk.yellow(' ⚠ 未找到 skills 目录,跳过 Claude Code skills 同步'));
208
206
  }
209
207
 
210
-
211
208
  // 工作区配置
212
- if (isWorkspace) {
213
- const configPath = join(projectDir, '.sillyspec', 'config.yaml');
214
- if (!existsSync(configPath)) {
215
- let projectsYaml = '';
216
- if (subprojects.length > 0) {
217
- projectsYaml = subprojects.map(p =>
218
- ` ${p.name}:\n path: ${p.path}\n role: ${p.role || p.name}${p.repo ? `\n repo: ${p.repo}` : ''}`
219
- ).join('\n');
220
- }
209
+ const configPath = join(projectDir, '.sillyspec', 'config.yaml');
210
+ if (!existsSync(configPath)) {
211
+ let projectsYaml = '';
212
+ if (subprojects.length > 0) {
213
+ projectsYaml = subprojects.map(p =>
214
+ ` ${p.name}:\n path: ${p.path}\n role: ${p.role || p.name}${p.repo ? `\n repo: ${p.repo}` : ''}`
215
+ ).join('\n');
216
+ }
221
217
 
222
- writeFileSync(configPath,
218
+ writeFileSync(configPath,
223
219
  `# SillySpec 工作区配置
224
220
  # 修改此文件后运行 /sillyspec:workspace 更新
225
221
 
@@ -228,16 +224,14 @@ ${projectsYaml || ' # 运行 /sillyspec:workspace add 添加子项目'}
228
224
 
229
225
  shared: []
230
226
  `
231
- );
232
- }
227
+ );
233
228
  }
234
229
  }
235
230
 
236
231
  // ── 安装完成总结 ──
237
232
 
238
- function showSummary(version, tools, isWorkspace) {
233
+ function showSummary(version, tools) {
239
234
  const toolLabels = tools.map(t => TOOL_LABELS[t] || t);
240
- const mode = isWorkspace ? '多项目工作区' : '单项目';
241
235
 
242
236
  console.log('');
243
237
  console.log(chalk.green(' ═══════════════════════════════════════'));
@@ -245,7 +239,6 @@ function showSummary(version, tools, isWorkspace) {
245
239
  console.log(chalk.green(' ═══════════════════════════════════════'));
246
240
  console.log('');
247
241
  console.log(` 已安装工具: ${chalk.cyan(toolLabels.join(', '))}`);
248
- console.log(` 模式: ${chalk.yellow(mode)}`);
249
242
  console.log(' 📁 .sillyspec/ — 项目规范目录');
250
243
  console.log('');
251
244
  console.log(' 下一步:使用 AI 技能开始工作');
@@ -270,7 +263,7 @@ export function getVersion() {
270
263
  // ── 主命令 ──
271
264
 
272
265
  export async function cmdInit(projectDir, options = {}) {
273
- const { tool, workspace, interactive } = options;
266
+ const { tool, interactive } = options;
274
267
  const version = getVersion();
275
268
 
276
269
  // ── 交互式模式(--interactive 或 -i)──
@@ -302,20 +295,11 @@ export async function cmdInit(projectDir, options = {}) {
302
295
  validate: (answer) => answer.length > 0 || '至少选择一个工具',
303
296
  });
304
297
 
305
- // 工作区模式
306
- const isWorkspace = await select({
307
- message: '选择项目模式',
308
- choices: [
309
- { name: '单项目模式', value: 'false' },
310
- { name: '多项目工作区', value: 'true' },
311
- ],
312
- }) === 'true';
313
-
314
- // 工作区子项目引导
298
+ // 子项目引导(始终执行)
315
299
  let subprojects = [];
316
- if (isWorkspace) {
300
+ {
317
301
  console.log('');
318
- console.log(chalk.yellow('📋 工作区模式 — 添加子项目'));
302
+ console.log(chalk.yellow('📋 添加子项目'));
319
303
  console.log(chalk.dim(' 子项目是工作区中的独立项目目录(如 frontend/、backend/)'));
320
304
  console.log('');
321
305
 
@@ -378,8 +362,8 @@ export async function cmdInit(projectDir, options = {}) {
378
362
  }
379
363
 
380
364
  console.log('');
381
- const count = await doInstall(projectDir, selectedTools, isWorkspace, subprojects);
382
- showSummary(version, selectedTools, isWorkspace);
365
+ await doInstall(projectDir, selectedTools, subprojects);
366
+ showSummary(version, selectedTools);
383
367
  return;
384
368
  }
385
369
 
@@ -397,7 +381,7 @@ export async function cmdInit(projectDir, options = {}) {
397
381
  tools = detectTools(projectDir);
398
382
  }
399
383
 
400
- await doInstall(projectDir, tools, !!workspace);
384
+ await doInstall(projectDir, tools);
401
385
 
402
386
  console.log('');
403
387
  console.log(chalk.green(` ✅ SillySpec v${version} 安装完成!`));
package/src/progress.js CHANGED
@@ -17,7 +17,7 @@ const PROGRESS_FILE = 'progress.json';
17
17
  const BACKUP_FILE = 'progress.json.bak';
18
18
 
19
19
  const CURRENT_VERSION = 2;
20
- const VALID_STAGES = ['brainstorm', 'propose', 'plan', 'execute', 'verify', 'scan', 'quick', 'archive', 'status'];
20
+ const VALID_STAGES = ['brainstorm', 'propose', 'plan', 'execute', 'verify', 'scan', 'quick', 'archive', 'status', 'doctor'];
21
21
  const VALID_STATUSES = ['pending', 'in-progress', 'completed', 'failed', 'blocked'];
22
22
 
23
23
  const STAGE_LABELS = {
@@ -30,6 +30,7 @@ const STAGE_LABELS = {
30
30
  quick: '⚡ 快速任务',
31
31
  archive: '📦 归档变更',
32
32
  status: '📊 状态查看',
33
+ doctor: '🩺 项目自检',
33
34
  };
34
35
 
35
36
  function emptyStage() {
@@ -312,6 +313,10 @@ export class ProgressManager {
312
313
  console.log(`⚠️ 发现问题,尝试修复...`);
313
314
  let fixed = { ...data, stages: { ...data.stages } };
314
315
  let changed = false;
316
+ if (!fixed.project) {
317
+ fixed.project = basename(cwd);
318
+ changed = true;
319
+ }
315
320
  if (!fixed._version || !Number.isInteger(fixed._version) || fixed._version < 1) {
316
321
  fixed._version = CURRENT_VERSION;
317
322
  changed = true;
@@ -39,7 +39,7 @@ export const definition = {
39
39
 
40
40
  ### 操作
41
41
  1. 如果 \`.sillyspec/ROADMAP.md\` 存在,标记对应 Phase 为已完成
42
- 2. \`git add .sillyspec/ && git commit -m "docs: archive sillyspec change <change-name>"\`
42
+ 2. \`git add .sillyspec/\` **不要 commit**,由用户通过统一提交工具处理
43
43
  3. 更新 progress.json:
44
44
  - 清除当前变更信息(归档后不再活跃)
45
45
  - 如果是主变更(有 MASTER.md),标记所有阶段为 ✅,然后清除
@@ -27,17 +27,16 @@ export const definition = {
27
27
  prompt: `加载项目现有上下文,理解代码结构和约定。
28
28
 
29
29
  ### 操作
30
- 1. 检查是否工作区模式:\`ls .sillyspec/projects/*.yaml\`
31
- 2. 工作区模式:加载 CODEBASE-OVERVIEW.md + 共享规范 + 子项目上下文
32
- 3. 单项目模式:加载 PROJECT.md、REQUIREMENTS.md、ROADMAP.md
33
- 4. 棕地项目:读取 docs/<project>/scan/ 下的 STRUCTURE.md、CONVENTIONS.md、ARCHITECTURE.md
34
- 5. 查看进行中的变更:\`ls .sillyspec/changes/ | grep -v archive\`
30
+ 1. 读取 CODEBASE-OVERVIEW.md + 共享规范 + 子项目上下文
31
+ 2. 询问本次需求属于哪个子项目
32
+ 3. 棕地项目:读取 docs/<project>/scan/ 下的 STRUCTURE.md、CONVENTIONS.md、ARCHITECTURE.md
33
+ 4. 查看进行中的变更:\`ls .sillyspec/changes/ | grep -v archive\`
35
34
 
36
35
  ### 输出
37
36
  项目现状理解摘要(3-5 句话,关键约定和架构决策)
38
37
 
39
38
  ### 注意
40
- - 工作区模式下需要询问本次需求属于哪个子项目
39
+ - 始终询问本次需求属于哪个子项目
41
40
  - 棕地项目必须读取数据模型章节`,
42
41
  outputHint: '上下文摘要',
43
42
  optional: false
@@ -0,0 +1,303 @@
1
+ // SillySpec Doctor — 项目自检阶段
2
+ // 检查项通过 prompt 中的 bash 命令执行,此文件仅定义步骤结构
3
+
4
+
5
+ export const definition = {
6
+ name: 'doctor',
7
+ title: '项目自检',
8
+ description: '检查 SillySpec 配置、构建环境和外部依赖',
9
+ auxiliary: true,
10
+ steps: [
11
+ {
12
+ name: 'SillySpec 内部检查',
13
+ prompt: `运行 SillySpec 内部检查。逐项执行以下命令并汇总结果:
14
+
15
+ ### 1. 目录结构完整性
16
+ \`\`\`bash
17
+ # 检查 .sillyspec/ 及子目录
18
+ for d in .sillyspec .sillyspec/projects .sillyspec/docs .sillyspec/changes .sillyspec/.runtime; do
19
+ [ -d "$d" ] && echo "✅ $d" || echo "❌ $d"
20
+ done
21
+ # 检查 progress.json
22
+ [ -f .sillyspec/.runtime/progress.json ] && echo "✅ progress.json 存在" || echo "❌ progress.json 不存在"
23
+ node -e "JSON.parse(require('fs').readFileSync('.sillyspec/.runtime/progress.json','utf8')); console.log('✅ progress.json 可解析')" 2>/dev/null || echo "⚠️ progress.json 不可解析"
24
+ \`\`\`
25
+
26
+ ### 2. 项目配置检查
27
+ \`\`\`bash
28
+ ls .sillyspec/projects/*.yaml 2>/dev/null
29
+ # 对每个 yaml 文件,检查 name 和 path 字段,验证 path 存在
30
+ for f in .sillyspec/projects/*.yaml; do
31
+ [ -f "$f" ] || continue
32
+ name=$(grep '^name:' "$f" | head -1 | sed 's/^name:[[:space:]]*//')
33
+ p=$(grep '^path:' "$f" | head -1 | sed 's/^path:[[:space:]]*//')
34
+ [ -z "$name" ] && echo "⚠️ $(basename $f) — 缺少 name"
35
+ [ -z "$p" ] && echo "⚠️ $(basename $f) — 缺少 path"
36
+ [ -n "$p" ] && [ ! -d "$p" ] && echo "❌ $(basename $f) — path 不存在: $p"
37
+ [ -n "$name" ] && [ -n "$p" ] && [ -d "$p" ] && echo "✅ $(basename $f) — $name ($p)"
38
+ done
39
+ \`\`\`
40
+
41
+ ### 3. 进度数据一致性
42
+ \`\`\`bash
43
+ # 读取 currentChange 并检查目录存在性
44
+ node -e "
45
+ const p = JSON.parse(require('fs').readFileSync('.sillyspec/.runtime/progress.json','utf8'));
46
+ const cc = p.currentChange;
47
+ if (!cc) { console.log('ℹ️ 无当前变更'); process.exit(0); }
48
+ const dir = '.sillyspec/changes/' + cc;
49
+ const exists = require('fs').existsSync(dir);
50
+ console.log(exists ? '✅ currentChange 目录存在: ' + cc : '❌ currentChange 目录不存在: ' + cc);
51
+ // 检查各阶段产出
52
+ const stages = p.stages || {};
53
+ for (const [name, sd] of Object.entries(stages)) {
54
+ if (sd.status === 'completed' && sd.steps.length > 0) {
55
+ const hasOutput = sd.steps.some(s => s.output && s.output.trim());
56
+ console.log(' ' + name + ': ' + (hasOutput ? '✅ 有产出' : '⚠️ 已完成但无产出记录'));
57
+ }
58
+ }
59
+ " 2>/dev/null || echo "⚠️ 无法读取 progress.json"
60
+ \`\`\`
61
+
62
+ ### 4. 孤儿文件检查
63
+ \`\`\`bash
64
+ node -e "
65
+ const fs = require('fs');
66
+ const dir = '.sillyspec/changes';
67
+ if (!fs.existsSync(dir)) { console.log('ℹ️ changes/ 目录不存在'); process.exit(0); }
68
+ const subs = fs.readdirSync(dir).filter(f => fs.statSync(dir+'/'+f).isDirectory());
69
+ if (subs.length === 0) { console.log('ℹ️ 无变更目录'); process.exit(0); }
70
+ let progress;
71
+ try { progress = JSON.parse(fs.readFileSync('.sillyspec/.runtime/progress.json','utf8')); } catch { console.log('⚠️ 无法读取 progress.json'); subs.forEach(s => console.log('❓ ' + s)); process.exit(0); }
72
+ const known = new Set();
73
+ if (progress.currentChange) known.add(progress.currentChange);
74
+ for (const sd of Object.values(progress.stages || {})) {
75
+ (sd.steps || []).forEach(s => { if (s.output) known.add(s.output); });
76
+ }
77
+ subs.forEach(s => {
78
+ console.log(known.has(s) ? '✅ ' + s + ' — 已关联' : '⚠️ ' + s + ' — 孤儿目录(可清理)');
79
+ });
80
+ "
81
+ \`\`\`
82
+
83
+ ### 5. 配置文件检查
84
+ \`\`\`bash
85
+ # 检查 local.yaml 和 STACK.md
86
+ for f in .sillyspec/projects/*.yaml; do
87
+ [ -f "$f" ] || continue
88
+ name=$(grep '^name:' "$f" | head -1 | sed 's/^name:[[:space:]]*//')
89
+ p=$(grep '^path:' "$f" | head -1 | sed 's/^path:[[:space:]]*//')
90
+ [ -z "$p" ] && continue
91
+ local_yaml="$p/.sillyspec/local.yaml"
92
+ stack_md="$p/.sillyspec/STACK.md"
93
+ [ -f "$local_yaml" ] && echo "✅ local.yaml ($name)" || echo "⚠️ local.yaml ($name) — 不存在"
94
+ if [ -f "$local_yaml" ]; then
95
+ grep -q 'build:' "$local_yaml" && echo " ✅ build 命令已配置" || echo " ⚠️ 缺少 build 命令"
96
+ grep -q 'test:' "$local_yaml" && echo " ✅ test 命令已配置" || echo " ⚠️ 缺少 test 命令"
97
+ fi
98
+ [ -f "$stack_md" ] && echo "✅ STACK.md ($name)" || echo "⚠️ STACK.md ($name) — 不存在"
99
+ done
100
+ \`\`\`
101
+
102
+ ### 输出
103
+ 汇总所有检查结果,按以下格式:
104
+ \`\`\`
105
+ ## SillySpec 内部
106
+ ✅/⚠️/❌ 各项状态
107
+ \`\`\`
108
+
109
+ ### 注意
110
+ - 不要编造路径或结果,严格基于命令输出
111
+ - 如果 .sillyspec/ 不存在,直接输出 ❌ 并跳过后续检查`,
112
+ outputHint: 'SillySpec 内部检查结果',
113
+ optional: false
114
+ },
115
+ {
116
+ name: '构建环境检查',
117
+ prompt: `检查项目构建环境。先探测项目使用的构建工具,再逐项检查可用性。
118
+
119
+ ### 1. 探测构建工具
120
+ \`\`\`bash
121
+ # 确定项目路径(使用 progress.json 中的项目或当前目录)
122
+ PROJECT_DIR=$(node -e "
123
+ const fs=require('fs');
124
+ try{const p=JSON.parse(fs.readFileSync('.sillyspec/.runtime/progress.json','utf8'));if(p.project){console.log(p.project);process.exit(0)}}catch{}
125
+ const files=fs.readdirSync('.sillyspec/projects').filter(f=>f.endsWith('.yaml'));
126
+ if(files.length>0){const c=fs.readFileSync('.sillyspec/projects/'+files[0],'utf8');const m=c.match(/^path:\\s*(.+)/m);console.log(m?m[1].trim():'.')}else console.log('.')
127
+ " 2>/dev/null)
128
+ echo "项目目录: $PROJECT_DIR"
129
+
130
+ # 探测构建工具
131
+ for f in pom.xml build.gradle package.json requirements.txt pyproject.toml go.mod Cargo.toml; do
132
+ [ -f "$PROJECT_DIR/$f" ] && echo "检测到: $f"
133
+ done
134
+ [ -f "$PROJECT_DIR/.sillyspec/STACK.md" ] && cat "$PROJECT_DIR/.sillyspec/STACK.md" | head -30
135
+ \`\`\`
136
+
137
+ ### 2. 构建工具可用性
138
+ 根据上面检测到的工具,运行对应检查(未检测到的跳过):
139
+
140
+ **Maven 项目:**
141
+ \`\`\`bash
142
+ timeout 10 mvn -v 2>/dev/null | head -1 && echo "✅ Maven 可用" || echo "❌ Maven 不可用"
143
+ [ -f ~/.m2/settings.xml ] && echo "✅ Maven settings.xml 存在" || echo "⚠️ Maven settings.xml 不存在"
144
+ timeout 10 java -version 2>&1 | head -1
145
+ \`\`\`
146
+
147
+ **Gradle 项目:**
148
+ \`\`\`bash
149
+ timeout 10 gradle -v 2>/dev/null | head -1 && echo "✅ Gradle 可用" || echo "❌ Gradle 不可用"
150
+ \`\`\`
151
+
152
+ **Node.js 项目:**
153
+ \`\`\`bash
154
+ timeout 5 node -v 2>/dev/null && echo "✅ Node.js 可用" || echo "❌ Node.js 不可用"
155
+ timeout 5 npm -v 2>/dev/null && echo "✅ npm 可用" || echo "❌ npm 不可用"
156
+ timeout 5 pnpm -v 2>/dev/null && echo "✅ pnpm 可用" || echo "ℹ️ pnpm 未安装"
157
+ # 检查 registry
158
+ npm config get registry 2>/dev/null
159
+ \`\`\`
160
+
161
+ **Python 项目:**
162
+ \`\`\`bash
163
+ timeout 5 python3 --version 2>/dev/null && echo "✅ Python3 可用" || echo "❌ Python3 不可用"
164
+ timeout 5 pip3 --version 2>/dev/null && echo "✅ pip3 可用" || echo "❌ pip3 不可用"
165
+ \`\`\`
166
+
167
+ ### 3. Maven 私服检查(仅 Maven 项目)
168
+ \`\`\`bash
169
+ # 从 settings.xml 提取仓库地址
170
+ if [ -f ~/.m2/settings.xml ]; then
171
+ grep -oP 'https?://[^<"]+:[0-9]+' ~/.m2/settings.xml 2>/dev/null | sort -u | while read url; do
172
+ timeout 5 curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null
173
+ echo " — $url"
174
+ done
175
+ fi
176
+ # 从 pom.xml 提取
177
+ if [ -f "$PROJECT_DIR/pom.xml" ]; then
178
+ grep -oP 'https?://[^<"]+:[0-9]+' "$PROJECT_DIR/pom.xml" 2>/dev/null | sort -u | while read url; do
179
+ code=$(timeout 5 curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null)
180
+ [ "$code" = "000" ] && echo "❌ 私服不可达: $url(超时)" || echo "✅ 私服可达 ($code): $url"
181
+ done
182
+ fi
183
+ \`\`\`
184
+
185
+ ### 4. 运行时环境
186
+ \`\`\`bash
187
+ timeout 5 node -v 2>/dev/null && echo "Node.js: $(node -v)" || echo "❌ Node.js 未安装"
188
+ timeout 5 git --version 2>/dev/null && echo "Git: $(git --version)" || echo "❌ Git 未安装"
189
+ timeout 10 git remote -v 2>/dev/null | head -2
190
+ timeout 5 git ls-remote --heads origin 2>/dev/null >/dev/null && echo "✅ Git remote 可达" || echo "⚠️ Git remote 不可达"
191
+ timeout 5 java -version 2>&1 | head -1
192
+ timeout 5 python3 --version 2>/dev/null
193
+ \`\`\`
194
+
195
+ ### 输出
196
+ 汇总所有检查结果:
197
+ \`\`\`
198
+ ## 构建环境
199
+ ✅/⚠️/❌ 各项状态
200
+ \`\`\`
201
+
202
+ ### 注意
203
+ - 未检测到的构建工具直接跳过,不要报错
204
+ - timeout 超时的命令视为不可用
205
+ - 不编造结果`,
206
+ outputHint: '构建环境检查结果',
207
+ optional: false
208
+ },
209
+ {
210
+ name: '外部依赖检查',
211
+ prompt: `检查外部依赖工具是否可用。
212
+
213
+ ### 1. Context7 MCP
214
+ \`\`\`bash
215
+ # 检查 MCP 配置
216
+ for f in ~/.config/claude/claude_desktop_config.json ~/.cursor/mcp.json ~/.openclaw/mcp.json; do
217
+ [ -f "$f" ] && echo "MCP 配置文件: $f" && grep -i context7 "$f" 2>/dev/null && echo "✅ Context7 已配置" || true
218
+ done
219
+ # 也检查 sillyspec 自身的 setup
220
+ node -e "
221
+ try{const m=require(require('path').join(require('os').homedir(),'.sillyspec','config.json'));console.log('✅ sillyspec config 存在')}catch{console.log('ℹ️ 无 sillyspec 全局配置')}
222
+ " 2>/dev/null
223
+ \`\`\`
224
+
225
+ ### 2. grep.app
226
+ \`\`\`bash
227
+ timeout 5 curl -s -o /dev/null -w "%{http_code}" https://grep.app 2>/dev/null | grep -q "200" && echo "✅ grep.app 可达" || echo "⚠️ grep.app 不可达"
228
+ \`\`\`
229
+
230
+ ### 3. 其他 AI 工具(可选)
231
+ \`\`\`bash
232
+ # 检查常用 AI/开发工具
233
+ timeout 5 which gh 2>/dev/null && echo "✅ GitHub CLI 可用" || echo "ℹ️ GitHub CLI 未安装"
234
+ timeout 5 which docker 2>/dev/null && echo "✅ Docker 可用" || echo "ℹ️ Docker 未安装"
235
+ \`\`\`
236
+
237
+ ### 输出
238
+ \`\`\`
239
+ ## 外部依赖
240
+ ✅/⚠️/❌ 各项状态
241
+ \`\`\`
242
+
243
+ ### 注意
244
+ - 不编造结果
245
+ - 工具未安装用 ℹ️ 标记(非错误),不可达用 ⚠️`,
246
+ outputHint: '外部依赖检查结果',
247
+ optional: false
248
+ },
249
+ {
250
+ name: '汇总报告',
251
+ prompt: `汇总前三步的所有检查结果,生成最终的自检报告。
252
+
253
+ ### 输出格式
254
+ \`\`\`
255
+ 🔍 SillySpec Doctor — 项目自检报告
256
+
257
+ ## SillySpec 内部
258
+ ✅ .sillyspec/ 目录结构 — 正常
259
+ ✅ projects/*.yaml — N 个项目已注册
260
+ ⚠️ local.yaml (xxx) — 缺少 test 命令
261
+ ❌ progress.json — brainstorm 标记完成但 design.md 不存在
262
+
263
+ ## 构建环境
264
+ ✅ Node.js v23.4.0 — 可用
265
+ ✅ npm 10.x — 可用
266
+ ✅ Java 17.0.2 — 可用
267
+ ❌ Maven 私服 (10.0.0.1:8081) — 不可达(超时)
268
+
269
+ ## 外部依赖
270
+ ✅ Context7 MCP — 已配置
271
+ ⚠️ grep.app — 不可达
272
+ \`\`\`
273
+
274
+ ### 要求
275
+ - 基于前 3 步的实际输出汇总,不要编造
276
+ - 每类问题归入对应分区
277
+ - 全部通过给出 🎉
278
+ - 如果有 ❌ 或 ⚠️,在末尾逐项给出修复建议
279
+
280
+ ### 修复建议模板
281
+ 根据问题类型给出具体可操作的修复命令:
282
+
283
+ **常见问题及修复:**
284
+ - CLI 未安装 → \`npm install -g sillyspec\` 或 \`npx sillyspec\`
285
+ - 缺少 local.yaml → \`sillyspec init\` 重新生成,或手动创建
286
+ - local.yaml 缺少 build/test → 补充对应命令
287
+ - 缺少 STACK.md → \`sillyspec run scan\` 重新扫描
288
+ - progress.json 不一致 → \`sillyspec run <阶段> --reset\` 重置对应阶段
289
+ - 孤儿目录 → 确认后 \`rm -rf .sillyspec/changes/<目录名>\`
290
+ - Maven 私服不可达 → 检查 VPN、settings.xml 配置、私服状态
291
+ - Git remote 不可达 → 检查网络、SSH key 或凭证
292
+ - 工具未安装 → 给出安装命令(如 \`brew install maven\`)
293
+
294
+ 每条建议格式:
295
+ \`\`\`
296
+ 💡 修复:<问题描述>
297
+ <具体命令或操作>
298
+ \`\`\``,
299
+ outputHint: '完整自检报告',
300
+ optional: false
301
+ }
302
+ ]
303
+ }
@@ -33,7 +33,7 @@ const fixedPrefix = [
33
33
  2. 读取 design.md(技术方案)
34
34
  3. 读取 CONVENTIONS.md、ARCHITECTURE.md
35
35
  4. 读取 local.yaml(构建命令)
36
- 5. 工作区模式:额外加载 CODEBASE-OVERVIEW.md
36
+ 5. 加载 CODEBASE-OVERVIEW.md
37
37
 
38
38
  ### 输出
39
39
  已加载的上下文摘要`,
@@ -7,6 +7,7 @@ import { definition as scan } from './scan.js'
7
7
  import { definition as quick } from './quick.js'
8
8
  import { definition as archive } from './archive.js'
9
9
  import { definition as status } from './status.js'
10
+ import { definition as doctor } from './doctor.js'
10
11
 
11
12
  export const stageRegistry = {
12
13
  brainstorm,
@@ -17,7 +18,8 @@ export const stageRegistry = {
17
18
  scan,
18
19
  quick,
19
20
  archive,
20
- status
21
+ status,
22
+ doctor
21
23
  }
22
24
 
23
25
  // 流程阶段顺序,用于 getNextStage
@@ -30,4 +32,4 @@ export function getNextStage(currentStage) {
30
32
  }
31
33
 
32
34
  // 辅助命令(不影响流程阶段推进)
33
- export const auxiliaryStages = ['scan', 'quick', 'archive', 'status']
35
+ export const auxiliaryStages = ['scan', 'quick', 'archive', 'status', 'doctor']
@@ -21,10 +21,9 @@ export const definition = {
21
21
  prompt: `加载所有规范文件和代码库上下文。
22
22
 
23
23
  ### 操作
24
- 1. 检测工作区模式
24
+ 1. 读取 CODEBASE-OVERVIEW.md + 各子项目上下文
25
25
  2. 读取 proposal.md、design.md、tasks.md、requirements.md
26
26
  3. 读取 CONVENTIONS.md、ARCHITECTURE.md、STACK.md
27
- 4. 工作区模式:额外加载 CODEBASE-OVERVIEW.md + 各子项目上下文
28
27
 
29
28
  ### 输出
30
29
  已加载的文件清单`,
@@ -22,7 +22,7 @@ export const definition = {
22
22
  prompt: `加载所有相关规范和代码库上下文。
23
23
 
24
24
  ### 操作
25
- 1. 检测工作区模式
25
+ 1. 加载 CODEBASE-OVERVIEW.md 和子项目上下文
26
26
  2. 读取最新设计文档、需求文档、代码库约定
27
27
  3. 如果是子阶段变更,读取 MASTER.md 和前序阶段设计
28
28
 
@@ -5,18 +5,18 @@ export const definition = {
5
5
  auxiliary: true,
6
6
  steps: [
7
7
  {
8
- name: '检查工作区和已有文档',
9
- prompt: `检查工作区模式和已有扫描文档。
8
+ name: '检查已有扫描文档和子项目列表',
9
+ prompt: `检查已有扫描文档和子项目列表。
10
10
 
11
11
  ### 操作
12
- 1. \`ls .sillyspec/projects/*.yaml 2>/dev/null | grep -q .\` — 判断是否工作区模式
13
- 2. \`ls docs/*/scan/ 2>/dev/null\` — 检查已有文档
14
- 3. \`wc -l docs/*/scan/*.md 2>/dev/null\` — 文档行数
15
- 4. 已有 3 份 → 建议升级深度扫描;已有 7 份 → 建议刷新或跳过
16
- 5. 工作区模式 → 询问扫描范围(逐个/选子项目/退出)
12
+ 1. \`ls .sillyspec/projects/*.yaml 2>/dev/null | grep -q .\` — 检查已有文档
13
+ 1. \`ls docs/*/scan/ 2>/dev/null\` — 检查已有文档
14
+ 2. \`wc -l docs/*/scan/*.md 2>/dev/null\` — 文档行数
15
+ 3. 已有 3 份 → 建议升级深度扫描;已有 7 份 → 建议刷新或跳过
16
+ 5. 显示子项目列表供选择扫描范围
17
17
 
18
18
  ### 输出
19
- 工作区模式判断 + 已有文档状态 + 扫描建议`,
19
+ 已有文档状态 + 扫描建议`,
20
20
  outputHint: '工作区和文档状态',
21
21
  optional: false
22
22
  },
@@ -127,7 +127,7 @@ TESTING.md、CONCERNS.md、PROJECT.md 路径`,
127
127
  1. 检查 7 份文档是否全部生成
128
128
  2. 自检门控:ARCHITECTURE(技术栈+Schema摘要)、CONVENTIONS(隐形规则+代码风格)、STRUCTURE(目录结构)、INTEGRATIONS(外部依赖)、TESTING(测试现状)、CONCERNS(技术债务)、PROJECT(项目概览)
129
129
  3. 清理:\`rm -f docs/<project>/scan/_env-detect.md\`
130
- 4. \`git add . && git commit -m "chore: sillyspec scan - codebase mapped"\`
130
+ 4. \`git add .\` **不要 commit**,由用户通过统一提交工具处理
131
131
 
132
132
  ### 输出
133
133
  扫描完整性报告 + commit hash
@@ -4,33 +4,6 @@ export const definition = {
4
4
  description: '查看项目进度和状态',
5
5
  auxiliary: true,
6
6
  steps: [
7
- {
8
- name: '检查工作区模式',
9
- prompt: `判断是否为工作区模式。
10
-
11
- ### 操作
12
- 1. \`ls .sillyspec/projects/*.yaml 2>/dev/null | grep -q .\`
13
- 2. 是 → 工作区模式,对每个子项目执行状态检查
14
- 3. 否 → 单项目模式,继续后续步骤
15
-
16
- ### 工作区模式输出格式:
17
- \`\`\`
18
- 🏢 工作区状态
19
- 📦 子项目:
20
- ✅ frontend ./frontend
21
- 📋 项目:已初始化
22
- 📂 代码库:已扫描(7 份文档)
23
- 🔄 进行中:1 个变更
24
- ⚠️ backend ./backend
25
- 📂 代码库:未扫描
26
- 📄 共享规范:2 份
27
- \`\`\`
28
-
29
- ### 输出
30
- 工作区/单项目判断`,
31
- outputHint: '模式判断',
32
- optional: false
33
- },
34
7
  {
35
8
  name: '项目基础信息',
36
9
  prompt: `收集项目基础信息。