speccrew 0.1.0
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/.speccrew/agents/speccrew-feature-designer.md +142 -0
- package/.speccrew/agents/speccrew-product-manager.md +61 -0
- package/.speccrew/agents/speccrew-system-designer.md +200 -0
- package/.speccrew/agents/speccrew-system-developer.md +238 -0
- package/.speccrew/agents/speccrew-task-worker.md +80 -0
- package/.speccrew/agents/speccrew-team-leader.md +92 -0
- package/.speccrew/agents/speccrew-test-manager.md +313 -0
- package/.speccrew/skills/speccrew-create-agents/SKILL.md +98 -0
- package/.speccrew/skills/speccrew-create-agents/templates/agents/designer-agent.md +54 -0
- package/.speccrew/skills/speccrew-create-agents/templates/agents/dev-agent.md +79 -0
- package/.speccrew/skills/speccrew-create-agents/templates/agents/test-agent.md +80 -0
- package/.speccrew/skills/speccrew-dev-backend/SKILL.md +205 -0
- package/.speccrew/skills/speccrew-dev-backend/templates/TASK-RECORD-TEMPLATE.md +118 -0
- package/.speccrew/skills/speccrew-dev-desktop/SKILL.md +258 -0
- package/.speccrew/skills/speccrew-dev-desktop/templates/TASK-RECORD-TEMPLATE.md +161 -0
- package/.speccrew/skills/speccrew-dev-frontend/SKILL.md +202 -0
- package/.speccrew/skills/speccrew-dev-frontend/templates/TASK-RECORD-TEMPLATE.md +115 -0
- package/.speccrew/skills/speccrew-dev-mobile/SKILL.md +200 -0
- package/.speccrew/skills/speccrew-dev-mobile/templates/TASK-RECORD-TEMPLATE.md +125 -0
- package/.speccrew/skills/speccrew-fd-api-contract/SKILL.md +73 -0
- package/.speccrew/skills/speccrew-fd-api-contract/templates/API-CONTRACT-TEMPLATE.md +96 -0
- package/.speccrew/skills/speccrew-fd-feature-design/SKILL.md +395 -0
- package/.speccrew/skills/speccrew-fd-feature-design/templates/FEATURE-SPEC-TEMPLATE.md +387 -0
- package/.speccrew/skills/speccrew-get-timestamp/SKILL.md +80 -0
- package/.speccrew/skills/speccrew-get-timestamp/scripts/get-timestamp.js +35 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/SKILL.md +1116 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE-FASTAPI.md +462 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE-JAVA.md +480 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE-NET.md +464 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE.md +480 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/MODULE-OVERVIEW-TEMPLATE.md +367 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/SKILL.md +667 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/STATUS-FORMATS.md +74 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/batch-orchestrator.js +176 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-next-batch.js +150 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-pending-features.js +106 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/mark-stale.js +249 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/process-batch-results.js +848 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/update-feature-status.js +226 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/SKILL.md +264 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/examples/features.json +34 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/generate-inventory.js +867 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/test-inventory.js +26 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/SKILL.md +165 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/scripts/apply-module-mapping.js +208 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/scripts/extract-module-summary.js +180 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/scripts/reindex-modules.js +358 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/SKILL.md +1055 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-DESKTOP.md +303 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-ELECTRON.md +327 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-MINIAPP.md +292 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-MOBILE.md +281 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI.md +324 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/SKILL.md +270 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/templates/COMPONENT-PATTERN-TEMPLATE.md +33 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/templates/LAYOUT-PATTERN-TEMPLATE.md +33 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/templates/PAGE-TYPE-TEMPLATE.md +33 -0
- package/.speccrew/skills/speccrew-knowledge-graph-query/SKILL.md +229 -0
- package/.speccrew/skills/speccrew-knowledge-graph-query/scripts/graph-query.js +549 -0
- package/.speccrew/skills/speccrew-knowledge-graph-write/SKILL.md +181 -0
- package/.speccrew/skills/speccrew-knowledge-graph-write/scripts/graph-write.js +651 -0
- package/.speccrew/skills/speccrew-knowledge-module-summarize/SKILL.md +305 -0
- package/.speccrew/skills/speccrew-knowledge-module-summarize/templates/MODULE-OVERVIEW-TEMPLATE.md +400 -0
- package/.speccrew/skills/speccrew-knowledge-system-summarize/SKILL.md +351 -0
- package/.speccrew/skills/speccrew-knowledge-system-summarize/templates/SYSTEM-OVERVIEW-TEMPLATE.md +294 -0
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/SKILL.md +683 -0
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/STATUS-FORMATS.md +550 -0
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/templates/techs-manifest-EXAMPLE.json +35 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/SKILL.md +1087 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/ARCHITECTURE-TEMPLATE.md +240 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/COLOR-SYSTEM-TEMPLATE.md +68 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/COMPONENT-LIBRARY-TEMPLATE.md +86 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-BUILD-TEMPLATE.md +466 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-DATA-TEMPLATE.md +432 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-DESIGN-TEMPLATE.md +1209 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-DEV-TEMPLATE.md +1433 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-SYSTEM-TEST-TEMPLATE.md +1052 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-UNIT-TEST-TEMPLATE.md +946 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/INDEX-TEMPLATE.md +29 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/PAGE-LAYOUTS-TEMPLATE.md +69 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/PAGE-TYPE-SUMMARY-TEMPLATE.md +74 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/TECH-STACK-TEMPLATE.md +232 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate-conventions/SKILL.md +628 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate-ui-style/SKILL.md +392 -0
- package/.speccrew/skills/speccrew-knowledge-techs-index/SKILL.md +489 -0
- package/.speccrew/skills/speccrew-knowledge-techs-index/templates/INDEX-TEMPLATE.md +243 -0
- package/.speccrew/skills/speccrew-knowledge-techs-init/SKILL.md +269 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/SKILL.md +562 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/BUSINESS-COMPONENTS-TEMPLATE.md +171 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/COMMON-COMPONENTS-TEMPLATE.md +177 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/COMPONENT-INDIVIDUAL-TEMPLATE.md +80 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/COMPONENT-LIBRARY-TEMPLATE.md +118 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/LAYOUT-INDIVIDUAL-TEMPLATE.md +97 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/LAYOUT-PATTERNS-TEMPLATE.md +208 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/NAVIGATION-PATTERNS-TEMPLATE.md +157 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/PAGE-TYPE-INDIVIDUAL-TEMPLATE.md +123 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/PAGE-TYPE-SUMMARY-TEMPLATE.md +58 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/SPACING-TEMPLATE.md +119 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/STYLE-SYSTEM-TEMPLATE.md +117 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/TYPOGRAPHY-TEMPLATE.md +107 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/UI-STYLE-GUIDE-TEMPLATE.md +171 -0
- package/.speccrew/skills/speccrew-pm-requirement-analysis/SKILL.md +434 -0
- package/.speccrew/skills/speccrew-pm-requirement-analysis/templates/BIZS-MODELING-TEMPLATE.md +332 -0
- package/.speccrew/skills/speccrew-pm-requirement-analysis/templates/PRD-TEMPLATE.md +200 -0
- package/.speccrew/skills/speccrew-pm-requirement-assess/SKILL.md +195 -0
- package/.speccrew/skills/speccrew-project-diagnosis/SKILL.md +208 -0
- package/.speccrew/skills/speccrew-project-diagnosis/templates/DIAGNOSIS-REPORT-TEMPLATE.md +202 -0
- package/.speccrew/skills/speccrew-sd-backend/SKILL.md +188 -0
- package/.speccrew/skills/speccrew-sd-backend/templates/INDEX-TEMPLATE.md +85 -0
- package/.speccrew/skills/speccrew-sd-backend/templates/SD-BACKEND-TEMPLATE.md +269 -0
- package/.speccrew/skills/speccrew-sd-desktop/SKILL.md +192 -0
- package/.speccrew/skills/speccrew-sd-desktop/templates/INDEX-TEMPLATE.md +271 -0
- package/.speccrew/skills/speccrew-sd-desktop/templates/SD-DESKTOP-TEMPLATE.md +673 -0
- package/.speccrew/skills/speccrew-sd-frontend/SKILL.md +176 -0
- package/.speccrew/skills/speccrew-sd-frontend/templates/INDEX-TEMPLATE.md +184 -0
- package/.speccrew/skills/speccrew-sd-frontend/templates/SD-FRONTEND-TEMPLATE.md +382 -0
- package/.speccrew/skills/speccrew-sd-mobile/SKILL.md +189 -0
- package/.speccrew/skills/speccrew-sd-mobile/templates/INDEX-TEMPLATE.md +219 -0
- package/.speccrew/skills/speccrew-sd-mobile/templates/SD-MOBILE-TEMPLATE.md +534 -0
- package/.speccrew/skills/speccrew-test-case-design/SKILL.md +284 -0
- package/.speccrew/skills/speccrew-test-case-design/templates/TEST-CASE-DESIGN-TEMPLATE.md +263 -0
- package/.speccrew/skills/speccrew-test-code-gen/SKILL.md +313 -0
- package/.speccrew/skills/speccrew-test-code-gen/templates/TEST-CODE-PLAN-TEMPLATE.md +180 -0
- package/.speccrew/skills/speccrew-test-execute/SKILL.md +283 -0
- package/.speccrew/skills/speccrew-test-execute/templates/BUG-REPORT-TEMPLATE.md +50 -0
- package/.speccrew/skills/speccrew-test-execute/templates/TEST-REPORT-TEMPLATE.md +57 -0
- package/.speccrew/skills/speccrew-workflow-diagnose/SKILL.md +155 -0
- package/LICENSE +21 -0
- package/README.ar.md +318 -0
- package/README.en.md +318 -0
- package/README.es.md +318 -0
- package/README.md +340 -0
- package/bin/cli.js +62 -0
- package/lib/commands/doctor.js +138 -0
- package/lib/commands/init.js +231 -0
- package/lib/commands/list.js +114 -0
- package/lib/commands/uninstall.js +117 -0
- package/lib/commands/update.js +351 -0
- package/lib/ide-adapters.js +73 -0
- package/lib/utils.js +104 -0
- package/package.json +28 -0
- package/workspace-template/docs/configs/document-templates.json +667 -0
- package/workspace-template/docs/configs/platform-mapping.json +194 -0
- package/workspace-template/docs/configs/tech-stack-mappings.json +313 -0
- package/workspace-template/docs/configs/validation-rules.json +87 -0
- package/workspace-template/docs/rules/mermaid-rule.md +114 -0
- package/workspace-template/docs/solutions/Agent/346/212/200/350/203/275/345/256/232/344/271/211+/351/234/200/346/261/202/346/226/207/346/241/243+UML/344/275/277/347/224/250/346/250/241/346/235/277/357/274/210ISA-95/345/205/255/346/256/265/345/274/217/350/236/215/345/220/210/347/211/210/357/274/211.md +586 -0
- package/workspace-template/docs/solutions/agent-knowledge-map.md +238 -0
- package/workspace-template/docs/solutions/bizs-knowledge-pipeline.md +678 -0
- package/workspace-template/docs/solutions/harness.md +410 -0
- package/workspace-template/docs/solutions/knowledge-incremental-sync-spec.md +943 -0
- package/workspace-template/docs/solutions/techs-knowledge-pipeline.md +803 -0
- package/workspace-template/docs/solutions/workspace-structure.md +318 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const command = process.argv[2];
|
|
5
|
+
const args = process.argv.slice(3);
|
|
6
|
+
const projectRoot = process.cwd();
|
|
7
|
+
|
|
8
|
+
function printUsage() {
|
|
9
|
+
console.log(`
|
|
10
|
+
SpecCrew - Spec-Driven Development toolkit for AI-powered IDEs
|
|
11
|
+
|
|
12
|
+
Usage: speccrew <command> [options]
|
|
13
|
+
|
|
14
|
+
Commands:
|
|
15
|
+
init Initialize SpecCrew in the current project
|
|
16
|
+
update Update SpecCrew agents and skills
|
|
17
|
+
doctor Check environment and installation health
|
|
18
|
+
uninstall Remove SpecCrew from the current project
|
|
19
|
+
list List installed agents and skills
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--help Show help
|
|
23
|
+
--version Show version
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
speccrew init --ide qoder
|
|
27
|
+
speccrew update
|
|
28
|
+
speccrew doctor
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
switch (command) {
|
|
33
|
+
case 'init':
|
|
34
|
+
require('../lib/commands/init').run();
|
|
35
|
+
break;
|
|
36
|
+
case 'update':
|
|
37
|
+
require('../lib/commands/update').run();
|
|
38
|
+
break;
|
|
39
|
+
case 'doctor':
|
|
40
|
+
require('../lib/commands/doctor').run(projectRoot, args);
|
|
41
|
+
break;
|
|
42
|
+
case 'uninstall':
|
|
43
|
+
require('../lib/commands/uninstall').run(projectRoot, args);
|
|
44
|
+
break;
|
|
45
|
+
case 'list':
|
|
46
|
+
require('../lib/commands/list').run(projectRoot, args);
|
|
47
|
+
break;
|
|
48
|
+
case '--version':
|
|
49
|
+
case '-v':
|
|
50
|
+
const pkg = require('../package.json');
|
|
51
|
+
console.log(pkg.version);
|
|
52
|
+
break;
|
|
53
|
+
case '--help':
|
|
54
|
+
case '-h':
|
|
55
|
+
case undefined:
|
|
56
|
+
printUsage();
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
console.error(`Unknown command: ${command}`);
|
|
60
|
+
printUsage();
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { readSpeccrewRC, getPackageVersion, getSourceRoot, isSpeccrewFile } = require('../utils');
|
|
4
|
+
const { detectIDE, IDE_CONFIGS } = require('../ide-adapters');
|
|
5
|
+
|
|
6
|
+
function run(projectRoot, args) {
|
|
7
|
+
console.log('SpecCrew Doctor\n');
|
|
8
|
+
|
|
9
|
+
const results = [];
|
|
10
|
+
|
|
11
|
+
// 1. Node.js 版本检查
|
|
12
|
+
const nodeVersion = process.version;
|
|
13
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
14
|
+
if (majorVersion >= 16) {
|
|
15
|
+
results.push({ status: 'PASS', message: `Node.js ${nodeVersion} (>= 16.0.0)` });
|
|
16
|
+
} else {
|
|
17
|
+
results.push({ status: 'FAIL', message: `Node.js ${nodeVersion} (< 16.0.0)` });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 2. SpecCrew 安装状态
|
|
21
|
+
const rc = readSpeccrewRC(projectRoot);
|
|
22
|
+
const version = getPackageVersion();
|
|
23
|
+
if (rc) {
|
|
24
|
+
results.push({ status: 'PASS', message: `SpecCrew v${version} installed` });
|
|
25
|
+
} else {
|
|
26
|
+
results.push({ status: 'WARN', message: 'Not initialized, run speccrew init' });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 3. IDE 目录检查
|
|
30
|
+
const detectedIDEs = detectIDE(projectRoot);
|
|
31
|
+
if (detectedIDEs.length > 0) {
|
|
32
|
+
const ideNames = detectedIDEs.map(ide => `${ide.name} (${ide.baseDir}/)`).join(', ');
|
|
33
|
+
results.push({ status: 'PASS', message: `IDE: ${ideNames}` });
|
|
34
|
+
} else {
|
|
35
|
+
results.push({ status: 'WARN', message: 'No supported IDE detected' });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 4. Agents 完整性检查
|
|
39
|
+
const sourceRoot = getSourceRoot();
|
|
40
|
+
const sourceAgentsDir = path.join(sourceRoot, 'agents');
|
|
41
|
+
let sourceAgentCount = 0;
|
|
42
|
+
let installedAgentCount = 0;
|
|
43
|
+
let missingAgents = [];
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(sourceAgentsDir)) {
|
|
46
|
+
const sourceAgents = fs.readdirSync(sourceAgentsDir).filter(isSpeccrewFile);
|
|
47
|
+
sourceAgentCount = sourceAgents.length;
|
|
48
|
+
|
|
49
|
+
for (const ide of detectedIDEs) {
|
|
50
|
+
const agentsDir = path.join(projectRoot, ide.agentsDir);
|
|
51
|
+
if (fs.existsSync(agentsDir)) {
|
|
52
|
+
const installed = fs.readdirSync(agentsDir).filter(isSpeccrewFile);
|
|
53
|
+
installedAgentCount = installed.length;
|
|
54
|
+
|
|
55
|
+
for (const agent of sourceAgents) {
|
|
56
|
+
const agentName = agent.replace(/\.md$/, '');
|
|
57
|
+
if (!installed.includes(agent)) {
|
|
58
|
+
missingAgents.push(agentName);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (missingAgents.length === 0 && sourceAgentCount > 0) {
|
|
66
|
+
results.push({ status: 'PASS', message: `Agents: ${installedAgentCount}/${sourceAgentCount} installed` });
|
|
67
|
+
} else if (sourceAgentCount > 0) {
|
|
68
|
+
results.push({ status: 'WARN', message: `Agents: ${installedAgentCount}/${sourceAgentCount} installed (missing: ${missingAgents.join(', ')})` });
|
|
69
|
+
} else {
|
|
70
|
+
results.push({ status: 'WARN', message: 'Agents: source not found' });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 5. Skills 完整性检查
|
|
74
|
+
const sourceSkillsDir = path.join(sourceRoot, 'skills');
|
|
75
|
+
let sourceSkillCount = 0;
|
|
76
|
+
let installedSkillCount = 0;
|
|
77
|
+
let missingSkills = [];
|
|
78
|
+
|
|
79
|
+
if (fs.existsSync(sourceSkillsDir)) {
|
|
80
|
+
const sourceSkills = fs.readdirSync(sourceSkillsDir).filter(isSpeccrewFile);
|
|
81
|
+
sourceSkillCount = sourceSkills.length;
|
|
82
|
+
|
|
83
|
+
for (const ide of detectedIDEs) {
|
|
84
|
+
const skillsDir = path.join(projectRoot, ide.skillsDir);
|
|
85
|
+
if (fs.existsSync(skillsDir)) {
|
|
86
|
+
const installed = fs.readdirSync(skillsDir).filter(isSpeccrewFile);
|
|
87
|
+
installedSkillCount = Math.max(installedSkillCount, installed.length);
|
|
88
|
+
|
|
89
|
+
for (const skill of sourceSkills) {
|
|
90
|
+
if (!installed.includes(skill)) {
|
|
91
|
+
missingSkills.push(skill);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (missingSkills.length === 0 && sourceSkillCount > 0) {
|
|
99
|
+
results.push({ status: 'PASS', message: `Skills: ${installedSkillCount}/${sourceSkillCount} installed` });
|
|
100
|
+
} else if (sourceSkillCount > 0) {
|
|
101
|
+
results.push({ status: 'WARN', message: `Skills: ${installedSkillCount}/${sourceSkillCount} installed (missing: ${missingSkills.join(', ')})` });
|
|
102
|
+
} else {
|
|
103
|
+
results.push({ status: 'WARN', message: 'Skills: source not found' });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 6. Workspace 目录检查
|
|
107
|
+
const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
|
|
108
|
+
const docsDir = path.join(workspaceDir, 'docs');
|
|
109
|
+
if (fs.existsSync(workspaceDir) && fs.existsSync(docsDir)) {
|
|
110
|
+
results.push({ status: 'PASS', message: 'Workspace: speccrew-workspace/ OK' });
|
|
111
|
+
} else if (fs.existsSync(workspaceDir)) {
|
|
112
|
+
results.push({ status: 'WARN', message: 'Workspace: speccrew-workspace/ exists but docs/ missing' });
|
|
113
|
+
} else {
|
|
114
|
+
results.push({ status: 'WARN', message: 'Workspace: speccrew-workspace/ not found' });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 输出结果
|
|
118
|
+
let passCount = 0;
|
|
119
|
+
let warnCount = 0;
|
|
120
|
+
let failCount = 0;
|
|
121
|
+
|
|
122
|
+
for (const result of results) {
|
|
123
|
+
const icon = result.status === 'PASS' ? 'PASS' : result.status === 'WARN' ? 'WARN' : 'FAIL';
|
|
124
|
+
const padding = ' ';
|
|
125
|
+
console.log(`${padding}${icon} ${result.message}`);
|
|
126
|
+
|
|
127
|
+
if (result.status === 'PASS') passCount++;
|
|
128
|
+
else if (result.status === 'WARN') warnCount++;
|
|
129
|
+
else failCount++;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log(`${passCount} passed, ${warnCount} warning${warnCount !== 1 ? 's' : ''}, ${failCount} error${failCount !== 1 ? 's' : ''}`);
|
|
134
|
+
|
|
135
|
+
return failCount === 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = { run };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const {
|
|
4
|
+
copyDirRecursive,
|
|
5
|
+
isSpeccrewFile,
|
|
6
|
+
writeSpeccrewRC,
|
|
7
|
+
getPackageVersion,
|
|
8
|
+
getSourceRoot,
|
|
9
|
+
getWorkspaceTemplatePath,
|
|
10
|
+
ensureDirectories,
|
|
11
|
+
} = require('../utils');
|
|
12
|
+
const { resolveIDE } = require('../ide-adapters');
|
|
13
|
+
|
|
14
|
+
// 解析命令行参数
|
|
15
|
+
function parseArgs() {
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
let ide = null;
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < args.length; i++) {
|
|
20
|
+
if (args[i] === '--ide' && i + 1 < args.length) {
|
|
21
|
+
ide = args[i + 1];
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { ide };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 检查 Node.js 版本 >= 16
|
|
30
|
+
function checkNodeVersion() {
|
|
31
|
+
const version = process.version;
|
|
32
|
+
const major = parseInt(version.slice(1).split('.')[0], 10);
|
|
33
|
+
if (major < 16) {
|
|
34
|
+
throw new Error(`Node.js version ${version} is not supported. Please upgrade to Node.js 16 or higher.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 复制 agents(speccrew-* 前缀文件,总是覆盖)
|
|
39
|
+
function copyAgents(sourceDir, destDir) {
|
|
40
|
+
if (!fs.existsSync(sourceDir)) return { copied: 0, skipped: 0 };
|
|
41
|
+
|
|
42
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
43
|
+
let copied = 0, skipped = 0;
|
|
44
|
+
|
|
45
|
+
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
46
|
+
for (const entry of entries) {
|
|
47
|
+
if (!isSpeccrewFile(entry.name)) {
|
|
48
|
+
skipped++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const srcPath = path.join(sourceDir, entry.name);
|
|
53
|
+
const destPath = path.join(destDir, entry.name);
|
|
54
|
+
|
|
55
|
+
fs.copyFileSync(srcPath, destPath);
|
|
56
|
+
copied++;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { copied, skipped };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 复制 skills(speccrew-* 前缀目录,递归复制,总是覆盖)
|
|
63
|
+
function copySkills(sourceDir, destDir) {
|
|
64
|
+
if (!fs.existsSync(sourceDir)) return { copied: 0, skipped: 0 };
|
|
65
|
+
|
|
66
|
+
let copied = 0, skipped = 0;
|
|
67
|
+
|
|
68
|
+
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
if (!isSpeccrewFile(entry.name)) {
|
|
71
|
+
skipped++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const srcPath = path.join(sourceDir, entry.name);
|
|
76
|
+
const destPath = path.join(destDir, entry.name);
|
|
77
|
+
|
|
78
|
+
if (entry.isDirectory()) {
|
|
79
|
+
const result = copyDirRecursive(srcPath, destPath);
|
|
80
|
+
copied += result.copied;
|
|
81
|
+
skipped += result.skipped;
|
|
82
|
+
} else {
|
|
83
|
+
fs.copyFileSync(srcPath, destPath);
|
|
84
|
+
copied++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { copied, skipped };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 创建 workspace 目录结构
|
|
92
|
+
function createWorkspaceStructure(workspaceDir) {
|
|
93
|
+
const dirs = [
|
|
94
|
+
'iterations',
|
|
95
|
+
'iteration-archives',
|
|
96
|
+
'knowledges/base/diagnosis-reports',
|
|
97
|
+
'knowledges/base/sync-state',
|
|
98
|
+
'knowledges/base/tech-debts',
|
|
99
|
+
'knowledges/bizs',
|
|
100
|
+
'knowledges/techs',
|
|
101
|
+
'docs/configs',
|
|
102
|
+
'docs/rules',
|
|
103
|
+
'docs/solutions',
|
|
104
|
+
'docs/templates',
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
ensureDirectories(workspaceDir, dirs);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 复制 workspace 模板(仅复制不存在的文件)
|
|
111
|
+
function copyWorkspaceTemplate(templateDir, workspaceDir) {
|
|
112
|
+
if (!fs.existsSync(templateDir)) return { copied: 0, skipped: 0 };
|
|
113
|
+
|
|
114
|
+
const docsSourceDir = path.join(templateDir, 'docs');
|
|
115
|
+
const docsDestDir = path.join(workspaceDir, 'docs');
|
|
116
|
+
|
|
117
|
+
if (!fs.existsSync(docsSourceDir)) return { copied: 0, skipped: 0 };
|
|
118
|
+
|
|
119
|
+
fs.mkdirSync(docsDestDir, { recursive: true });
|
|
120
|
+
let copied = 0, skipped = 0;
|
|
121
|
+
|
|
122
|
+
function copyIfNotExists(src, dest) {
|
|
123
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const srcPath = path.join(src, entry.name);
|
|
126
|
+
const destPath = path.join(dest, entry.name);
|
|
127
|
+
|
|
128
|
+
if (entry.isDirectory()) {
|
|
129
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
130
|
+
copyIfNotExists(srcPath, destPath);
|
|
131
|
+
} else {
|
|
132
|
+
if (!fs.existsSync(destPath)) {
|
|
133
|
+
fs.copyFileSync(srcPath, destPath);
|
|
134
|
+
copied++;
|
|
135
|
+
} else {
|
|
136
|
+
skipped++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
copyIfNotExists(docsSourceDir, docsDestDir);
|
|
143
|
+
return { copied, skipped };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 主函数
|
|
147
|
+
function run() {
|
|
148
|
+
try {
|
|
149
|
+
// 1. 解析参数
|
|
150
|
+
const { ide: cliIdeArg } = parseArgs();
|
|
151
|
+
|
|
152
|
+
// 2. 确定项目根目录
|
|
153
|
+
const projectRoot = process.cwd();
|
|
154
|
+
|
|
155
|
+
// 3. 检查 Node.js 版本
|
|
156
|
+
checkNodeVersion();
|
|
157
|
+
|
|
158
|
+
// 4. 解析 IDE
|
|
159
|
+
const ideConfigs = resolveIDE(projectRoot, cliIdeArg);
|
|
160
|
+
|
|
161
|
+
// 5. 确定源文件路径
|
|
162
|
+
const sourceRoot = getSourceRoot();
|
|
163
|
+
|
|
164
|
+
// 统计信息
|
|
165
|
+
const stats = {
|
|
166
|
+
ides: [],
|
|
167
|
+
totalAgents: 0,
|
|
168
|
+
totalSkills: 0,
|
|
169
|
+
workspaceCreated: false,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// 6. 对每个检测到的 IDE 复制 agents 和 skills
|
|
173
|
+
for (const ideConfig of ideConfigs) {
|
|
174
|
+
const agentsSourceDir = path.join(sourceRoot, 'agents');
|
|
175
|
+
const skillsSourceDir = path.join(sourceRoot, 'skills');
|
|
176
|
+
const agentsDestDir = path.join(projectRoot, ideConfig.agentsDir);
|
|
177
|
+
const skillsDestDir = path.join(projectRoot, ideConfig.skillsDir);
|
|
178
|
+
|
|
179
|
+
const agentsResult = copyAgents(agentsSourceDir, agentsDestDir);
|
|
180
|
+
const skillsResult = copySkills(skillsSourceDir, skillsDestDir);
|
|
181
|
+
|
|
182
|
+
stats.ides.push({
|
|
183
|
+
name: ideConfig.name,
|
|
184
|
+
baseDir: ideConfig.baseDir,
|
|
185
|
+
agents: agentsResult.copied,
|
|
186
|
+
skills: skillsResult.copied,
|
|
187
|
+
});
|
|
188
|
+
stats.totalAgents += agentsResult.copied;
|
|
189
|
+
stats.totalSkills += skillsResult.copied;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 7. 创建 speccrew-workspace 目录结构
|
|
193
|
+
const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
|
|
194
|
+
createWorkspaceStructure(workspaceDir);
|
|
195
|
+
stats.workspaceCreated = true;
|
|
196
|
+
|
|
197
|
+
// 8. 复制 workspace 模板
|
|
198
|
+
const templateDir = getWorkspaceTemplatePath();
|
|
199
|
+
copyWorkspaceTemplate(templateDir, workspaceDir);
|
|
200
|
+
|
|
201
|
+
// 9. 写入 .speccrewrc
|
|
202
|
+
const version = getPackageVersion();
|
|
203
|
+
const rcConfig = {
|
|
204
|
+
ide: ideConfigs.length === 1 ? ideConfigs[0].id : ideConfigs.map(c => c.id),
|
|
205
|
+
version: version,
|
|
206
|
+
installedAt: new Date().toISOString(),
|
|
207
|
+
};
|
|
208
|
+
writeSpeccrewRC(projectRoot, rcConfig);
|
|
209
|
+
|
|
210
|
+
// 10. 输出安装摘要
|
|
211
|
+
console.log(`SpecCrew v${version} installed successfully!\n`);
|
|
212
|
+
|
|
213
|
+
for (const ide of stats.ides) {
|
|
214
|
+
console.log(`IDE: ${ide.name} (${ide.baseDir}/)`);
|
|
215
|
+
console.log(`Agents: ${ide.agents} installed`);
|
|
216
|
+
console.log(`Skills: ${ide.skills} installed`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (stats.workspaceCreated) {
|
|
220
|
+
console.log('Workspace: speccrew-workspace/ created');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('\nGet started: Ask your AI agent to help with your project!');
|
|
224
|
+
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error(`Error: ${error.message}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = { run };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { readSpeccrewRC, getPackageVersion, isSpeccrewFile } = require('../utils');
|
|
4
|
+
|
|
5
|
+
function run(projectRoot, args) {
|
|
6
|
+
// 检查是否已初始化
|
|
7
|
+
const rc = readSpeccrewRC(projectRoot);
|
|
8
|
+
if (!rc) {
|
|
9
|
+
console.log('SpecCrew is not initialized in this project.');
|
|
10
|
+
console.log('Run "speccrew init" to initialize.');
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const version = getPackageVersion();
|
|
15
|
+
const ides = rc.ide ? (Array.isArray(rc.ide) ? rc.ide : [rc.ide]) : [];
|
|
16
|
+
const ideNames = ides.map(id => {
|
|
17
|
+
const names = { qoder: 'Qoder', cursor: 'Cursor' };
|
|
18
|
+
return names[id] || id;
|
|
19
|
+
});
|
|
20
|
+
const ideDisplay = ideNames.length > 0 ? ideNames.join(', ') : 'Unknown';
|
|
21
|
+
const ideDirs = ides.map(id => {
|
|
22
|
+
const dirs = { qoder: '.qoder/', cursor: '.cursor/' };
|
|
23
|
+
return dirs[id] || `.${id}/`;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
console.log(`SpecCrew v${version} | IDE: ${ideDisplay} (${ideDirs.join(', ')})\n`);
|
|
27
|
+
|
|
28
|
+
// 收集所有 agents 和 skills
|
|
29
|
+
const speccrewAgents = new Set();
|
|
30
|
+
const speccrewSkills = new Set();
|
|
31
|
+
const userAgents = new Set();
|
|
32
|
+
const userSkills = new Set();
|
|
33
|
+
|
|
34
|
+
for (const ideId of ides) {
|
|
35
|
+
const ideConfig = getIDEConfig(ideId);
|
|
36
|
+
if (!ideConfig) continue;
|
|
37
|
+
|
|
38
|
+
// 扫描 agents
|
|
39
|
+
const agentsDir = path.join(projectRoot, ideConfig.agentsDir);
|
|
40
|
+
if (fs.existsSync(agentsDir)) {
|
|
41
|
+
const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const name = entry.name;
|
|
44
|
+
if (isSpeccrewFile(name)) {
|
|
45
|
+
speccrewAgents.add(name.replace(/\.md$/, ''));
|
|
46
|
+
} else if (!name.startsWith('.') && entry.isDirectory()) {
|
|
47
|
+
userAgents.add(`${ideConfig.agentsDir}/${name}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 扫描 skills
|
|
53
|
+
const skillsDir = path.join(projectRoot, ideConfig.skillsDir);
|
|
54
|
+
if (fs.existsSync(skillsDir)) {
|
|
55
|
+
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const name = entry.name;
|
|
58
|
+
if (isSpeccrewFile(name)) {
|
|
59
|
+
speccrewSkills.add(name);
|
|
60
|
+
} else if (!name.startsWith('.') && entry.isDirectory()) {
|
|
61
|
+
userSkills.add(`${ideConfig.skillsDir}/${name}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 输出 Agents
|
|
68
|
+
const sortedAgents = Array.from(speccrewAgents).sort();
|
|
69
|
+
console.log(`Agents (${sortedAgents.length}):`);
|
|
70
|
+
for (const agent of sortedAgents) {
|
|
71
|
+
console.log(` ${agent}`);
|
|
72
|
+
}
|
|
73
|
+
console.log('');
|
|
74
|
+
|
|
75
|
+
// 输出 Skills
|
|
76
|
+
const sortedSkills = Array.from(speccrewSkills).sort();
|
|
77
|
+
console.log(`Skills (${sortedSkills.length}):`);
|
|
78
|
+
for (const skill of sortedSkills) {
|
|
79
|
+
console.log(` ${skill}`);
|
|
80
|
+
}
|
|
81
|
+
console.log('');
|
|
82
|
+
|
|
83
|
+
// 输出用户自定义内容
|
|
84
|
+
const allUserDefined = [...Array.from(userAgents), ...Array.from(userSkills)].sort();
|
|
85
|
+
if (allUserDefined.length > 0) {
|
|
86
|
+
console.log('User-defined:');
|
|
87
|
+
for (const item of allUserDefined) {
|
|
88
|
+
console.log(` ${item}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 获取 IDE 配置(简化版,避免循环依赖)
|
|
96
|
+
function getIDEConfig(ideId) {
|
|
97
|
+
const configs = {
|
|
98
|
+
qoder: {
|
|
99
|
+
name: 'Qoder',
|
|
100
|
+
baseDir: '.qoder',
|
|
101
|
+
skillsDir: '.qoder/skills',
|
|
102
|
+
agentsDir: '.qoder/agents',
|
|
103
|
+
},
|
|
104
|
+
cursor: {
|
|
105
|
+
name: 'Cursor',
|
|
106
|
+
baseDir: '.cursor',
|
|
107
|
+
skillsDir: '.cursor/skills',
|
|
108
|
+
agentsDir: '.cursor/agents',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
return configs[ideId] || null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = { run };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { readSpeccrewRC, isSpeccrewFile, removeDirRecursive } = require('../utils');
|
|
4
|
+
|
|
5
|
+
function run(projectRoot, args) {
|
|
6
|
+
// 检查是否已初始化
|
|
7
|
+
const rc = readSpeccrewRC(projectRoot);
|
|
8
|
+
if (!rc) {
|
|
9
|
+
console.log('SpecCrew is not initialized in this project.');
|
|
10
|
+
console.log('Run "speccrew init" to initialize.');
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
console.log('SpecCrew Uninstall\n');
|
|
15
|
+
|
|
16
|
+
const isAll = args.includes('--all');
|
|
17
|
+
let removedAgents = 0;
|
|
18
|
+
let removedSkills = 0;
|
|
19
|
+
const removedItems = [];
|
|
20
|
+
|
|
21
|
+
// 获取 IDE 配置
|
|
22
|
+
const ides = rc.ide ? (Array.isArray(rc.ide) ? rc.ide : [rc.ide]) : [];
|
|
23
|
+
|
|
24
|
+
// 删除每个 IDE 目录下的 speccrew-* agents 和 skills
|
|
25
|
+
for (const ideId of ides) {
|
|
26
|
+
const ideConfig = getIDEConfig(ideId);
|
|
27
|
+
if (!ideConfig) continue;
|
|
28
|
+
|
|
29
|
+
// 删除 agents
|
|
30
|
+
const agentsDir = path.join(projectRoot, ideConfig.agentsDir);
|
|
31
|
+
if (fs.existsSync(agentsDir)) {
|
|
32
|
+
const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (isSpeccrewFile(entry.name)) {
|
|
35
|
+
const fullPath = path.join(agentsDir, entry.name);
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
removeDirRecursive(fullPath);
|
|
38
|
+
} else {
|
|
39
|
+
fs.unlinkSync(fullPath);
|
|
40
|
+
}
|
|
41
|
+
removedAgents++;
|
|
42
|
+
removedItems.push(`${ideConfig.baseDir}/agents/${entry.name}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 删除 skills
|
|
48
|
+
const skillsDir = path.join(projectRoot, ideConfig.skillsDir);
|
|
49
|
+
if (fs.existsSync(skillsDir)) {
|
|
50
|
+
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (isSpeccrewFile(entry.name)) {
|
|
53
|
+
const fullPath = path.join(skillsDir, entry.name);
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
removeDirRecursive(fullPath);
|
|
56
|
+
} else {
|
|
57
|
+
fs.unlinkSync(fullPath);
|
|
58
|
+
}
|
|
59
|
+
removedSkills++;
|
|
60
|
+
removedItems.push(`${ideConfig.baseDir}/skills/${entry.name}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 如果 --all,删除 workspace
|
|
67
|
+
let workspaceRemoved = false;
|
|
68
|
+
if (isAll) {
|
|
69
|
+
const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
|
|
70
|
+
if (fs.existsSync(workspaceDir)) {
|
|
71
|
+
removeDirRecursive(workspaceDir);
|
|
72
|
+
workspaceRemoved = true;
|
|
73
|
+
removedItems.push('speccrew-workspace/');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 删除 .speccrewrc
|
|
78
|
+
const rcPath = path.join(projectRoot, '.speccrewrc');
|
|
79
|
+
if (fs.existsSync(rcPath)) {
|
|
80
|
+
fs.unlinkSync(rcPath);
|
|
81
|
+
removedItems.push('.speccrewrc');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 输出摘要
|
|
85
|
+
console.log(`Removed ${removedAgents} agent(s) and ${removedSkills} skill(s)`);
|
|
86
|
+
if (workspaceRemoved) {
|
|
87
|
+
console.log('Removed speccrew-workspace/');
|
|
88
|
+
}
|
|
89
|
+
console.log('Removed .speccrewrc');
|
|
90
|
+
console.log('\nSpecCrew has been uninstalled.');
|
|
91
|
+
if (!isAll) {
|
|
92
|
+
console.log('Workspace data preserved. Use --all to remove everything.');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 获取 IDE 配置(简化版,避免循环依赖)
|
|
99
|
+
function getIDEConfig(ideId) {
|
|
100
|
+
const configs = {
|
|
101
|
+
qoder: {
|
|
102
|
+
name: 'Qoder',
|
|
103
|
+
baseDir: '.qoder',
|
|
104
|
+
skillsDir: '.qoder/skills',
|
|
105
|
+
agentsDir: '.qoder/agents',
|
|
106
|
+
},
|
|
107
|
+
cursor: {
|
|
108
|
+
name: 'Cursor',
|
|
109
|
+
baseDir: '.cursor',
|
|
110
|
+
skillsDir: '.cursor/skills',
|
|
111
|
+
agentsDir: '.cursor/agents',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
return configs[ideId] || null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = { run };
|