@wandoupeas/coding-forge 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/LICENSE +21 -0
- package/README.md +347 -0
- package/dist/agent/adapters/base.d.ts +9 -0
- package/dist/agent/adapters/base.d.ts.map +1 -0
- package/dist/agent/adapters/base.js +2 -0
- package/dist/agent/adapters/base.js.map +1 -0
- package/dist/agent/adapters/claude-code.d.ts +4 -0
- package/dist/agent/adapters/claude-code.d.ts.map +1 -0
- package/dist/agent/adapters/claude-code.js +25 -0
- package/dist/agent/adapters/claude-code.js.map +1 -0
- package/dist/agent/adapters/codex.d.ts +4 -0
- package/dist/agent/adapters/codex.d.ts.map +1 -0
- package/dist/agent/adapters/codex.js +27 -0
- package/dist/agent/adapters/codex.js.map +1 -0
- package/dist/agent/adapters/command-bridge.d.ts +67 -0
- package/dist/agent/adapters/command-bridge.d.ts.map +1 -0
- package/dist/agent/adapters/command-bridge.js +266 -0
- package/dist/agent/adapters/command-bridge.js.map +1 -0
- package/dist/agent/adapters/index.d.ts +7 -0
- package/dist/agent/adapters/index.d.ts.map +1 -0
- package/dist/agent/adapters/index.js +25 -0
- package/dist/agent/adapters/index.js.map +1 -0
- package/dist/agent/adapters/stub.d.ts +3 -0
- package/dist/agent/adapters/stub.d.ts.map +1 -0
- package/dist/agent/adapters/stub.js +66 -0
- package/dist/agent/adapters/stub.js.map +1 -0
- package/dist/agent/context.d.ts +36 -0
- package/dist/agent/context.d.ts.map +1 -0
- package/dist/agent/context.js +16 -0
- package/dist/agent/context.js.map +1 -0
- package/dist/agent/handlers/index.d.ts +6 -0
- package/dist/agent/handlers/index.d.ts.map +1 -0
- package/dist/agent/handlers/index.js +61 -0
- package/dist/agent/handlers/index.js.map +1 -0
- package/dist/agent/index.d.ts +18 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +37 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/types.d.ts +21 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +5 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/agent-briefing.d.ts +24 -0
- package/dist/cli/agent-briefing.d.ts.map +1 -0
- package/dist/cli/agent-briefing.js +133 -0
- package/dist/cli/agent-briefing.js.map +1 -0
- package/dist/cli/commands/checkpoint.d.ts +12 -0
- package/dist/cli/commands/checkpoint.d.ts.map +1 -0
- package/dist/cli/commands/checkpoint.js +86 -0
- package/dist/cli/commands/checkpoint.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +14 -0
- package/dist/cli/commands/dashboard.d.ts.map +1 -0
- package/dist/cli/commands/dashboard.js +192 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +27 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +425 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +303 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/knowledge.d.ts +6 -0
- package/dist/cli/commands/knowledge.d.ts.map +1 -0
- package/dist/cli/commands/knowledge.js +408 -0
- package/dist/cli/commands/knowledge.js.map +1 -0
- package/dist/cli/commands/logs.d.ts +50 -0
- package/dist/cli/commands/logs.d.ts.map +1 -0
- package/dist/cli/commands/logs.js +248 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/mailbox.d.ts +12 -0
- package/dist/cli/commands/mailbox.d.ts.map +1 -0
- package/dist/cli/commands/mailbox.js +131 -0
- package/dist/cli/commands/mailbox.js.map +1 -0
- package/dist/cli/commands/onboard.d.ts +43 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -0
- package/dist/cli/commands/onboard.js +313 -0
- package/dist/cli/commands/onboard.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +16 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +211 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +102 -0
- package/dist/cli/commands/resume.d.ts.map +1 -0
- package/dist/cli/commands/resume.js +256 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/review.d.ts +13 -0
- package/dist/cli/commands/review.d.ts.map +1 -0
- package/dist/cli/commands/review.js +91 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/run.d.ts +19 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +161 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/superpowers.d.ts +21 -0
- package/dist/cli/commands/superpowers.d.ts.map +1 -0
- package/dist/cli/commands/superpowers.js +135 -0
- package/dist/cli/commands/superpowers.js.map +1 -0
- package/dist/cli/commands/ui.d.ts +14 -0
- package/dist/cli/commands/ui.d.ts.map +1 -0
- package/dist/cli/commands/ui.js +41 -0
- package/dist/cli/commands/ui.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +11 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +46 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +69 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/logger.d.ts +18 -0
- package/dist/cli/utils/logger.d.ts.map +1 -0
- package/dist/cli/utils/logger.js +57 -0
- package/dist/cli/utils/logger.js.map +1 -0
- package/dist/core/checkpoint.d.ts +54 -0
- package/dist/core/checkpoint.d.ts.map +1 -0
- package/dist/core/checkpoint.js +227 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/context.d.ts +36 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +71 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/deliverable.d.ts +34 -0
- package/dist/core/deliverable.d.ts.map +1 -0
- package/dist/core/deliverable.js +125 -0
- package/dist/core/deliverable.js.map +1 -0
- package/dist/core/harness-hints.d.ts +43 -0
- package/dist/core/harness-hints.d.ts.map +1 -0
- package/dist/core/harness-hints.js +162 -0
- package/dist/core/harness-hints.js.map +1 -0
- package/dist/core/logger.d.ts +104 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +199 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mailbox.d.ts +69 -0
- package/dist/core/mailbox.d.ts.map +1 -0
- package/dist/core/mailbox.js +225 -0
- package/dist/core/mailbox.js.map +1 -0
- package/dist/core/planning.d.ts +110 -0
- package/dist/core/planning.d.ts.map +1 -0
- package/dist/core/planning.js +832 -0
- package/dist/core/planning.js.map +1 -0
- package/dist/core/runtime.d.ts +22 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +364 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/session.d.ts +62 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +266 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/superpowers-registry.d.ts +20 -0
- package/dist/core/superpowers-registry.d.ts.map +1 -0
- package/dist/core/superpowers-registry.js +180 -0
- package/dist/core/superpowers-registry.js.map +1 -0
- package/dist/core/superpowers-runs.d.ts +19 -0
- package/dist/core/superpowers-runs.d.ts.map +1 -0
- package/dist/core/superpowers-runs.js +231 -0
- package/dist/core/superpowers-runs.js.map +1 -0
- package/dist/core/task.d.ts +43 -0
- package/dist/core/task.d.ts.map +1 -0
- package/dist/core/task.js +232 -0
- package/dist/core/task.js.map +1 -0
- package/dist/core/threads.d.ts +23 -0
- package/dist/core/threads.d.ts.map +1 -0
- package/dist/core/threads.js +92 -0
- package/dist/core/threads.js.map +1 -0
- package/dist/core/worker.d.ts +48 -0
- package/dist/core/worker.d.ts.map +1 -0
- package/dist/core/worker.js +181 -0
- package/dist/core/worker.js.map +1 -0
- package/dist/core/workflow-context.d.ts +16 -0
- package/dist/core/workflow-context.d.ts.map +1 -0
- package/dist/core/workflow-context.js +102 -0
- package/dist/core/workflow-context.js.map +1 -0
- package/dist/core/workspace.d.ts +13 -0
- package/dist/core/workspace.d.ts.map +1 -0
- package/dist/core/workspace.js +208 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/testing/index.d.ts +21 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +55 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types/index.d.ts +298 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/assets/index-DLwnhrrc.css +1 -0
- package/dist/ui/assets/index-cDUGEuwi.js +234 -0
- package/dist/ui/http/handlers/project-artifacts.d.ts +3 -0
- package/dist/ui/http/handlers/project-artifacts.d.ts.map +1 -0
- package/dist/ui/http/handlers/project-artifacts.js +20 -0
- package/dist/ui/http/handlers/project-artifacts.js.map +1 -0
- package/dist/ui/http/handlers/project-overview.d.ts +3 -0
- package/dist/ui/http/handlers/project-overview.d.ts.map +1 -0
- package/dist/ui/http/handlers/project-overview.js +20 -0
- package/dist/ui/http/handlers/project-overview.js.map +1 -0
- package/dist/ui/http/handlers/project-recovery.d.ts +3 -0
- package/dist/ui/http/handlers/project-recovery.d.ts.map +1 -0
- package/dist/ui/http/handlers/project-recovery.js +20 -0
- package/dist/ui/http/handlers/project-recovery.js.map +1 -0
- package/dist/ui/http/handlers/project-runtime.d.ts +3 -0
- package/dist/ui/http/handlers/project-runtime.d.ts.map +1 -0
- package/dist/ui/http/handlers/project-runtime.js +20 -0
- package/dist/ui/http/handlers/project-runtime.js.map +1 -0
- package/dist/ui/http/handlers/project-tasks.d.ts +3 -0
- package/dist/ui/http/handlers/project-tasks.d.ts.map +1 -0
- package/dist/ui/http/handlers/project-tasks.js +20 -0
- package/dist/ui/http/handlers/project-tasks.js.map +1 -0
- package/dist/ui/http/handlers/projects.d.ts +23 -0
- package/dist/ui/http/handlers/projects.d.ts.map +1 -0
- package/dist/ui/http/handlers/projects.js +29 -0
- package/dist/ui/http/handlers/projects.js.map +1 -0
- package/dist/ui/http/router.d.ts +5 -0
- package/dist/ui/http/router.d.ts.map +1 -0
- package/dist/ui/http/router.js +81 -0
- package/dist/ui/http/router.js.map +1 -0
- package/dist/ui/http/server.d.ts +29 -0
- package/dist/ui/http/server.d.ts.map +1 -0
- package/dist/ui/http/server.js +143 -0
- package/dist/ui/http/server.js.map +1 -0
- package/dist/ui/index.html +13 -0
- package/dist/ui/project-registry.d.ts +9 -0
- package/dist/ui/project-registry.d.ts.map +1 -0
- package/dist/ui/project-registry.js +19 -0
- package/dist/ui/project-registry.js.map +1 -0
- package/dist/ui/project-scanner.d.ts +10 -0
- package/dist/ui/project-scanner.d.ts.map +1 -0
- package/dist/ui/project-scanner.js +135 -0
- package/dist/ui/project-scanner.js.map +1 -0
- package/dist/ui/read-models/artifacts.d.ts +58 -0
- package/dist/ui/read-models/artifacts.d.ts.map +1 -0
- package/dist/ui/read-models/artifacts.js +97 -0
- package/dist/ui/read-models/artifacts.js.map +1 -0
- package/dist/ui/read-models/overview.d.ts +38 -0
- package/dist/ui/read-models/overview.d.ts.map +1 -0
- package/dist/ui/read-models/overview.js +55 -0
- package/dist/ui/read-models/overview.js.map +1 -0
- package/dist/ui/read-models/recovery.d.ts +18 -0
- package/dist/ui/read-models/recovery.d.ts.map +1 -0
- package/dist/ui/read-models/recovery.js +26 -0
- package/dist/ui/read-models/recovery.js.map +1 -0
- package/dist/ui/read-models/runtime.d.ts +53 -0
- package/dist/ui/read-models/runtime.d.ts.map +1 -0
- package/dist/ui/read-models/runtime.js +86 -0
- package/dist/ui/read-models/runtime.js.map +1 -0
- package/dist/ui/read-models/tasks.d.ts +34 -0
- package/dist/ui/read-models/tasks.d.ts.map +1 -0
- package/dist/ui/read-models/tasks.js +46 -0
- package/dist/ui/read-models/tasks.js.map +1 -0
- package/dist/utils/config.d.ts +30 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +152 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/file.d.ts +24 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +42 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/lock.d.ts +7 -0
- package/dist/utils/lock.d.ts.map +1 -0
- package/dist/utils/lock.js +36 -0
- package/dist/utils/lock.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,832 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { extname, join } from 'path';
|
|
4
|
+
import { ensureDir, readJson, writeJson, writeText } from '../utils/file.js';
|
|
5
|
+
import { buildSuperpowersRegistry } from './superpowers-registry.js';
|
|
6
|
+
import { loadWorkspaceState } from './workspace.js';
|
|
7
|
+
export { buildExecutionContext } from './context.js';
|
|
8
|
+
export const TECH_STACK_OPTIONS = {
|
|
9
|
+
backend: [
|
|
10
|
+
{ name: 'Node.js + TypeScript + NestJS', value: 'nodejs-nestjs', skills: ['backend-patterns', 'tdd-workflow'] },
|
|
11
|
+
{ name: 'Go + Gin', value: 'go-gin', skills: ['golang-patterns', 'backend-patterns'] },
|
|
12
|
+
{ name: 'Java + Spring Boot', value: 'java-spring', skills: ['backend-patterns'] },
|
|
13
|
+
{ name: 'Python + FastAPI', value: 'python-fastapi', skills: ['backend-patterns'] }
|
|
14
|
+
],
|
|
15
|
+
frontend: [
|
|
16
|
+
{ name: 'Vue 3 + TypeScript', value: 'vue3', skills: ['vue-best-practices', 'frontend-design'] },
|
|
17
|
+
{ name: 'React 18 + TypeScript', value: 'react', skills: ['frontend-design'] },
|
|
18
|
+
{ name: 'Angular', value: 'angular', skills: ['frontend-design'] }
|
|
19
|
+
],
|
|
20
|
+
database: [
|
|
21
|
+
{ name: 'PostgreSQL', value: 'postgresql', skills: ['postgres-patterns'] },
|
|
22
|
+
{ name: 'MySQL', value: 'mysql', skills: [] },
|
|
23
|
+
{ name: 'MongoDB', value: 'mongodb', skills: [] },
|
|
24
|
+
{ name: 'PostgreSQL + Redis', value: 'postgres-redis', skills: ['postgres-patterns'] }
|
|
25
|
+
],
|
|
26
|
+
infrastructure: [
|
|
27
|
+
{ name: 'Docker', value: 'docker', skills: [] },
|
|
28
|
+
{ name: 'Docker + Kubernetes', value: 'docker-k8s', skills: [] },
|
|
29
|
+
{ name: 'AWS', value: 'aws', skills: [] },
|
|
30
|
+
{ name: '阿里云', value: 'aliyun', skills: [] }
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
export const AVAILABLE_SKILLS = {
|
|
34
|
+
'frontend-design': {
|
|
35
|
+
name: 'frontend-design',
|
|
36
|
+
patterns: ['ui', 'frontend', '界面', '设计', 'css', '样式', '组件', 'react', 'vue', 'angular', 'html', 'tailwind'],
|
|
37
|
+
description: 'Create distinctive, production-grade frontend interfaces',
|
|
38
|
+
category: 'frontend',
|
|
39
|
+
priority: 9
|
|
40
|
+
},
|
|
41
|
+
'vue-best-practices': {
|
|
42
|
+
name: 'vue-best-practices',
|
|
43
|
+
patterns: ['vue', 'vue3', 'pinia', 'composition api', 'vue router'],
|
|
44
|
+
description: 'Vue.js Composition API with TypeScript',
|
|
45
|
+
category: 'frontend',
|
|
46
|
+
priority: 8
|
|
47
|
+
},
|
|
48
|
+
'backend-patterns': {
|
|
49
|
+
name: 'backend-patterns',
|
|
50
|
+
patterns: ['backend', 'api', 'server', 'rest', 'graphql', 'microservice', '后端', '接口', '服务'],
|
|
51
|
+
description: 'Backend architecture patterns, API design, database optimization',
|
|
52
|
+
category: 'backend',
|
|
53
|
+
priority: 9
|
|
54
|
+
},
|
|
55
|
+
'postgres-patterns': {
|
|
56
|
+
name: 'postgres-patterns',
|
|
57
|
+
patterns: ['postgres', 'postgresql', 'supabase', 'sql', '关系型数据库', '事务'],
|
|
58
|
+
description: 'PostgreSQL database patterns',
|
|
59
|
+
category: 'database',
|
|
60
|
+
priority: 8
|
|
61
|
+
},
|
|
62
|
+
'golang-patterns': {
|
|
63
|
+
name: 'golang-patterns',
|
|
64
|
+
patterns: ['go', 'golang', 'gin', 'gorm'],
|
|
65
|
+
description: 'Idiomatic Go patterns and best practices',
|
|
66
|
+
category: 'language',
|
|
67
|
+
priority: 8
|
|
68
|
+
},
|
|
69
|
+
'tdd-workflow': {
|
|
70
|
+
name: 'tdd-workflow',
|
|
71
|
+
patterns: ['test', '测试', 'tdd', 'unit test', 'jest', 'vitest', 'pytest', 'coverage'],
|
|
72
|
+
description: 'Test-driven development with 80%+ coverage',
|
|
73
|
+
category: 'testing',
|
|
74
|
+
priority: 7
|
|
75
|
+
},
|
|
76
|
+
'claude-api': {
|
|
77
|
+
name: 'claude-api',
|
|
78
|
+
patterns: ['claude api', 'anthropic sdk', 'claude_code', 'ai integration', 'llm api'],
|
|
79
|
+
description: 'Build apps with Claude API or Anthropic SDK',
|
|
80
|
+
category: 'ai',
|
|
81
|
+
priority: 6
|
|
82
|
+
},
|
|
83
|
+
'mcp-builder': {
|
|
84
|
+
name: 'mcp-builder',
|
|
85
|
+
patterns: ['mcp', 'model context protocol', 'mcp server'],
|
|
86
|
+
description: 'Creating MCP servers for LLM tool integration',
|
|
87
|
+
category: 'ai',
|
|
88
|
+
priority: 6
|
|
89
|
+
},
|
|
90
|
+
'docx': {
|
|
91
|
+
name: 'docx',
|
|
92
|
+
patterns: ['word', 'docx', 'document generation', 'report generation'],
|
|
93
|
+
description: 'Create, read, edit Word documents',
|
|
94
|
+
category: 'document',
|
|
95
|
+
priority: 5
|
|
96
|
+
},
|
|
97
|
+
'pdf': {
|
|
98
|
+
name: 'pdf',
|
|
99
|
+
patterns: ['pdf generation', 'pdf parsing', 'pdf extraction'],
|
|
100
|
+
description: 'Reading, extracting, creating PDFs',
|
|
101
|
+
category: 'document',
|
|
102
|
+
priority: 5
|
|
103
|
+
},
|
|
104
|
+
'pptx': {
|
|
105
|
+
name: 'pptx',
|
|
106
|
+
patterns: ['ppt generation', 'presentation', 'slide deck', 'powerpoint'],
|
|
107
|
+
description: 'Creating presentations and slide decks',
|
|
108
|
+
category: 'document',
|
|
109
|
+
priority: 5
|
|
110
|
+
},
|
|
111
|
+
'xlsx': {
|
|
112
|
+
name: 'xlsx',
|
|
113
|
+
patterns: ['excel generation', 'spreadsheet', 'xlsx export', 'csv export', '数据导出'],
|
|
114
|
+
description: 'Working with spreadsheets',
|
|
115
|
+
category: 'document',
|
|
116
|
+
priority: 5
|
|
117
|
+
},
|
|
118
|
+
'web-artifacts-builder': {
|
|
119
|
+
name: 'web-artifacts-builder',
|
|
120
|
+
patterns: ['react dashboard', 'tailwind', 'shadcn/ui', 'complex artifact'],
|
|
121
|
+
description: 'Complex multi-component HTML artifacts',
|
|
122
|
+
category: 'frontend',
|
|
123
|
+
priority: 7
|
|
124
|
+
},
|
|
125
|
+
'algorithmic-art': {
|
|
126
|
+
name: 'algorithmic-art',
|
|
127
|
+
patterns: ['generative art', 'p5.js', 'algorithmic visualization', 'creative coding'],
|
|
128
|
+
description: 'Creating algorithmic art using p5.js',
|
|
129
|
+
category: 'other',
|
|
130
|
+
priority: 4
|
|
131
|
+
},
|
|
132
|
+
'clickhouse-io': {
|
|
133
|
+
name: 'clickhouse-io',
|
|
134
|
+
patterns: ['clickhouse', 'olap', 'analytics', '大数据'],
|
|
135
|
+
description: 'ClickHouse database patterns for analytics',
|
|
136
|
+
category: 'database',
|
|
137
|
+
priority: 6
|
|
138
|
+
},
|
|
139
|
+
'security-review': {
|
|
140
|
+
name: 'security-review',
|
|
141
|
+
patterns: ['auth', 'authentication', 'authorization', 'oauth', 'jwt', 'security', '权限', '认证'],
|
|
142
|
+
description: 'Security patterns and review checklist',
|
|
143
|
+
category: 'backend',
|
|
144
|
+
priority: 8
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
export async function buildPlanFromKnowledge(basePath, options = {}) {
|
|
148
|
+
const workspace = await loadWorkspaceState(basePath);
|
|
149
|
+
const analysis = await analyzeKnowledgeBase(basePath, workspace.indexes.knowledge);
|
|
150
|
+
const template = resolveTemplate(options.template, analysis.projectType);
|
|
151
|
+
const requiredSkills = options.superpowers ? detectRequiredSkills(analysis) : [];
|
|
152
|
+
if (!options.force && workspace.tasks.tasks.length > 0) {
|
|
153
|
+
return {
|
|
154
|
+
phases: workspace.phases.phases,
|
|
155
|
+
tasks: workspace.tasks.tasks,
|
|
156
|
+
analysis,
|
|
157
|
+
requiredSkills,
|
|
158
|
+
template,
|
|
159
|
+
techStack: (await loadTechStack(basePath)) ||
|
|
160
|
+
options.techStack ||
|
|
161
|
+
inferTechStackFromAnalysis(analysis),
|
|
162
|
+
reusedExistingPlan: true
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const techStack = options.techStack ||
|
|
166
|
+
(await options.resolveTechStack?.(analysis)) ||
|
|
167
|
+
(await loadTechStack(basePath)) ||
|
|
168
|
+
inferTechStackFromAnalysis(analysis);
|
|
169
|
+
await saveTechStack(basePath, techStack);
|
|
170
|
+
const phases = generatePhases(template);
|
|
171
|
+
const tasks = generateSmartTasks(phases, template, analysis, requiredSkills, {
|
|
172
|
+
superpowers: options.superpowers,
|
|
173
|
+
execution: options.execution
|
|
174
|
+
}, techStack);
|
|
175
|
+
let planDocumentPath;
|
|
176
|
+
if (options.superpowers) {
|
|
177
|
+
planDocumentPath = await generateSuperpowersPlan(basePath, phases, tasks, analysis, requiredSkills, {
|
|
178
|
+
execution: options.execution
|
|
179
|
+
}, techStack);
|
|
180
|
+
}
|
|
181
|
+
await writeJson(workspace.paths.phases, { phases });
|
|
182
|
+
await writeJson(workspace.paths.tasks, { tasks });
|
|
183
|
+
return {
|
|
184
|
+
phases,
|
|
185
|
+
tasks,
|
|
186
|
+
analysis,
|
|
187
|
+
requiredSkills,
|
|
188
|
+
template,
|
|
189
|
+
techStack,
|
|
190
|
+
reusedExistingPlan: false,
|
|
191
|
+
planDocumentPath
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
export function getTechStackName(category, value) {
|
|
195
|
+
const found = TECH_STACK_OPTIONS[category].find((option) => option.value === value);
|
|
196
|
+
return found?.name || value;
|
|
197
|
+
}
|
|
198
|
+
async function loadTechStack(basePath) {
|
|
199
|
+
const configPath = join(basePath, '.webforge', 'techstack.json');
|
|
200
|
+
if (!existsSync(configPath)) {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const config = await readJson(configPath);
|
|
205
|
+
return {
|
|
206
|
+
backend: config.backend,
|
|
207
|
+
frontend: config.frontend,
|
|
208
|
+
database: config.database,
|
|
209
|
+
infrastructure: Array.isArray(config.infrastructure) ? config.infrastructure : []
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function saveTechStack(basePath, stack) {
|
|
217
|
+
await writeJson(join(basePath, '.webforge', 'techstack.json'), {
|
|
218
|
+
...stack,
|
|
219
|
+
updatedAt: new Date().toISOString()
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function resolveTemplate(requestedTemplate, detectedProjectType) {
|
|
223
|
+
if (!requestedTemplate || requestedTemplate === 'auto') {
|
|
224
|
+
return detectedProjectType || 'web';
|
|
225
|
+
}
|
|
226
|
+
return requestedTemplate;
|
|
227
|
+
}
|
|
228
|
+
async function analyzeKnowledgeBase(basePath, knowledgeEntries) {
|
|
229
|
+
const documents = [];
|
|
230
|
+
const contents = {};
|
|
231
|
+
const keywords = [];
|
|
232
|
+
const techStack = [];
|
|
233
|
+
if (knowledgeEntries.length === 0) {
|
|
234
|
+
return { documents: [], contents: {}, keywords: [], techStack: [] };
|
|
235
|
+
}
|
|
236
|
+
const parsedEntries = new Map(knowledgeEntries
|
|
237
|
+
.filter((entry) => entry.type === 'parsed')
|
|
238
|
+
.map((entry) => [entry.path, entry]));
|
|
239
|
+
const sourceEntries = knowledgeEntries.filter((entry) => entry.type !== 'parsed');
|
|
240
|
+
const sourceBasenameCounts = new Map();
|
|
241
|
+
for (const entry of sourceEntries) {
|
|
242
|
+
const basename = getKnowledgeBasename(entry.path.replace(/^\.webforge\/knowledge\//, ''));
|
|
243
|
+
sourceBasenameCounts.set(basename, (sourceBasenameCounts.get(basename) || 0) + 1);
|
|
244
|
+
}
|
|
245
|
+
const matchedParsedPaths = new Set();
|
|
246
|
+
for (const entry of sourceEntries) {
|
|
247
|
+
const documentId = entry.path.replace(/^\.webforge\/knowledge\//, '');
|
|
248
|
+
const fallbackCandidates = getParsedCandidatePaths(documentId, sourceBasenameCounts);
|
|
249
|
+
const fallback = resolveParsedFallback(documentId, parsedEntries, sourceBasenameCounts);
|
|
250
|
+
const content = await readKnowledgeContent(basePath, entry, fallback);
|
|
251
|
+
if (fallback) {
|
|
252
|
+
for (const candidate of fallbackCandidates) {
|
|
253
|
+
if (candidate && parsedEntries.has(candidate)) {
|
|
254
|
+
matchedParsedPaths.add(candidate);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
documents.push(documentId);
|
|
259
|
+
contents[documentId] = content;
|
|
260
|
+
extractKeywordsFromFilename(documentId.toLowerCase(), keywords);
|
|
261
|
+
if (content) {
|
|
262
|
+
extractTechStackFromContent(content, techStack);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
for (const entry of knowledgeEntries) {
|
|
266
|
+
if (entry.type === 'parsed' &&
|
|
267
|
+
!matchedParsedPaths.has(entry.path)) {
|
|
268
|
+
const documentId = entry.path.replace(/^\.webforge\/knowledge\//, '');
|
|
269
|
+
const content = await readKnowledgeContent(basePath, entry);
|
|
270
|
+
documents.push(documentId);
|
|
271
|
+
contents[documentId] = content;
|
|
272
|
+
extractKeywordsFromFilename(documentId.toLowerCase(), keywords);
|
|
273
|
+
if (content) {
|
|
274
|
+
extractTechStackFromContent(content, techStack);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const allText = [...documents, ...Object.values(contents)].join(' ').toLowerCase();
|
|
279
|
+
let projectType = 'web';
|
|
280
|
+
if (allText.includes('小程序') ||
|
|
281
|
+
allText.includes('移动端') ||
|
|
282
|
+
allText.includes('mobile app')) {
|
|
283
|
+
projectType = 'mobile';
|
|
284
|
+
}
|
|
285
|
+
else if (allText.includes('后台') ||
|
|
286
|
+
allText.includes('管理后台') ||
|
|
287
|
+
allText.includes('admin')) {
|
|
288
|
+
projectType = 'backend';
|
|
289
|
+
}
|
|
290
|
+
else if (allText.includes('全栈') ||
|
|
291
|
+
allText.includes('fullstack') ||
|
|
292
|
+
(allText.includes('前端') && allText.includes('后端'))) {
|
|
293
|
+
projectType = 'web';
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
documents,
|
|
297
|
+
contents,
|
|
298
|
+
projectType,
|
|
299
|
+
keywords: [...new Set(keywords)],
|
|
300
|
+
techStack: [...new Set(techStack)]
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
async function readKnowledgeContent(basePath, entry, parsedFallback) {
|
|
304
|
+
const ext = extname(entry.path).toLowerCase();
|
|
305
|
+
const readableDirectly = entry.type === 'parsed' || ext === '.md' || ext === '.txt';
|
|
306
|
+
if (readableDirectly) {
|
|
307
|
+
return readTextFile(join(basePath, entry.path));
|
|
308
|
+
}
|
|
309
|
+
if (parsedFallback) {
|
|
310
|
+
return readTextFile(join(basePath, parsedFallback.path));
|
|
311
|
+
}
|
|
312
|
+
return '';
|
|
313
|
+
}
|
|
314
|
+
async function readTextFile(path) {
|
|
315
|
+
try {
|
|
316
|
+
return await readFile(path, 'utf-8');
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return '';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function buildParsedFallbackPath(relativeKnowledgePath) {
|
|
323
|
+
const ext = extname(relativeKnowledgePath);
|
|
324
|
+
const extSuffix = ext ? `--${ext.replace(/^\./, '')}` : '';
|
|
325
|
+
return `.webforge/knowledge/parsed/${getKnowledgeStem(relativeKnowledgePath).replace(/[\/]/g, '--')}${extSuffix}.md`;
|
|
326
|
+
}
|
|
327
|
+
function buildLegacyParsedFallbackPath(relativeKnowledgePath) {
|
|
328
|
+
return `.webforge/knowledge/parsed/${getKnowledgeBasename(relativeKnowledgePath)}.md`;
|
|
329
|
+
}
|
|
330
|
+
function buildV1ParsedFallbackPath(relativeKnowledgePath) {
|
|
331
|
+
return `.webforge/knowledge/parsed/${getKnowledgeStem(relativeKnowledgePath).replace(/[\/]/g, '--')}.md`;
|
|
332
|
+
}
|
|
333
|
+
function getKnowledgeStem(relativeKnowledgePath) {
|
|
334
|
+
const ext = extname(relativeKnowledgePath);
|
|
335
|
+
return ext
|
|
336
|
+
? relativeKnowledgePath.slice(0, -ext.length)
|
|
337
|
+
: relativeKnowledgePath;
|
|
338
|
+
}
|
|
339
|
+
function getKnowledgeBasename(relativeKnowledgePath) {
|
|
340
|
+
return getKnowledgeStem(relativeKnowledgePath).split('/').pop() || '';
|
|
341
|
+
}
|
|
342
|
+
function resolveParsedFallback(relativeKnowledgePath, parsedEntries, sourceBasenameCounts) {
|
|
343
|
+
for (const candidate of getParsedCandidatePaths(relativeKnowledgePath, sourceBasenameCounts)) {
|
|
344
|
+
const match = parsedEntries.get(candidate);
|
|
345
|
+
if (match) {
|
|
346
|
+
return match;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
function getParsedCandidatePaths(relativeKnowledgePath, sourceBasenameCounts) {
|
|
352
|
+
const basename = getKnowledgeBasename(relativeKnowledgePath);
|
|
353
|
+
const candidates = [
|
|
354
|
+
buildParsedFallbackPath(relativeKnowledgePath),
|
|
355
|
+
buildV1ParsedFallbackPath(relativeKnowledgePath)
|
|
356
|
+
];
|
|
357
|
+
if ((sourceBasenameCounts.get(basename) || 0) === 1) {
|
|
358
|
+
candidates.push(buildLegacyParsedFallbackPath(relativeKnowledgePath));
|
|
359
|
+
}
|
|
360
|
+
return candidates;
|
|
361
|
+
}
|
|
362
|
+
function extractKeywordsFromFilename(filename, keywords) {
|
|
363
|
+
const keywordMap = {
|
|
364
|
+
'物业': ['物业'],
|
|
365
|
+
'绩效': ['绩效', '考核'],
|
|
366
|
+
'管理': ['管理系统'],
|
|
367
|
+
'用户': ['用户系统', '用户管理'],
|
|
368
|
+
'订单': ['订单系统'],
|
|
369
|
+
'支付': ['支付系统'],
|
|
370
|
+
'库存': ['库存管理'],
|
|
371
|
+
'财务': ['财务系统'],
|
|
372
|
+
'人事': ['人事管理', 'hr'],
|
|
373
|
+
'工单': ['工单系统'],
|
|
374
|
+
'审批': ['审批流程'],
|
|
375
|
+
'报表': ['数据报表', '统计'],
|
|
376
|
+
'小程序': ['小程序', '微信小程序'],
|
|
377
|
+
'移动端': ['移动端', 'app'],
|
|
378
|
+
'后台': ['后台系统', '管理后台']
|
|
379
|
+
};
|
|
380
|
+
for (const [key, values] of Object.entries(keywordMap)) {
|
|
381
|
+
if (filename.includes(key)) {
|
|
382
|
+
keywords.push(...values);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function extractTechStackFromContent(content, techStack) {
|
|
387
|
+
const techPatterns = {
|
|
388
|
+
Vue: ['vue', 'vue3', 'vue.js', 'pinia', 'vuex'],
|
|
389
|
+
React: ['react', 'react.js', 'next.js', 'redux'],
|
|
390
|
+
Angular: ['angular'],
|
|
391
|
+
TypeScript: ['typescript', 'ts'],
|
|
392
|
+
Go: ['golang', 'go语言', 'gin框架'],
|
|
393
|
+
'Node.js': ['node.js', 'nodejs', 'express', 'koa'],
|
|
394
|
+
Python: ['python', 'django', 'flask', 'fastapi'],
|
|
395
|
+
Java: ['java', 'spring', 'springboot'],
|
|
396
|
+
PostgreSQL: ['postgresql', 'postgres'],
|
|
397
|
+
MySQL: ['mysql'],
|
|
398
|
+
MongoDB: ['mongodb'],
|
|
399
|
+
Redis: ['redis'],
|
|
400
|
+
Docker: ['docker', '容器化'],
|
|
401
|
+
Kubernetes: ['kubernetes', 'k8s'],
|
|
402
|
+
AWS: ['aws', 'amazon web services'],
|
|
403
|
+
阿里云: ['阿里云', 'aliyun']
|
|
404
|
+
};
|
|
405
|
+
const lowerContent = content.toLowerCase();
|
|
406
|
+
for (const [tech, patterns] of Object.entries(techPatterns)) {
|
|
407
|
+
if (patterns.some((pattern) => lowerContent.includes(pattern))) {
|
|
408
|
+
techStack.push(tech);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function detectRequiredSkills(analysis) {
|
|
413
|
+
const skillScores = new Map();
|
|
414
|
+
const allText = [
|
|
415
|
+
...analysis.documents,
|
|
416
|
+
...Object.values(analysis.contents),
|
|
417
|
+
...analysis.keywords,
|
|
418
|
+
...analysis.techStack
|
|
419
|
+
]
|
|
420
|
+
.join(' ')
|
|
421
|
+
.toLowerCase();
|
|
422
|
+
for (const [skillId, skillInfo] of Object.entries(AVAILABLE_SKILLS)) {
|
|
423
|
+
let score = 0;
|
|
424
|
+
for (const pattern of skillInfo.patterns) {
|
|
425
|
+
const patternLower = pattern.toLowerCase();
|
|
426
|
+
const regex = new RegExp(`\\b${escapeRegExp(patternLower)}\\b`, 'g');
|
|
427
|
+
const matches = allText.match(regex);
|
|
428
|
+
if (matches) {
|
|
429
|
+
score += matches.length * 2;
|
|
430
|
+
}
|
|
431
|
+
else if (allText.includes(patternLower)) {
|
|
432
|
+
score += 1;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (score > 0) {
|
|
436
|
+
skillScores.set(skillId, score + skillInfo.priority);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (analysis.projectType === 'web') {
|
|
440
|
+
if (!skillScores.has('frontend-design')) {
|
|
441
|
+
skillScores.set('frontend-design', 5);
|
|
442
|
+
}
|
|
443
|
+
if (!skillScores.has('backend-patterns')) {
|
|
444
|
+
skillScores.set('backend-patterns', 5);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else if (analysis.projectType === 'mobile') {
|
|
448
|
+
if (!skillScores.has('frontend-design')) {
|
|
449
|
+
skillScores.set('frontend-design', 5);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
else if (analysis.projectType === 'backend') {
|
|
453
|
+
if (!skillScores.has('backend-patterns')) {
|
|
454
|
+
skillScores.set('backend-patterns', 8);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
for (const tech of analysis.techStack) {
|
|
458
|
+
const techLower = tech.toLowerCase();
|
|
459
|
+
if (techLower.includes('vue') && !skillScores.has('vue-best-practices')) {
|
|
460
|
+
skillScores.set('vue-best-practices', 10);
|
|
461
|
+
}
|
|
462
|
+
if (techLower === 'go' && !skillScores.has('golang-patterns')) {
|
|
463
|
+
skillScores.set('golang-patterns', 10);
|
|
464
|
+
}
|
|
465
|
+
if (techLower.includes('postgres') && !skillScores.has('postgres-patterns')) {
|
|
466
|
+
skillScores.set('postgres-patterns', 10);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (allText.includes('test') || allText.includes('测试') || skillScores.size > 0) {
|
|
470
|
+
skillScores.set('tdd-workflow', Math.max(skillScores.get('tdd-workflow') || 0, 7));
|
|
471
|
+
}
|
|
472
|
+
return Array.from(skillScores.entries())
|
|
473
|
+
.sort((left, right) => right[1] - left[1])
|
|
474
|
+
.slice(0, 8)
|
|
475
|
+
.map(([skill]) => skill);
|
|
476
|
+
}
|
|
477
|
+
function inferTechStackFromAnalysis(analysis) {
|
|
478
|
+
const techStack = {
|
|
479
|
+
backend: 'nodejs-nestjs',
|
|
480
|
+
frontend: 'vue3',
|
|
481
|
+
database: 'postgresql',
|
|
482
|
+
infrastructure: ['docker']
|
|
483
|
+
};
|
|
484
|
+
if (analysis.techStack.some((item) => item.toLowerCase().includes('go'))) {
|
|
485
|
+
techStack.backend = 'go-gin';
|
|
486
|
+
}
|
|
487
|
+
else if (analysis.techStack.some((item) => item.toLowerCase().includes('java'))) {
|
|
488
|
+
techStack.backend = 'java-spring';
|
|
489
|
+
}
|
|
490
|
+
else if (analysis.techStack.some((item) => item.toLowerCase().includes('python'))) {
|
|
491
|
+
techStack.backend = 'python-fastapi';
|
|
492
|
+
}
|
|
493
|
+
if (analysis.techStack.some((item) => item.toLowerCase().includes('react'))) {
|
|
494
|
+
techStack.frontend = 'react';
|
|
495
|
+
}
|
|
496
|
+
else if (analysis.techStack.some((item) => item.toLowerCase().includes('angular'))) {
|
|
497
|
+
techStack.frontend = 'angular';
|
|
498
|
+
}
|
|
499
|
+
if (analysis.techStack.some((item) => item.toLowerCase().includes('mysql'))) {
|
|
500
|
+
techStack.database = 'mysql';
|
|
501
|
+
}
|
|
502
|
+
else if (analysis.techStack.some((item) => item.toLowerCase().includes('mongo'))) {
|
|
503
|
+
techStack.database = 'mongodb';
|
|
504
|
+
}
|
|
505
|
+
else if (analysis.techStack.some((item) => item.toLowerCase().includes('redis'))) {
|
|
506
|
+
techStack.database = 'postgres-redis';
|
|
507
|
+
}
|
|
508
|
+
if (analysis.techStack.some((item) => item.toLowerCase().includes('k8s') ||
|
|
509
|
+
item.toLowerCase().includes('kubernetes'))) {
|
|
510
|
+
techStack.infrastructure = ['docker-k8s'];
|
|
511
|
+
}
|
|
512
|
+
if (analysis.techStack.some((item) => item.toLowerCase().includes('aliyun') || item.includes('阿里云'))) {
|
|
513
|
+
techStack.infrastructure.push('aliyun');
|
|
514
|
+
}
|
|
515
|
+
else if (analysis.techStack.some((item) => item.toLowerCase().includes('aws'))) {
|
|
516
|
+
techStack.infrastructure.push('aws');
|
|
517
|
+
}
|
|
518
|
+
return techStack;
|
|
519
|
+
}
|
|
520
|
+
function generatePhases(template) {
|
|
521
|
+
const now = new Date().toISOString();
|
|
522
|
+
const basePhases = [
|
|
523
|
+
{ id: 'P1', name: '需求分析', depends_on: [] },
|
|
524
|
+
{ id: 'P2', name: '架构设计', depends_on: ['P1'] },
|
|
525
|
+
{ id: 'P3', name: '后端开发', depends_on: ['P2'] },
|
|
526
|
+
{ id: 'P4', name: '前端开发', depends_on: ['P2'] },
|
|
527
|
+
{ id: 'P5', name: '集成测试', depends_on: ['P3', 'P4'] },
|
|
528
|
+
{ id: 'P6', name: '部署上线', depends_on: ['P5'] }
|
|
529
|
+
];
|
|
530
|
+
if (template === 'mobile') {
|
|
531
|
+
basePhases[3].name = '移动端开发';
|
|
532
|
+
}
|
|
533
|
+
else if (template === 'backend') {
|
|
534
|
+
basePhases.splice(3, 1);
|
|
535
|
+
const testingPhase = basePhases.find((phase) => phase.id === 'P5');
|
|
536
|
+
if (testingPhase) {
|
|
537
|
+
testingPhase.depends_on = ['P3'];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return basePhases.map((phase, index) => ({
|
|
541
|
+
id: phase.id,
|
|
542
|
+
name: phase.name,
|
|
543
|
+
status: index === 0 ? 'in_progress' : 'pending',
|
|
544
|
+
progress: 0,
|
|
545
|
+
depends_on: phase.depends_on,
|
|
546
|
+
created_at: now,
|
|
547
|
+
updated_at: now
|
|
548
|
+
}));
|
|
549
|
+
}
|
|
550
|
+
function generateSmartTasks(phases, template, analysis, requiredSkills, options, selectedTechStack) {
|
|
551
|
+
const tasks = [];
|
|
552
|
+
let taskCounter = 1;
|
|
553
|
+
const features = {
|
|
554
|
+
hasAuth: analysis.keywords.some((keyword) => ['用户', '登录', '权限', 'auth', 'authentication'].includes(keyword)),
|
|
555
|
+
hasPayment: analysis.keywords.includes('支付') || analysis.keywords.includes('payment'),
|
|
556
|
+
hasData: analysis.keywords.some((keyword) => ['绩效', '数据', '报表', 'data', 'report'].includes(keyword)),
|
|
557
|
+
hasUI: analysis.keywords.some((keyword) => ['ui', '界面', '设计', '前端', 'frontend'].includes(keyword)),
|
|
558
|
+
hasVue: analysis.techStack.some((item) => item.toLowerCase().includes('vue')),
|
|
559
|
+
hasReact: analysis.techStack.some((item) => item.toLowerCase().includes('react')),
|
|
560
|
+
hasGo: analysis.techStack.some((item) => item.toLowerCase().includes('go')),
|
|
561
|
+
hasPostgres: analysis.techStack.some((item) => item.toLowerCase().includes('postgres'))
|
|
562
|
+
};
|
|
563
|
+
for (const phase of phases) {
|
|
564
|
+
const phaseTasks = createSmartPhaseTasks(phase, taskCounter, template, features, requiredSkills, options, selectedTechStack);
|
|
565
|
+
tasks.push(...phaseTasks);
|
|
566
|
+
taskCounter += phaseTasks.length;
|
|
567
|
+
}
|
|
568
|
+
return tasks;
|
|
569
|
+
}
|
|
570
|
+
function createSmartPhaseTasks(phase, startCounter, template, features, requiredSkills, options, techStack) {
|
|
571
|
+
const tasks = [];
|
|
572
|
+
const now = new Date().toISOString();
|
|
573
|
+
const backendSkill = techStack?.backend.includes('go')
|
|
574
|
+
? 'golang-patterns'
|
|
575
|
+
: 'backend-patterns';
|
|
576
|
+
const frontendSkill = techStack?.frontend.includes('vue')
|
|
577
|
+
? 'vue-best-practices'
|
|
578
|
+
: 'frontend-design';
|
|
579
|
+
const dbSkill = techStack?.database.includes('postgres') ? 'postgres-patterns' : '';
|
|
580
|
+
const taskDefs = {
|
|
581
|
+
P1: [
|
|
582
|
+
{ title: '需求梳理与确认', assignee: 'pm', priority: 1 },
|
|
583
|
+
{ title: '用户故事编写', assignee: 'pm', priority: 2 },
|
|
584
|
+
{ title: '功能清单整理', assignee: 'pm', priority: 2 }
|
|
585
|
+
],
|
|
586
|
+
P2: [
|
|
587
|
+
{ title: '系统架构设计', assignee: 'tech-lead', priority: 1, skills: [backendSkill] },
|
|
588
|
+
{ title: '数据库设计', assignee: 'backend', priority: 1, skills: dbSkill ? [dbSkill] : [] },
|
|
589
|
+
{ title: 'API 接口设计', assignee: 'backend', priority: 2, skills: [backendSkill] },
|
|
590
|
+
{ title: 'UI/UX 设计', assignee: 'frontend', priority: 2, skills: [frontendSkill] }
|
|
591
|
+
],
|
|
592
|
+
P3: [
|
|
593
|
+
{ title: '项目初始化与环境搭建', assignee: 'backend', priority: 1 },
|
|
594
|
+
{ title: '核心业务接口开发', assignee: 'backend', priority: 1, skills: [backendSkill] },
|
|
595
|
+
{ title: '数据模型实现', assignee: 'backend', priority: 1, skills: dbSkill ? [dbSkill] : [] }
|
|
596
|
+
],
|
|
597
|
+
P4: [
|
|
598
|
+
{ title: '前端项目搭建', assignee: 'frontend', priority: 1, skills: [frontendSkill] },
|
|
599
|
+
{ title: '页面组件开发', assignee: 'frontend', priority: 1, skills: [frontendSkill] },
|
|
600
|
+
{ title: 'API 对接集成', assignee: 'frontend', priority: 2, skills: [frontendSkill, backendSkill] }
|
|
601
|
+
],
|
|
602
|
+
P5: [
|
|
603
|
+
{ title: '单元测试编写', assignee: 'qa', priority: 1, skills: ['tdd-workflow'] },
|
|
604
|
+
{ title: '接口测试', assignee: 'qa', priority: 2, skills: ['tdd-workflow'] },
|
|
605
|
+
{ title: '端到端测试', assignee: 'qa', priority: 2, skills: ['tdd-workflow'] },
|
|
606
|
+
{ title: 'Bug 修复', assignee: 'backend', priority: 1 }
|
|
607
|
+
],
|
|
608
|
+
P6: [
|
|
609
|
+
{ title: '生产环境部署', assignee: 'devops', priority: 1 },
|
|
610
|
+
{ title: '监控配置', assignee: 'devops', priority: 2 },
|
|
611
|
+
{ title: '文档交付', assignee: 'pm', priority: 2 }
|
|
612
|
+
]
|
|
613
|
+
};
|
|
614
|
+
if (features.hasAuth && phase.id === 'P3') {
|
|
615
|
+
taskDefs[phase.id].push({ title: '用户认证系统开发', assignee: 'backend', priority: 1, skills: [backendSkill, 'security-review'] }, { title: '权限管理实现', assignee: 'backend', priority: 2, skills: [backendSkill] });
|
|
616
|
+
}
|
|
617
|
+
if (features.hasData && phase.id === 'P3') {
|
|
618
|
+
taskDefs[phase.id].push({
|
|
619
|
+
title: '数据统计接口开发',
|
|
620
|
+
assignee: 'backend',
|
|
621
|
+
priority: 2,
|
|
622
|
+
skills: [backendSkill, dbSkill].filter(Boolean)
|
|
623
|
+
}, { title: '报表导出功能', assignee: 'backend', priority: 3, skills: [backendSkill, 'xlsx'] });
|
|
624
|
+
}
|
|
625
|
+
if (features.hasPayment && phase.id === 'P3') {
|
|
626
|
+
taskDefs[phase.id].push({ title: '支付接口对接', assignee: 'backend', priority: 1, skills: [backendSkill] }, {
|
|
627
|
+
title: '订单系统开发',
|
|
628
|
+
assignee: 'backend',
|
|
629
|
+
priority: 1,
|
|
630
|
+
skills: [backendSkill, dbSkill].filter(Boolean)
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
if (options.superpowers && phase.id === 'P2') {
|
|
634
|
+
taskDefs[phase.id].unshift({
|
|
635
|
+
title: 'Superpowers Skills 配置',
|
|
636
|
+
assignee: 'tech-lead',
|
|
637
|
+
priority: 1,
|
|
638
|
+
skills: requiredSkills
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
const defs = [...(taskDefs[phase.id] || [{ title: `${phase.name} - 通用任务`, assignee: 'backend', priority: 2 }])];
|
|
642
|
+
defs.sort((left, right) => left.priority - right.priority);
|
|
643
|
+
for (let index = 0; index < defs.length; index += 1) {
|
|
644
|
+
const def = defs[index];
|
|
645
|
+
const taskId = `T${String(startCounter + index).padStart(3, '0')}`;
|
|
646
|
+
const dependsOn = index > 0 ? [`T${String(startCounter + index - 1).padStart(3, '0')}`] : [];
|
|
647
|
+
const hasPhaseDeps = phase.depends_on.length > 0;
|
|
648
|
+
const status = dependsOn.length === 0 && !hasPhaseDeps ? 'ready' : 'pending';
|
|
649
|
+
const metadata = {};
|
|
650
|
+
if (options.superpowers) {
|
|
651
|
+
metadata.superpowers = true;
|
|
652
|
+
metadata.execution = options.execution;
|
|
653
|
+
if (def.skills && def.skills.length > 0) {
|
|
654
|
+
metadata.skills = def.skills;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
tasks.push({
|
|
658
|
+
id: taskId,
|
|
659
|
+
phase: phase.id,
|
|
660
|
+
title: def.title,
|
|
661
|
+
description: template === 'mobile' && phase.id === 'P4' ? '移动端实现任务' : '',
|
|
662
|
+
status,
|
|
663
|
+
assignee: def.assignee,
|
|
664
|
+
depends_on: dependsOn,
|
|
665
|
+
priority: def.priority,
|
|
666
|
+
created_at: now,
|
|
667
|
+
updated_at: now,
|
|
668
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {})
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
return tasks;
|
|
672
|
+
}
|
|
673
|
+
async function generateSuperpowersPlan(basePath, phases, tasks, analysis, requiredSkills, options, techStack) {
|
|
674
|
+
const planDir = join(basePath, 'docs', 'superpowers', 'plans');
|
|
675
|
+
await ensureDir(planDir);
|
|
676
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
677
|
+
const planFile = join(planDir, `${timestamp}-implementation-plan.md`);
|
|
678
|
+
const prdSummary = analysis.documents.length > 0
|
|
679
|
+
? analysis.documents.map((document) => `- ${document}`).join('\n')
|
|
680
|
+
: '使用默认模板生成';
|
|
681
|
+
const planContent = generatePlanMarkdown(phases, tasks, requiredSkills, options, prdSummary, techStack);
|
|
682
|
+
await writeText(planFile, planContent);
|
|
683
|
+
await writeJson(join(basePath, '.webforge', 'superpowers.json'), buildSuperpowersRegistry({
|
|
684
|
+
required: requiredSkills,
|
|
685
|
+
optional: [],
|
|
686
|
+
execution: options.execution ?? null,
|
|
687
|
+
...(techStack ? { techStack: techStack } : {})
|
|
688
|
+
}));
|
|
689
|
+
return planFile;
|
|
690
|
+
}
|
|
691
|
+
function generatePlanMarkdown(phases, tasks, requiredSkills, options, prdSummary, techStack) {
|
|
692
|
+
const executionMode = options.execution === 'subagent'
|
|
693
|
+
? 'superpowers:subagent-driven-development (推荐)'
|
|
694
|
+
: 'superpowers:executing-plans';
|
|
695
|
+
const techStackSection = techStack
|
|
696
|
+
? `
|
|
697
|
+
## 技术栈
|
|
698
|
+
|
|
699
|
+
### 后端
|
|
700
|
+
${getTechStackName('backend', techStack.backend)}
|
|
701
|
+
|
|
702
|
+
### 前端
|
|
703
|
+
${getTechStackName('frontend', techStack.frontend)}
|
|
704
|
+
|
|
705
|
+
### 数据库
|
|
706
|
+
${getTechStackName('database', techStack.database)}
|
|
707
|
+
|
|
708
|
+
### 基础设施
|
|
709
|
+
${techStack.infrastructure
|
|
710
|
+
.map((item) => `- ${getTechStackName('infrastructure', item)}`)
|
|
711
|
+
.join('\n')}
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
`
|
|
716
|
+
: '';
|
|
717
|
+
return `# WebForge 项目实现计划
|
|
718
|
+
|
|
719
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use ${executionMode} to implement this plan task-by-task. Steps use checkbox (\`- [ ]\`) syntax for tracking.
|
|
720
|
+
|
|
721
|
+
**Goal:** 基于需求文档完成 Web 项目的开发与交付
|
|
722
|
+
|
|
723
|
+
**Architecture:** 采用多 Agent 协作模式,PM/Frontend/Backend/QA/DevOps 各司其职,通过任务系统协调工作。
|
|
724
|
+
|
|
725
|
+
**Tech Stack:** ${techStack ? `${getTechStackName('backend', techStack.backend)} + ${getTechStackName('frontend', techStack.frontend)} + ${getTechStackName('database', techStack.database)}` : 'TypeScript, Node.js, CLI Framework, Vitest'}
|
|
726
|
+
|
|
727
|
+
**Required Skills:** ${requiredSkills.map((skill) => `@${skill}`).join(', ')}
|
|
728
|
+
|
|
729
|
+
${techStackSection}## PRD 文档
|
|
730
|
+
|
|
731
|
+
${prdSummary}
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
## 阶段规划
|
|
736
|
+
|
|
737
|
+
| 阶段 | 名称 | 任务数 | 状态 |
|
|
738
|
+
|------|------|--------|------|
|
|
739
|
+
${phases
|
|
740
|
+
.map((phase) => {
|
|
741
|
+
const taskCount = tasks.filter((task) => task.phase === phase.id).length;
|
|
742
|
+
return `| ${phase.id} | ${phase.name} | ${taskCount} | ${phase.status} |`;
|
|
743
|
+
})
|
|
744
|
+
.join('\n')}
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
## 任务详情
|
|
749
|
+
|
|
750
|
+
${phases
|
|
751
|
+
.map((phase) => {
|
|
752
|
+
const phaseTasks = tasks.filter((task) => task.phase === phase.id);
|
|
753
|
+
return `### ${phase.id}: ${phase.name}
|
|
754
|
+
|
|
755
|
+
${phaseTasks
|
|
756
|
+
.map((task) => `
|
|
757
|
+
#### ${task.id}: ${task.title}
|
|
758
|
+
|
|
759
|
+
**Assignee:** ${task.assignee}
|
|
760
|
+
**Priority:** ${task.priority}
|
|
761
|
+
**Depends on:** ${task.depends_on.join(', ') || 'None'}
|
|
762
|
+
**Required Skills:** ${(task.metadata?.skills || []).map((skill) => `@${skill}`).join(', ') || 'N/A'}
|
|
763
|
+
|
|
764
|
+
- [ ] **Step 1: 分析任务需求**
|
|
765
|
+
- 阅读相关文档
|
|
766
|
+
- 理解任务目标
|
|
767
|
+
- 识别技术要点
|
|
768
|
+
|
|
769
|
+
- [ ] **Step 2: 设计与规划**
|
|
770
|
+
- 确定实现方案
|
|
771
|
+
- 识别需要的 skills
|
|
772
|
+
- 估算工作量
|
|
773
|
+
|
|
774
|
+
- [ ] **Step 3: 实现开发**
|
|
775
|
+
- 编写代码
|
|
776
|
+
- 遵循项目规范
|
|
777
|
+
- 使用正确的 skills
|
|
778
|
+
|
|
779
|
+
- [ ] **Step 4: 测试验证**
|
|
780
|
+
- 运行测试: \`npm test\`
|
|
781
|
+
- 预期: PASS
|
|
782
|
+
|
|
783
|
+
- [ ] **Step 5: 提交交付**
|
|
784
|
+
- 创建交付物
|
|
785
|
+
- 更新任务状态
|
|
786
|
+
- 通知相关人员
|
|
787
|
+
`)
|
|
788
|
+
.join('')}`;
|
|
789
|
+
})
|
|
790
|
+
.join('\n---\n')}
|
|
791
|
+
|
|
792
|
+
---
|
|
793
|
+
|
|
794
|
+
## Execution Guidelines
|
|
795
|
+
|
|
796
|
+
### Subagent-Driven Mode (Recommended)
|
|
797
|
+
|
|
798
|
+
1. 每个任务由一个独立的 subagent 执行
|
|
799
|
+
2. 任务完成后进行审查
|
|
800
|
+
3. 审查通过后更新任务状态
|
|
801
|
+
4. 继续下一个任务
|
|
802
|
+
|
|
803
|
+
### Skills Usage
|
|
804
|
+
|
|
805
|
+
执行任务时,根据任务内容选择合适的 skills:
|
|
806
|
+
|
|
807
|
+
${requiredSkills
|
|
808
|
+
.map((skill) => `- **@${skill}**: ${AVAILABLE_SKILLS[skill]?.description || 'Use when applicable'}`)
|
|
809
|
+
.join('\n')}
|
|
810
|
+
|
|
811
|
+
### Task State Transitions
|
|
812
|
+
|
|
813
|
+
\`\`\`
|
|
814
|
+
pending → ready → in_progress → completed
|
|
815
|
+
↑ ↓ ↓
|
|
816
|
+
└──── blocked ←────┘
|
|
817
|
+
\`\`\`
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
## Generated
|
|
822
|
+
|
|
823
|
+
- **Date:** ${new Date().toISOString()}
|
|
824
|
+
- **Execution Mode:** ${options.execution}
|
|
825
|
+
- **Total Tasks:** ${tasks.length}
|
|
826
|
+
- **Total Phases:** ${phases.length}
|
|
827
|
+
`;
|
|
828
|
+
}
|
|
829
|
+
function escapeRegExp(value) {
|
|
830
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
831
|
+
}
|
|
832
|
+
//# sourceMappingURL=planning.js.map
|