sillyspec 3.8.4 → 3.8.6
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 +0 -6
- package/docs/.vitepress/config.mts +45 -0
- package/docs/.vitepress/dist/404.html +25 -0
- package/docs/.vitepress/dist/assets/app.YytxICdd.js +1 -0
- package/docs/.vitepress/dist/assets/chunks/framework.Czhw_PXq.js +19 -0
- package/docs/.vitepress/dist/assets/chunks/theme.DusTRZQk.js +1 -0
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.js +1 -0
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.lean.js +1 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.js +15 -0
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.js +4 -0
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.js +4 -0
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.js +5 -0
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.js +28 -0
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.js +30 -0
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.lean.js +1 -0
- package/docs/.vitepress/dist/assets/style.DFTx90Kk.css +1 -0
- package/docs/.vitepress/dist/hashmap.json +1 -0
- package/docs/.vitepress/dist/index.html +28 -0
- package/docs/.vitepress/dist/sillyspec/commands.html +42 -0
- package/docs/.vitepress/dist/sillyspec/dashboard.html +31 -0
- package/docs/.vitepress/dist/sillyspec/file-io.html +28 -0
- package/docs/.vitepress/dist/sillyspec/getting-started.html +31 -0
- package/docs/.vitepress/dist/sillyspec/install.html +32 -0
- package/docs/.vitepress/dist/sillyspec/lifecycle.html +55 -0
- package/docs/.vitepress/dist/sillyspec/structure.html +57 -0
- package/docs/.vitepress/dist/vp-icons.css +1 -0
- package/docs/index.md +34 -0
- package/docs/sillyspec/commands.md +218 -0
- package/docs/sillyspec/dashboard.md +51 -0
- package/docs/sillyspec/file-io.md +34 -0
- package/docs/sillyspec/getting-started.md +61 -0
- package/docs/sillyspec/install.md +51 -0
- package/docs/sillyspec/lifecycle.md +146 -0
- package/docs/sillyspec/structure.md +62 -0
- package/package.json +11 -9
- package/packages/dashboard/dist/assets/index-Bh-GPjKY.css +1 -0
- package/packages/dashboard/dist/assets/index-CrCn5Gg6.js +17 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package-lock.json +0 -220
- package/packages/dashboard/package.json +5 -8
- package/packages/dashboard/server/index.js +106 -255
- package/packages/dashboard/server/parser.js +29 -333
- package/packages/dashboard/server/watcher.js +131 -203
- package/packages/dashboard/src/App.vue +10 -181
- package/packages/dashboard/src/components/ActionBar.vue +42 -26
- package/packages/dashboard/src/components/CommandPalette.vue +65 -40
- package/packages/dashboard/src/components/DetailPanel.vue +53 -68
- package/packages/dashboard/src/components/LogStream.vue +33 -13
- package/packages/dashboard/src/components/PipelineStage.vue +8 -8
- package/packages/dashboard/src/components/PipelineView.vue +45 -80
- package/packages/dashboard/src/components/ProjectList.vue +45 -103
- package/packages/dashboard/src/components/StageBadge.vue +13 -13
- package/packages/dashboard/src/components/StepCard.vue +15 -15
- package/packages/dashboard/src/composables/useDashboard.js +6 -20
- package/packages/dashboard/src/composables/useKeyboard.js +4 -6
- package/packages/dashboard/src/main.js +1 -4
- package/packages/dashboard/src/style.css +17 -17
- package/src/index.js +12 -123
- package/src/init.js +227 -86
- package/src/setup.js +9 -1
- package/templates/archive.md +121 -0
- package/templates/brainstorm.md +240 -0
- package/{.claude/skills/sillyspec-commit/SKILL.md → templates/commit.md} +47 -29
- package/templates/continue.md +32 -0
- package/templates/execute.md +314 -0
- package/templates/explore.md +60 -0
- package/templates/export.md +21 -0
- package/templates/init.md +61 -0
- package/templates/plan.md +157 -0
- package/templates/quick.md +135 -0
- package/templates/scan-quick.md +49 -0
- package/templates/scan.md +172 -0
- package/templates/skills/playwright-e2e/SKILL.md +340 -0
- package/templates/status.md +75 -0
- package/templates/verify.md +253 -0
- package/templates/workspace-sync.md +99 -0
- package/templates/workspace.md +70 -0
- package/.claude/skills/sillyspec-archive/SKILL.md +0 -17
- package/.claude/skills/sillyspec-auto/SKILL.md +0 -77
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -17
- package/.claude/skills/sillyspec-continue/SKILL.md +0 -44
- package/.claude/skills/sillyspec-doctor/SKILL.md +0 -22
- package/.claude/skills/sillyspec-execute/SKILL.md +0 -17
- package/.claude/skills/sillyspec-explore/SKILL.md +0 -96
- package/.claude/skills/sillyspec-export/SKILL.md +0 -53
- package/.claude/skills/sillyspec-init/SKILL.md +0 -170
- package/.claude/skills/sillyspec-plan/SKILL.md +0 -52
- package/.claude/skills/sillyspec-propose/SKILL.md +0 -17
- package/.claude/skills/sillyspec-quick/SKILL.md +0 -17
- package/.claude/skills/sillyspec-resume/SKILL.md +0 -111
- package/.claude/skills/sillyspec-scan/SKILL.md +0 -17
- package/.claude/skills/sillyspec-state/SKILL.md +0 -54
- package/.claude/skills/sillyspec-status/SKILL.md +0 -17
- package/.claude/skills/sillyspec-verify/SKILL.md +0 -17
- package/.claude/skills/sillyspec-workspace/SKILL.md +0 -149
- package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +0 -97
- package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +0 -51
- package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +0 -29
- package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +0 -34
- package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +0 -13
- package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +0 -43
- package/.sillyspec/changes/auto-mode/design.md +0 -50
- package/.sillyspec/changes/auto-mode/proposal.md +0 -19
- package/.sillyspec/changes/auto-mode/requirements.md +0 -21
- package/.sillyspec/changes/auto-mode/tasks.md +0 -7
- package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +0 -199
- package/.sillyspec/changes/dashboard/design.md.braindraft +0 -206
- package/.sillyspec/changes/run-command-design/design.md +0 -1230
- package/.sillyspec/changes/unified-docs-design/design.md +0 -199
- package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
- package/.sillyspec/knowledge/INDEX.md +0 -8
- package/.sillyspec/knowledge/uncategorized.md +0 -3
- package/.sillyspec/projects/sillyspec.yaml +0 -3
- package/packages/dashboard/dist/assets/index-D1EVTLmc.js +0 -7446
- package/packages/dashboard/dist/assets/index-DGe8CqeP.css +0 -1
- package/packages/dashboard/public/logo.jpg +0 -0
- package/packages/dashboard/src/components/DocPreview.vue +0 -160
- package/packages/dashboard/src/components/DocTree.vue +0 -58
- package/packages/dashboard/src/components/ProjectOverview.vue +0 -178
- package/packages/dashboard/src/components/detail/DocsDetail.vue +0 -48
- package/packages/dashboard/src/components/detail/GitDetail.vue +0 -61
- package/packages/dashboard/src/components/detail/TechDetail.vue +0 -43
- package/src/derive.js +0 -147
- package/src/migrate.js +0 -117
- package/src/progress.js +0 -495
- package/src/run.js +0 -640
- package/src/stages/archive.js +0 -54
- package/src/stages/brainstorm.js +0 -239
- package/src/stages/doctor.js +0 -312
- package/src/stages/execute.js +0 -258
- package/src/stages/index.js +0 -35
- package/src/stages/plan.js +0 -259
- package/src/stages/propose.js +0 -115
- package/src/stages/quick.js +0 -64
- package/src/stages/scan.js +0 -141
- package/src/stages/status.js +0 -65
- package/src/stages/verify.js +0 -135
- /package/.sillyspec/{changes/brainstorm-archive → specs}/2026-04-05-dashboard-design.md +0 -0
- /package/{packages/dashboard → docs/.vitepress}/dist/favicon.jpg +0 -0
- /package/{logo.jpg → docs/.vitepress/dist/logo.jpg} +0 -0
- /package/{packages/dashboard → docs}/public/favicon.jpg +0 -0
- /package/{packages/dashboard/dist → docs/public}/logo.jpg +0 -0
package/src/init.js
CHANGED
|
@@ -1,32 +1,55 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync,
|
|
2
|
-
import { join, resolve, dirname
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
|
|
2
|
+
import { join, resolve, dirname } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
|
-
import { checkbox, confirm, input } from '@inquirer/prompts';
|
|
6
|
-
import { ProgressManager } from './progress.js';
|
|
5
|
+
import { checkbox, select, confirm, input } from '@inquirer/prompts';
|
|
7
6
|
import chalk from 'chalk';
|
|
7
|
+
import ora from 'ora';
|
|
8
8
|
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = dirname(__filename);
|
|
11
|
-
|
|
12
|
-
// ── 递归复制目录 ──
|
|
13
|
-
function copyDirSync(src, dst) {
|
|
14
|
-
mkdirSync(dst, { recursive: true });
|
|
15
|
-
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
16
|
-
if (entry.name === 'node_modules' || entry.name === '.git') continue;
|
|
17
|
-
const srcPath = join(src, entry.name);
|
|
18
|
-
const dstPath = join(dst, entry.name);
|
|
19
|
-
if (entry.isDirectory()) {
|
|
20
|
-
copyDirSync(srcPath, dstPath);
|
|
21
|
-
} else if (entry.name.endsWith('.md')) {
|
|
22
|
-
writeFileSync(dstPath, readFileSync(srcPath));
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
11
|
+
const TEMPLATE_DIR = resolve(__dirname, '..', 'templates');
|
|
26
12
|
|
|
27
13
|
// ── 元数据映射 ──
|
|
28
14
|
|
|
15
|
+
const DESCRIPTIONS = {
|
|
16
|
+
init: '绿地项目初始化 — 深度提问、调研、需求文档、路线图',
|
|
17
|
+
scan: '代码库扫描 — 支持快速扫描和深度扫描两阶段',
|
|
18
|
+
explore: '自由思考模式 — 讨论、画图、调研,不写代码',
|
|
19
|
+
brainstorm: '需求探索 — 结构化头脑风暴,生成设计文档(创建性工作前必用)',
|
|
20
|
+
plan: '编写实现计划 — 任务拆分与实现路径',
|
|
21
|
+
execute: '波次执行 — 子代理并行 + 强制 TDD + 两阶段审查',
|
|
22
|
+
verify: '验证实现 — 对照规范检查 + 测试套件',
|
|
23
|
+
archive: '归档变更 — 规范沉淀,可追溯',
|
|
24
|
+
commit: '智能提交 — 自动收集变更信息,生成 commit message',
|
|
25
|
+
status: '查看项目进度和状态',
|
|
26
|
+
continue: '自动判断并执行下一步',
|
|
27
|
+
state: '查看当前工作状态 — 显示 STATE.md 内容',
|
|
28
|
+
|
|
29
|
+
quick: '快速任务 — 跳过完整流程,直接做',
|
|
30
|
+
workspace: '工作区管理 — 初始化、管理多项目工作区,查看子项目状态',
|
|
31
|
+
export: '导出成功方案为可复用模板',
|
|
32
|
+
};
|
|
29
33
|
|
|
34
|
+
const ARG_HINTS = {
|
|
35
|
+
init: '[项目名]',
|
|
36
|
+
scan: '[可选:指定区域,如 \'api\' 或 \'auth\'] [--deep 深度扫描]',
|
|
37
|
+
explore: '[探索主题]',
|
|
38
|
+
brainstorm: '[需求或想法描述]',
|
|
39
|
+
|
|
40
|
+
plan: '[计划名]',
|
|
41
|
+
execute: '[任务编号或 \'all\']',
|
|
42
|
+
verify: '[可选:指定验证范围]',
|
|
43
|
+
archive: '[变更名]',
|
|
44
|
+
commit: '[可选:自定义 commit message]',
|
|
45
|
+
status: '',
|
|
46
|
+
continue: '',
|
|
47
|
+
state: '[可选备注]',
|
|
48
|
+
|
|
49
|
+
quick: '[任务描述]',
|
|
50
|
+
workspace: '[可选:add/remove/status/info]',
|
|
51
|
+
export: '<change-name> [--to <path>]',
|
|
52
|
+
};
|
|
30
53
|
|
|
31
54
|
const VALID_TOOLS = ['claude', 'claude_skills', 'cursor', 'openclaw', 'codex', 'gemini', 'opencode'];
|
|
32
55
|
|
|
@@ -40,6 +63,8 @@ const TOOL_LABELS = {
|
|
|
40
63
|
opencode: 'OpenCode (通过 INSTRUCTIONS.md)',
|
|
41
64
|
};
|
|
42
65
|
|
|
66
|
+
|
|
67
|
+
// 指令文件工具:注入规范引用到指令文件
|
|
43
68
|
const INSTRUCTION_TOOLS = ['codex', 'gemini', 'opencode'];
|
|
44
69
|
|
|
45
70
|
const INSTRUCTION_FILE_MAP = {
|
|
@@ -53,16 +78,83 @@ const INJECTION_CONTENT = `## SillySpec — 规范驱动开发
|
|
|
53
78
|
在执行开发任务时,遵循以下规范:
|
|
54
79
|
|
|
55
80
|
### 代码规范
|
|
56
|
-
- 写代码前先读取 \`.sillyspec/
|
|
81
|
+
- 写代码前先读取 \`.sillyspec/codebase/CONVENTIONS.md\`(代码风格)和 \`.sillyspec/codebase/ARCHITECTURE.md\`(架构)
|
|
57
82
|
- 调用已有方法前,用 grep 确认方法存在,不许编造
|
|
58
|
-
- 遵循 \`.sillyspec/
|
|
83
|
+
- 遵循 \`.sillyspec/codebase/CONVENTIONS.md\` 中的代码风格
|
|
59
84
|
|
|
60
85
|
### 工作流程
|
|
61
|
-
- 读取 \`.sillyspec
|
|
86
|
+
- 读取 \`.sillyspec/STATE.md\` 确认当前阶段
|
|
62
87
|
- 各阶段产出文件位于 \`.sillyspec/changes/<变更名>/\` 下
|
|
88
|
+
- 详细流程参考模板文件:\`.sillyspec/.templates/\`(brainstorm.md, plan.md, execute.md 等)
|
|
63
89
|
`;
|
|
64
90
|
|
|
65
|
-
// ──
|
|
91
|
+
// ── 适配器 ──
|
|
92
|
+
|
|
93
|
+
function generateClaude(projectDir, name, desc, body, argHint, version) {
|
|
94
|
+
const outDir = join(projectDir, '.claude', 'commands', 'sillyspec');
|
|
95
|
+
mkdirSync(outDir, { recursive: true });
|
|
96
|
+
writeFileSync(join(outDir, `${name}.md`),
|
|
97
|
+
`---
|
|
98
|
+
description: ${desc}
|
|
99
|
+
argument-hint: "${argHint}"
|
|
100
|
+
version: "${version}"
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
${body}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function generateClaudeSkills(projectDir, name, desc, body, argHint, version) {
|
|
108
|
+
const outDir = join(projectDir, '.claude', 'skills', `sillyspec-${name}`);
|
|
109
|
+
mkdirSync(outDir, { recursive: true });
|
|
110
|
+
writeFileSync(join(outDir, 'SKILL.md'),
|
|
111
|
+
`---
|
|
112
|
+
name: sillyspec:${name}
|
|
113
|
+
description: ${desc}
|
|
114
|
+
version: "${version}"
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
${body}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function generateCursor(projectDir, name, desc, body, argHint, version) {
|
|
122
|
+
const outDir = join(projectDir, '.cursor', 'commands');
|
|
123
|
+
mkdirSync(outDir, { recursive: true });
|
|
124
|
+
writeFileSync(join(outDir, `sillyspec-${name}.md`),
|
|
125
|
+
`---
|
|
126
|
+
name: /sillyspec-${name}
|
|
127
|
+
id: sillyspec-${name}
|
|
128
|
+
description: ${desc}
|
|
129
|
+
version: "${version}"
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
${body}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function generateOpenclaw(projectDir, name, desc, body, argHint, version) {
|
|
137
|
+
const outDir = join(projectDir, '.openclaw', 'skills', `sillyspec-${name}`);
|
|
138
|
+
mkdirSync(outDir, { recursive: true });
|
|
139
|
+
writeFileSync(join(outDir, 'SKILL.md'),
|
|
140
|
+
`---
|
|
141
|
+
name: sillyspec:${name}
|
|
142
|
+
description: ${desc}
|
|
143
|
+
version: "${version}"
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
${body}`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const GENERATORS = {
|
|
151
|
+
claude: generateClaude,
|
|
152
|
+
claude_skills: generateClaudeSkills,
|
|
153
|
+
cursor: generateCursor,
|
|
154
|
+
openclaw: generateOpenclaw,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// ── 指令文件注入 ──
|
|
66
158
|
|
|
67
159
|
function injectInstructions(tool, projectDir) {
|
|
68
160
|
const fileName = INSTRUCTION_FILE_MAP[tool];
|
|
@@ -106,33 +198,22 @@ function isTTY() {
|
|
|
106
198
|
|
|
107
199
|
// ── 核心安装逻辑 ──
|
|
108
200
|
|
|
109
|
-
async function doInstall(projectDir, tools, subprojects = []) {
|
|
201
|
+
async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
|
|
110
202
|
// 创建基础目录
|
|
111
|
-
// .sillyspec/
|
|
112
|
-
// .sillyspec/
|
|
113
|
-
// .sillyspec/
|
|
114
|
-
// .sillyspec
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
writeFileSync(projectYamlPath, `name: ${projectName}\npath: .\nstatus: active\n`);
|
|
203
|
+
// .sillyspec/codebase/ → scan
|
|
204
|
+
// .sillyspec/codebase/details/ → scan (deep)
|
|
205
|
+
// .sillyspec/changes/ → brainstorm/plan
|
|
206
|
+
// .sillyspec/changes/archive/ → archive
|
|
207
|
+
// .sillyspec/quicklog/ → quick
|
|
208
|
+
// .sillyspec/knowledge/ → archive (spec 沉淀)
|
|
209
|
+
// .sillyspec/.runtime/ → runtime data (gitignored)
|
|
210
|
+
// (plan 内容已合并到 tasks.md)
|
|
211
|
+
if (isWorkspace) {
|
|
212
|
+
mkdirSync(join(projectDir, '.sillyspec', 'shared'), { recursive: true });
|
|
213
|
+
mkdirSync(join(projectDir, '.sillyspec', 'workspace'), { recursive: true });
|
|
123
214
|
}
|
|
124
215
|
|
|
125
|
-
//
|
|
126
|
-
const scanDir = join(projectDir, '.sillyspec', 'docs', projectName, 'scan');
|
|
127
|
-
mkdirSync(scanDir, { recursive: true });
|
|
128
|
-
const gitkeepPath = join(scanDir, '.gitkeep');
|
|
129
|
-
if (!existsSync(gitkeepPath)) writeFileSync(gitkeepPath, '');
|
|
130
|
-
|
|
131
|
-
// 创建 shared/workspace 目录
|
|
132
|
-
mkdirSync(join(projectDir, '.sillyspec', 'shared'), { recursive: true });
|
|
133
|
-
mkdirSync(join(projectDir, '.sillyspec', 'workspace'), { recursive: true });
|
|
134
|
-
|
|
135
|
-
// 创建知识库骨架
|
|
216
|
+
// 创建知识库骨架(所有模式)
|
|
136
217
|
const knowledgeDir = join(projectDir, '.sillyspec', 'knowledge');
|
|
137
218
|
mkdirSync(knowledgeDir, { recursive: true });
|
|
138
219
|
const indexPath = join(knowledgeDir, 'INDEX.md');
|
|
@@ -150,13 +231,6 @@ async function doInstall(projectDir, tools, subprojects = []) {
|
|
|
150
231
|
mkdirSync(join(runtimeDir, sub), { recursive: true });
|
|
151
232
|
}
|
|
152
233
|
|
|
153
|
-
// 创建初始 progress.json
|
|
154
|
-
const progressPath = join(runtimeDir, 'progress.json');
|
|
155
|
-
if (!existsSync(progressPath)) {
|
|
156
|
-
const pm = new ProgressManager();
|
|
157
|
-
pm.init(projectDir);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
234
|
// 创建初始 user-inputs.md
|
|
161
235
|
const inputsPath = join(runtimeDir, 'user-inputs.md');
|
|
162
236
|
if (!existsSync(inputsPath)) {
|
|
@@ -164,7 +238,7 @@ async function doInstall(projectDir, tools, subprojects = []) {
|
|
|
164
238
|
}
|
|
165
239
|
|
|
166
240
|
const gitignorePath = join(projectDir, '.gitignore');
|
|
167
|
-
const ignoreRules = ['.sillyspec/codebase/SCAN-RAW.md', '.sillyspec/local.yaml', '.sillyspec/.runtime/'];
|
|
241
|
+
const ignoreRules = ['.sillyspec/STATE.md', '.sillyspec/codebase/SCAN-RAW.md', '.sillyspec/local.yaml', '.sillyspec/.runtime/'];
|
|
168
242
|
if (existsSync(gitignorePath)) {
|
|
169
243
|
const content = readFileSync(gitignorePath, 'utf8');
|
|
170
244
|
let updated = content.trimEnd();
|
|
@@ -178,38 +252,83 @@ async function doInstall(projectDir, tools, subprojects = []) {
|
|
|
178
252
|
writeFileSync(gitignorePath, ignoreRules.join('\n') + '\n');
|
|
179
253
|
}
|
|
180
254
|
|
|
181
|
-
//
|
|
255
|
+
// 生成 slash command 文件
|
|
256
|
+
const templateFiles = readdirSync(TEMPLATE_DIR).filter(f => f.endsWith('.md'));
|
|
257
|
+
let count = 0;
|
|
258
|
+
|
|
182
259
|
for (let i = 0; i < tools.length; i++) {
|
|
183
260
|
const toolName = tools[i];
|
|
261
|
+
const label = TOOL_LABELS[toolName] || toolName;
|
|
262
|
+
|
|
184
263
|
if (INSTRUCTION_TOOLS.includes(toolName)) {
|
|
185
|
-
|
|
264
|
+
const spinner = ora(`安装 ${label}... (${i + 1}/${tools.length})`).start();
|
|
265
|
+
try {
|
|
266
|
+
injectInstructions(toolName, projectDir);
|
|
267
|
+
// 复制模板文件到 .sillyspec/.templates/
|
|
268
|
+
const templatesSourceDir = join(TEMPLATE_DIR);
|
|
269
|
+
const templatesDir = join(projectDir, '.sillyspec', '.templates');
|
|
270
|
+
if (!existsSync(templatesDir)) {
|
|
271
|
+
mkdirSync(templatesDir, { recursive: true });
|
|
272
|
+
for (const file of readdirSync(templatesSourceDir)) {
|
|
273
|
+
if (file.endsWith('.md')) {
|
|
274
|
+
copyFileSync(join(templatesSourceDir, file), join(templatesDir, file));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
spinner.succeed(`${label} 完成`);
|
|
279
|
+
count++;
|
|
280
|
+
} catch (err) {
|
|
281
|
+
spinner.fail(`${label} 失败: ${err.message}`);
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const spinner = ora(`安装 ${label}... (${i + 1}/${tools.length})`).start();
|
|
288
|
+
try {
|
|
289
|
+
const gen = GENERATORS[toolName];
|
|
290
|
+
const ver = getVersion();
|
|
291
|
+
for (const file of templateFiles) {
|
|
292
|
+
const name = file.replace('.md', '');
|
|
293
|
+
const desc = DESCRIPTIONS[name] || `SillySpec ${name}`;
|
|
294
|
+
const argHint = ARG_HINTS[name] || '';
|
|
295
|
+
const body = readFileSync(join(TEMPLATE_DIR, file), 'utf8');
|
|
296
|
+
gen(projectDir, name, desc, body, argHint, ver);
|
|
297
|
+
count++;
|
|
298
|
+
}
|
|
299
|
+
spinner.succeed(`${label} 完成`);
|
|
300
|
+
} catch (err) {
|
|
301
|
+
spinner.fail(`${label} 失败: ${err.message}`);
|
|
302
|
+
throw err;
|
|
186
303
|
}
|
|
187
304
|
}
|
|
188
305
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
306
|
+
// 工作区子项目配置 → projects/*.yaml
|
|
307
|
+
if (isWorkspace && subprojects.length > 0) {
|
|
308
|
+
const projectsDir = join(projectDir, '.sillyspec', 'projects');
|
|
309
|
+
mkdirSync(projectsDir, { recursive: true });
|
|
310
|
+
for (const p of subprojects) {
|
|
311
|
+
const pFile = join(projectsDir, `${p.name}.yaml`);
|
|
312
|
+
if (!existsSync(pFile)) {
|
|
313
|
+
writeFileSync(pFile,
|
|
314
|
+
`# ${p.name}
|
|
315
|
+
name: ${p.name}
|
|
316
|
+
path: ${p.path}
|
|
317
|
+
role: ${p.role || p.name}${p.repo ? `\nrepo: ${p.repo}` : ''}
|
|
318
|
+
`
|
|
319
|
+
);
|
|
201
320
|
}
|
|
202
|
-
console.log(chalk.green(' ✓ Claude Code skills 已同步 (' + sillyspecSkills.length + ' 个)'));
|
|
203
321
|
}
|
|
204
|
-
} else {
|
|
205
|
-
console.log(chalk.yellow(' ⚠ 未找到 skills 目录,跳过 Claude Code skills 同步'));
|
|
206
322
|
}
|
|
323
|
+
|
|
324
|
+
return count;
|
|
207
325
|
}
|
|
208
326
|
|
|
209
327
|
// ── 安装完成总结 ──
|
|
210
328
|
|
|
211
|
-
function showSummary(version, tools) {
|
|
329
|
+
function showSummary(version, tools, isWorkspace, count) {
|
|
212
330
|
const toolLabels = tools.map(t => TOOL_LABELS[t] || t);
|
|
331
|
+
const mode = isWorkspace ? '多项目工作区' : '单项目';
|
|
213
332
|
|
|
214
333
|
console.log('');
|
|
215
334
|
console.log(chalk.green(' ═══════════════════════════════════════'));
|
|
@@ -217,13 +336,20 @@ function showSummary(version, tools) {
|
|
|
217
336
|
console.log(chalk.green(' ═══════════════════════════════════════'));
|
|
218
337
|
console.log('');
|
|
219
338
|
console.log(` 已安装工具: ${chalk.cyan(toolLabels.join(', '))}`);
|
|
339
|
+
console.log(` 模式: ${chalk.yellow(mode)}`);
|
|
340
|
+
console.log('');
|
|
341
|
+
console.log(` 📄 ${count} 个命令已就绪`);
|
|
220
342
|
console.log(' 📁 .sillyspec/ — 项目规范目录');
|
|
221
343
|
console.log('');
|
|
222
|
-
console.log('
|
|
223
|
-
console.log('
|
|
224
|
-
console.log('
|
|
344
|
+
console.log(' 入口选择:');
|
|
345
|
+
console.log(' 全新项目:' + chalk.bold('/sillyspec:init'));
|
|
346
|
+
console.log(' 已有代码:' + chalk.bold('/sillyspec:scan'));
|
|
347
|
+
console.log(' 自由思考:' + chalk.bold('/sillyspec:explore "你的想法"'));
|
|
348
|
+
console.log('');
|
|
349
|
+
console.log(chalk.gray(' 重启你的 AI 工具以使 slash commands 生效。'));
|
|
225
350
|
console.log('');
|
|
226
351
|
console.log(chalk.dim(' 💡 推荐安装 MCP 工具增强 AI 能力:sillyspec setup'));
|
|
352
|
+
console.log(chalk.dim(' Context7 — 查最新文档 | grep.app — 搜开源实现 | Chrome DevTools — 浏览器自动化'));
|
|
227
353
|
console.log('');
|
|
228
354
|
}
|
|
229
355
|
|
|
@@ -241,7 +367,7 @@ export function getVersion() {
|
|
|
241
367
|
// ── 主命令 ──
|
|
242
368
|
|
|
243
369
|
export async function cmdInit(projectDir, options = {}) {
|
|
244
|
-
const { tool, interactive } = options;
|
|
370
|
+
const { tool, workspace, interactive } = options;
|
|
245
371
|
const version = getVersion();
|
|
246
372
|
|
|
247
373
|
// ── 交互式模式(--interactive 或 -i)──
|
|
@@ -273,11 +399,20 @@ export async function cmdInit(projectDir, options = {}) {
|
|
|
273
399
|
validate: (answer) => answer.length > 0 || '至少选择一个工具',
|
|
274
400
|
});
|
|
275
401
|
|
|
276
|
-
//
|
|
402
|
+
// 工作区模式
|
|
403
|
+
const isWorkspace = await select({
|
|
404
|
+
message: '选择项目模式',
|
|
405
|
+
choices: [
|
|
406
|
+
{ name: '单项目模式', value: 'false' },
|
|
407
|
+
{ name: '多项目工作区', value: 'true' },
|
|
408
|
+
],
|
|
409
|
+
}) === 'true';
|
|
410
|
+
|
|
411
|
+
// 工作区子项目引导
|
|
277
412
|
let subprojects = [];
|
|
278
|
-
{
|
|
413
|
+
if (isWorkspace) {
|
|
279
414
|
console.log('');
|
|
280
|
-
console.log(chalk.yellow('📋 添加子项目'));
|
|
415
|
+
console.log(chalk.yellow('📋 工作区模式 — 添加子项目'));
|
|
281
416
|
console.log(chalk.dim(' 子项目是工作区中的独立项目目录(如 frontend/、backend/)'));
|
|
282
417
|
console.log('');
|
|
283
418
|
|
|
@@ -340,8 +475,8 @@ export async function cmdInit(projectDir, options = {}) {
|
|
|
340
475
|
}
|
|
341
476
|
|
|
342
477
|
console.log('');
|
|
343
|
-
await doInstall(projectDir, selectedTools, subprojects);
|
|
344
|
-
showSummary(version, selectedTools);
|
|
478
|
+
const count = await doInstall(projectDir, selectedTools, isWorkspace, subprojects);
|
|
479
|
+
showSummary(version, selectedTools, isWorkspace, count);
|
|
345
480
|
return;
|
|
346
481
|
}
|
|
347
482
|
|
|
@@ -359,17 +494,23 @@ export async function cmdInit(projectDir, options = {}) {
|
|
|
359
494
|
tools = detectTools(projectDir);
|
|
360
495
|
}
|
|
361
496
|
|
|
362
|
-
await doInstall(projectDir, tools);
|
|
497
|
+
const count = await doInstall(projectDir, tools, !!workspace);
|
|
363
498
|
|
|
364
499
|
console.log('');
|
|
365
500
|
console.log(chalk.green(` ✅ SillySpec v${version} 安装完成!`));
|
|
366
501
|
console.log('');
|
|
502
|
+
console.log(` 📄 ${count} 个命令已就绪`);
|
|
367
503
|
console.log(' 📁 .sillyspec/ — 项目规范目录');
|
|
368
504
|
console.log('');
|
|
369
|
-
console.log('
|
|
370
|
-
console.log(`
|
|
371
|
-
console.log(`
|
|
505
|
+
console.log(' 下一步:');
|
|
506
|
+
console.log(` 全新项目 → ${chalk.bold('/sillyspec:init')}`);
|
|
507
|
+
console.log(` 已有代码 → ${chalk.bold('/sillyspec:scan')}`);
|
|
508
|
+
console.log(` 自由探索 → ${chalk.bold('/sillyspec:explore "你的想法"')}`);
|
|
509
|
+
if (workspace) {
|
|
510
|
+
console.log(` 管理子项目 → ${chalk.bold('/sillyspec:workspace add')}`);
|
|
511
|
+
}
|
|
372
512
|
console.log('');
|
|
373
513
|
console.log(chalk.dim(' 💡 增强能力:sillyspec setup(安装 MCP 工具)'));
|
|
514
|
+
console.log(chalk.dim(' 💡 完整配置:sillyspec init --interactive'));
|
|
374
515
|
console.log('');
|
|
375
516
|
}
|
package/src/setup.js
CHANGED
|
@@ -11,7 +11,15 @@ const __dirname = dirname(__filename);
|
|
|
11
11
|
|
|
12
12
|
// ── Skill 定义 ──
|
|
13
13
|
|
|
14
|
-
const SKILLS = [
|
|
14
|
+
const SKILLS = [
|
|
15
|
+
{
|
|
16
|
+
id: 'playwright-e2e',
|
|
17
|
+
name: 'Playwright E2E 测试参考',
|
|
18
|
+
description: 'E2E 测试编写最佳实践,AI 执行测试任务时自动读取',
|
|
19
|
+
source: join(__dirname, '..', 'templates', 'skills', 'playwright-e2e'),
|
|
20
|
+
target: 'playwright-e2e',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
15
23
|
|
|
16
24
|
// ── MCP 工具定义 ──
|
|
17
25
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
## 交互规范
|
|
2
|
+
**当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。**
|
|
3
|
+
|
|
4
|
+
## 核心约束(必须遵守)
|
|
5
|
+
- ❌ 未经验证就归档(必须先确认验证通过)
|
|
6
|
+
- ❌ 未勾选的 checkbox 未告知用户就归档
|
|
7
|
+
- ❌ 归档后留下活跃变更的残留状态
|
|
8
|
+
- ❌ 覆盖已存在的归档目录
|
|
9
|
+
|
|
10
|
+
## 变更名称
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 流程
|
|
16
|
+
|
|
17
|
+
### 1. 前置检查(门禁)
|
|
18
|
+
|
|
19
|
+
读取 `.sillyspec/changes/<change-name>/` 下所有必要文件,逐项检查:
|
|
20
|
+
|
|
21
|
+
- [ ] **文件完整性:** 检查 `design.md` 是否存在,缺失则警告
|
|
22
|
+
- [ ] **任务完成度:** 读取 `tasks.md`,统计已完成/未完成任务数。**有未完成的 → 用 AskUserQuestion 询问:**
|
|
23
|
+
- ① 继续归档(未完成任务将被标记完成)
|
|
24
|
+
- ② 取消,回去完成任务
|
|
25
|
+
|
|
26
|
+
> 任一门禁不通过且用户选择取消 → 终止流程。
|
|
27
|
+
|
|
28
|
+
### 2. 展示归档清单
|
|
29
|
+
|
|
30
|
+
展示即将归档的内容摘要:
|
|
31
|
+
- 变更目录名
|
|
32
|
+
- 包含的文件列表
|
|
33
|
+
- 任务完成统计(✅ 已完成 / ⬜ 未完成)
|
|
34
|
+
- 一句话总结本次变更
|
|
35
|
+
- quicklog 修改记录(如有 `.sillyspec/changes/<change-name>/quicklog/` 目录)
|
|
36
|
+
|
|
37
|
+
### 3. Spec 沉淀
|
|
38
|
+
|
|
39
|
+
将 `.sillyspec/changes/<change-name>/` 下的设计文档**复制到 `.sillyspec/knowledge/` 主目录**,确保已完成的设计规范可被后续变更参考。如目标已存在同名文件则跳过并提示。
|
|
40
|
+
|
|
41
|
+
### 4. 用户确认
|
|
42
|
+
|
|
43
|
+
用 AskUserQuestion 让用户确认:
|
|
44
|
+
- ① 确认归档
|
|
45
|
+
- ② 取消
|
|
46
|
+
|
|
47
|
+
### 4.5 生成归档摘要
|
|
48
|
+
|
|
49
|
+
在变更目录下自动生成 `SUMMARY.md`:
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
# <变更名> 归档
|
|
53
|
+
|
|
54
|
+
- 创建:YYYY-MM-DD
|
|
55
|
+
- 完成:YYYY-MM-DD
|
|
56
|
+
- 涉及阶段:brainstorm → plan → execute → verify
|
|
57
|
+
|
|
58
|
+
## 关键决策
|
|
59
|
+
- (从 design.md 提取 3-5 条核心决策)
|
|
60
|
+
|
|
61
|
+
## 产出文件
|
|
62
|
+
- design.md — 设计文档
|
|
63
|
+
- tasks.md — 任务清单
|
|
64
|
+
- quicklog/ — 关联 quick 修改(N次)
|
|
65
|
+
- quick1: 描述
|
|
66
|
+
- quick2: 描述
|
|
67
|
+
|
|
68
|
+
## 代码变更统计
|
|
69
|
+
- 新增 X 文件,修改 Y 文件,删除 Z 文件
|
|
70
|
+
- 详见 CHANGELOG.md
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
在变更目录下自动生成 `CHANGELOG.md`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# 收集该变更相关的 git commit(按变更名过滤或按时间范围)
|
|
77
|
+
git log --oneline --no-merges -- .sillyspec/changes/<change-name>/ 2>/dev/null
|
|
78
|
+
# 以及变更目录创建后的所有 commit
|
|
79
|
+
git log --oneline --no-merges --since="<创建时间>" -- "*.ts" "*.js" "*.vue" "*.java" 2>/dev/null
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
写入 CHANGELOG.md,格式:
|
|
83
|
+
```markdown
|
|
84
|
+
# <变更名> 变更日志
|
|
85
|
+
|
|
86
|
+
## brainstorm 阶段
|
|
87
|
+
- (相关 commit)
|
|
88
|
+
|
|
89
|
+
## plan 阶段
|
|
90
|
+
- (相关 commit)
|
|
91
|
+
|
|
92
|
+
## execute 阶段
|
|
93
|
+
- (相关 commit)
|
|
94
|
+
|
|
95
|
+
## quick 修改
|
|
96
|
+
- (相关 commit)
|
|
97
|
+
|
|
98
|
+
## verify 阶段
|
|
99
|
+
- (相关 commit)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 5. 执行归档
|
|
103
|
+
|
|
104
|
+
- 目标路径:`.sillyspec/changes/archive/YYYY-MM-DD-<change-name>/`
|
|
105
|
+
- **检查目标路径是否已存在**,存在则中止并报错,防止覆盖
|
|
106
|
+
- 移动变更目录到归档路径
|
|
107
|
+
|
|
108
|
+
### 6. 归档后更新
|
|
109
|
+
|
|
110
|
+
- **tasks.md:** 确保所有 checkbox 都已勾选 `[x]`
|
|
111
|
+
- **ROADMAP.md**(如存在):标记对应 Phase 已完成
|
|
112
|
+
- **STATE.md:** 清除当前变更信息,历史记录追加归档完成(含精确到秒的时间戳)
|
|
113
|
+
- **Git 暂存:** `git add .sillyspec/`
|
|
114
|
+
|
|
115
|
+
**工作区模式下:** 如果变更属于某个子项目,cd 到子项目目录执行 git add。工作区根目录无 git 则跳过。
|
|
116
|
+
|
|
117
|
+
💡 归档产出已暂存。准备好后用 `/sillyspec:commit` 提交。
|
|
118
|
+
|
|
119
|
+
### 最后说:
|
|
120
|
+
|
|
121
|
+
> ✅ 变更 `<change-name>` 已归档到 `archive/YYYY-MM-DD-<change-name>/`。继续:`/sillyspec:brainstorm "新想法"`
|