claude-coder 1.9.2 → 1.10.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.
Files changed (81) hide show
  1. package/README.md +236 -214
  2. package/bin/cli.js +170 -155
  3. package/package.json +55 -55
  4. package/recipes/_shared/roles/developer.md +11 -11
  5. package/recipes/_shared/roles/product.md +12 -12
  6. package/recipes/_shared/roles/tester.md +12 -12
  7. package/recipes/_shared/test/report-format.md +86 -86
  8. package/recipes/backend/base.md +27 -27
  9. package/recipes/backend/components/auth.md +18 -18
  10. package/recipes/backend/components/crud-api.md +18 -18
  11. package/recipes/backend/components/file-service.md +15 -15
  12. package/recipes/backend/manifest.json +20 -20
  13. package/recipes/backend/test/api-test.md +25 -25
  14. package/recipes/console/base.md +37 -37
  15. package/recipes/console/components/modal-form.md +20 -20
  16. package/recipes/console/components/pagination.md +17 -17
  17. package/recipes/console/components/search.md +17 -17
  18. package/recipes/console/components/table-list.md +18 -18
  19. package/recipes/console/components/tabs.md +14 -14
  20. package/recipes/console/components/tree.md +15 -15
  21. package/recipes/console/components/upload.md +15 -15
  22. package/recipes/console/manifest.json +24 -24
  23. package/recipes/console/test/crud-e2e.md +47 -47
  24. package/recipes/h5/base.md +26 -26
  25. package/recipes/h5/components/animation.md +11 -11
  26. package/recipes/h5/components/countdown.md +11 -11
  27. package/recipes/h5/components/share.md +11 -11
  28. package/recipes/h5/components/swiper.md +11 -11
  29. package/recipes/h5/manifest.json +21 -21
  30. package/recipes/h5/test/h5-e2e.md +20 -20
  31. package/src/commands/auth.js +420 -420
  32. package/src/commands/setup-modules/helpers.js +100 -100
  33. package/src/commands/setup-modules/index.js +25 -25
  34. package/src/commands/setup-modules/mcp.js +115 -115
  35. package/src/commands/setup-modules/provider.js +260 -260
  36. package/src/commands/setup-modules/safety.js +47 -47
  37. package/src/commands/setup-modules/simplify.js +52 -52
  38. package/src/commands/setup.js +172 -172
  39. package/src/common/assets.js +259 -245
  40. package/src/common/config.js +147 -125
  41. package/src/common/constants.js +55 -55
  42. package/src/common/indicator.js +260 -260
  43. package/src/common/interaction.js +170 -170
  44. package/src/common/logging.js +77 -77
  45. package/src/common/sdk.js +48 -50
  46. package/src/common/tasks.js +88 -88
  47. package/src/common/utils.js +214 -213
  48. package/src/core/coding.js +35 -33
  49. package/src/core/design.js +268 -0
  50. package/src/core/go.js +264 -264
  51. package/src/core/hooks.js +514 -500
  52. package/src/core/init.js +175 -166
  53. package/src/core/plan.js +194 -188
  54. package/src/core/prompts.js +292 -247
  55. package/src/core/repair.js +36 -36
  56. package/src/core/runner.js +471 -471
  57. package/src/core/scan.js +94 -93
  58. package/src/core/session.js +294 -280
  59. package/src/core/simplify.js +76 -74
  60. package/src/core/state.js +120 -105
  61. package/src/index.js +80 -76
  62. package/templates/{codingSystem.md → coding/system.md} +65 -65
  63. package/templates/{codingUser.md → coding/user.md} +18 -17
  64. package/templates/design/base.md +103 -0
  65. package/templates/design/fixSystem.md +71 -0
  66. package/templates/design/fixUser.md +3 -0
  67. package/templates/design/init.md +304 -0
  68. package/templates/design/system.md +108 -0
  69. package/templates/design/user.md +11 -0
  70. package/templates/{goSystem.md → go/system.md} +130 -130
  71. package/templates/{bash-process.md → other/bash-process.md} +12 -12
  72. package/templates/{coreProtocol.md → other/coreProtocol.md} +30 -29
  73. package/templates/{guidance.json → other/guidance.json} +72 -72
  74. package/templates/{requirements.example.md → other/requirements.example.md} +57 -57
  75. package/templates/{test_rule.md → other/test_rule.md} +192 -194
  76. package/templates/{web-testing.md → other/web-testing.md} +17 -17
  77. package/templates/{planSystem.md → plan/system.md} +78 -78
  78. package/templates/{planUser.md → plan/user.md} +10 -9
  79. package/templates/{scanSystem.md → scan/system.md} +120 -120
  80. package/templates/{scanUser.md → scan/user.md} +10 -10
  81. package/types/index.d.ts +217 -217
@@ -1,33 +1,35 @@
1
- 'use strict';
2
-
3
- const { buildSystemPrompt, buildCodingContext } = require('./prompts');
4
- const { Session } = require('./session');
5
- const { log } = require('../common/config');
6
-
7
- async function executeCoding(config, sessionNum, opts = {}) {
8
- const taskId = opts.taskId || 'unknown';
9
- const dateStr = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 12);
10
-
11
- return Session.run('coding', config, {
12
- sessionNum,
13
- logFileName: `${taskId}_session_${sessionNum}_${dateStr}.log`,
14
- label: `coding task=${taskId}`,
15
-
16
- async execute(session) {
17
- const prompt = buildCodingContext(sessionNum, opts);
18
- const queryOpts = session.buildQueryOptions(opts);
19
- queryOpts.systemPrompt = buildSystemPrompt('coding');
20
- queryOpts.disallowedTools = ['askUserQuestion'];
21
-
22
- const { subtype, cost, usage } = await session.runQuery(prompt, queryOpts);
23
-
24
- if (subtype && subtype !== 'success' && subtype !== 'unknown') {
25
- log('warn', `session 结束原因: ${subtype}`);
26
- }
27
-
28
- return { cost, tokenUsage: usage, subtype: subtype || 'unknown' };
29
- },
30
- });
31
- }
32
-
33
- module.exports = { executeCoding };
1
+ 'use strict';
2
+
3
+ const { buildSystemPrompt, buildCodingContext } = require('./prompts');
4
+ const { Session } = require('./session');
5
+ const { log } = require('../common/config');
6
+
7
+ async function executeCoding(config, sessionNum, opts = {}) {
8
+ const taskId = opts.taskId || 'unknown';
9
+ const dateStr = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 12);
10
+
11
+ return Session.run('coding', config, {
12
+ sessionNum,
13
+ logFileName: `${taskId}_session_${sessionNum}_${dateStr}.log`,
14
+ label: `coding task=${taskId}`,
15
+
16
+ async execute(session) {
17
+ const prompt = buildCodingContext(sessionNum, opts);
18
+ const queryOpts = session.buildQueryOptions(opts);
19
+ queryOpts.systemPrompt = buildSystemPrompt('coding');
20
+ queryOpts.disallowedTools = ['askUserQuestion'];
21
+
22
+ const { subtype, cost, usage } = await session.runQuery(prompt, queryOpts, {
23
+ continue: true,
24
+ });
25
+
26
+ if (subtype && subtype !== 'success' && subtype !== 'unknown') {
27
+ log('warn', `session 结束原因: ${subtype}`);
28
+ }
29
+
30
+ return { cost, tokenUsage: usage, subtype: subtype || 'unknown' };
31
+ },
32
+ });
33
+ }
34
+
35
+ module.exports = { executeCoding };
@@ -0,0 +1,268 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const readline = require('readline');
6
+ const { buildSystemPrompt } = require('./prompts');
7
+ const { log, printModeBanner } = require('../common/config');
8
+ const { assets } = require('../common/assets');
9
+ const { saveDesignState } = require('./state');
10
+ const { Session } = require('./session');
11
+
12
+ // ─── Design Dir ───────────────────────────────────────────
13
+
14
+ function getDesignDir() {
15
+ return assets.dir('design');
16
+ }
17
+
18
+ function scanPenFiles(designDir) {
19
+ const files = [];
20
+ const scan = (dir, prefix = '') => {
21
+ if (!fs.existsSync(dir)) return;
22
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
23
+ if (entry.isDirectory()) {
24
+ scan(path.join(dir, entry.name), prefix + entry.name + '/');
25
+ } else if (entry.name.endsWith('.pen')) {
26
+ files.push({ rel: prefix + entry.name, abs: path.join(dir, entry.name) });
27
+ }
28
+ }
29
+ };
30
+ scan(designDir);
31
+ return files;
32
+ }
33
+
34
+ // ─── Type Resolution ─────────────────────────────────────
35
+
36
+ function resolveType(opts, designDir) {
37
+ if (opts.type) return opts.type;
38
+
39
+ const systemPenPath = path.join(designDir, 'system.lib.pen');
40
+ if (!fs.existsSync(systemPenPath)) return 'init';
41
+
42
+ return 'new';
43
+ }
44
+
45
+ // ─── Prompt Builders ─────────────────────────────────────
46
+
47
+ function hasProjectCode(root) {
48
+ const markers = ['package.json', 'pyproject.toml', 'requirements.txt', 'Cargo.toml', 'go.mod', 'pom.xml'];
49
+ const dirs = ['src', 'lib', 'app', 'frontend', 'web', 'client', 'pages'];
50
+ for (const m of markers) { if (fs.existsSync(path.join(root, m))) return true; }
51
+ for (const d of dirs) {
52
+ const p = path.join(root, d);
53
+ if (fs.existsSync(p) && fs.statSync(p).isDirectory()) return true;
54
+ }
55
+ return false;
56
+ }
57
+
58
+ function buildProjectContext() {
59
+ const root = assets.projectRoot;
60
+ const hasCode = hasProjectCode(root);
61
+ let ctx = `### 项目类型\n${hasCode ? '已有代码项目(设计时应 Read 源码还原真实内容)' : '全新项目(无现有代码,根据需求从零设计)'}\n- 项目根路径: ${root}\n\n`;
62
+
63
+ if (hasCode) {
64
+ const pkgPath = path.join(root, 'package.json');
65
+ if (fs.existsSync(pkgPath)) {
66
+ try {
67
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
68
+ ctx += `### 项目信息\n- name: ${pkg.name || '未定义'}\n- description: ${pkg.description || '未定义'}\n\n`;
69
+ } catch { /* ignore */ }
70
+ }
71
+ }
72
+
73
+ return ctx;
74
+ }
75
+
76
+ function buildDesignPrompt(instruction, designDir) {
77
+ let designContext = `### 设计文件目录\n绝对路径: ${designDir}\n`;
78
+
79
+ const systemPenPath = path.join(designDir, 'system.lib.pen');
80
+ const isInit = !fs.existsSync(systemPenPath);
81
+ designContext += isInit
82
+ ? '### 设计库\n尚未创建 system.lib.pen,请先根据下方「初始化模板」生成。\n\n'
83
+ : '### 设计库\n已有 system.lib.pen,请先 Read 查看并复用。\n\n';
84
+
85
+ if (isInit) {
86
+ const initTemplate = assets.read('designInit') || '';
87
+ if (initTemplate) {
88
+ designContext += `### 初始化模板\n\n${initTemplate}\n\n`;
89
+ }
90
+ }
91
+
92
+ const mapPath = path.join(designDir, 'design_map.json');
93
+ if (fs.existsSync(mapPath)) {
94
+ try {
95
+ const map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
96
+ const pages = Object.entries(map.pages || {});
97
+ if (pages.length > 0) {
98
+ designContext += '### 已有页面\n';
99
+ for (const [name, info] of pages) {
100
+ designContext += `- **${name}**: ${info.description} (${path.join(designDir, info.pen)})\n`;
101
+ }
102
+ designContext += '\n';
103
+ }
104
+ } catch { /* ignore */ }
105
+ }
106
+
107
+ designContext += buildProjectContext();
108
+
109
+ return assets.render('designUser', {
110
+ designContext,
111
+ instruction: instruction
112
+ ? `用户需求:\n${instruction}`
113
+ : '用户未提供需求,使用对话模式收集。',
114
+ modeHint: instruction
115
+ ? '【自动模式】用户已提供需求,直接设计,不要提问。'
116
+ : '【对话模式】使用 AskUserQuestion 工具引导用户描述需求。',
117
+ });
118
+ }
119
+
120
+ function buildFixPrompt(designDir, userInput) {
121
+ const penFiles = scanPenFiles(designDir);
122
+ let designContext = '### 需要检查修复的 .pen 文件\n\n';
123
+ if (penFiles.length === 0) {
124
+ designContext += '(未发现 .pen 文件)\n';
125
+ } else {
126
+ for (const f of penFiles) {
127
+ designContext += `- ${f.abs}\n`;
128
+ }
129
+ }
130
+ designContext += '\n';
131
+
132
+ const instruction = userInput
133
+ ? `用户反馈的问题:\n${userInput}\n\n请 Read 每个文件,检查并修复所有不合规内容。`
134
+ : '请 Read 每个文件,检查并修复所有不合规内容。';
135
+
136
+ return assets.render('designFixUser', { designContext, instruction });
137
+ }
138
+
139
+ // ─── Post-session Summary ─────────────────────────────────
140
+
141
+ function showDesignSummary(designDir) {
142
+ const penFiles = scanPenFiles(designDir);
143
+ if (penFiles.length === 0) {
144
+ log('warn', '设计目录中没有 .pen 文件');
145
+ return 0;
146
+ }
147
+
148
+ console.log('');
149
+ console.log('┌─ 设计文件 ─────────────────────────────────────┐');
150
+ for (const f of penFiles) {
151
+ console.log(`│ ${f.rel.padEnd(52)}│`);
152
+ }
153
+ console.log('└───────────────────────────────────────────────┘');
154
+
155
+ const hasMap = fs.existsSync(path.join(designDir, 'design_map.json'));
156
+ if (hasMap) log('info', 'design_map.json OK');
157
+
158
+ let hasJsonError = false;
159
+ for (const f of penFiles) {
160
+ try {
161
+ JSON.parse(fs.readFileSync(f.abs, 'utf8'));
162
+ } catch (e) {
163
+ hasJsonError = true;
164
+ log('error', `${f.rel}: JSON 语法错误 — ${e.message}`);
165
+ }
166
+ }
167
+ if (hasJsonError) {
168
+ log('warn', '存在 JSON 格式问题,建议运行: claude-coder design --type fix');
169
+ }
170
+
171
+ return penFiles.length;
172
+ }
173
+
174
+ // ─── User Confirm ────────────────────────────────────────
175
+
176
+ function askUser(question) {
177
+ if (!process.stdin.isTTY) return Promise.resolve('');
178
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
179
+ return new Promise(resolve => {
180
+ rl.question(question, answer => { rl.close(); resolve(answer.trim()); });
181
+ });
182
+ }
183
+
184
+ // ─── Fix Session ─────────────────────────────────────────
185
+
186
+ async function runFixSession(config, designDir, userInput, opts) {
187
+ const ts = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 12);
188
+ log('info', '正在修复 .pen 文件...');
189
+
190
+ await Session.run('design', config, {
191
+ logFileName: `design_fix_${ts}.log`,
192
+ label: 'design_fix',
193
+ async execute(session) {
194
+ const queryOpts = session.buildQueryOptions(opts);
195
+ queryOpts.systemPrompt = buildSystemPrompt('designFix');
196
+ return await session.runQuery(buildFixPrompt(designDir, userInput), queryOpts);
197
+ },
198
+ });
199
+
200
+ log('ok', '修复完成');
201
+ showDesignSummary(designDir);
202
+ }
203
+
204
+ // ─── Main Entry ──────────────────────────────────────────
205
+
206
+ async function executeDesign(config, input, opts = {}) {
207
+ if (opts.reset) {
208
+ saveDesignState({});
209
+ log('ok', 'Design 状态已重置');
210
+ return;
211
+ }
212
+
213
+ const designDir = getDesignDir();
214
+ if (!fs.existsSync(designDir)) fs.mkdirSync(designDir, { recursive: true });
215
+ const pagesDir = path.join(designDir, 'pages');
216
+ if (!fs.existsSync(pagesDir)) fs.mkdirSync(pagesDir, { recursive: true });
217
+
218
+ const type = resolveType(opts, designDir);
219
+ const instruction = input || '';
220
+ const isAutoMode = !!instruction;
221
+ const designLabel = type === 'fix' ? '修复' : isAutoMode ? '自动' : '对话';
222
+ printModeBanner('design', `${type} · ${designLabel}`, config?.model);
223
+
224
+ if (!opts.model || !opts.model.includes('glm-5')) {
225
+ log('info', '提示: design 推荐使用 --model glm-5 获得最佳效果');
226
+ }
227
+
228
+ if (type === 'fix') {
229
+ const penFiles = scanPenFiles(designDir);
230
+ if (penFiles.length === 0) {
231
+ log('warn', '设计目录中没有 .pen 文件需要修复');
232
+ return;
233
+ }
234
+ const answer = await askUser(`\n发现 ${penFiles.length} 个 .pen 文件,是否进行修复?(Y/n) `);
235
+ if (answer.toLowerCase() === 'n') { log('info', '已取消'); return; }
236
+ await runFixSession(config, designDir, input, opts);
237
+ return;
238
+ }
239
+
240
+ const ts = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 12);
241
+
242
+ const sessionResult = await Session.run('design', config, {
243
+ logFileName: `design_${ts}_1.log`,
244
+ label: isAutoMode ? 'design_auto' : 'design_dialogue',
245
+ async execute(session) {
246
+ const queryOpts = session.buildQueryOptions(opts);
247
+ queryOpts.systemPrompt = buildSystemPrompt('design');
248
+ return await session.runQuery(buildDesignPrompt(instruction, designDir), queryOpts);
249
+ },
250
+ });
251
+
252
+ if (sessionResult && !sessionResult.success) {
253
+ log('warn', 'AI 会话未正常完成,检查生成结果...');
254
+ }
255
+
256
+ const penCount = showDesignSummary(designDir);
257
+ if (penCount === 0) {
258
+ log('error', 'AI 未生成任何 .pen 文件');
259
+ return;
260
+ }
261
+
262
+ saveDesignState({ lastTimestamp: new Date().toISOString(), designDir, penCount, type });
263
+ log('ok', `设计完成! 文件: ${penCount}`);
264
+ log('info', '迭代调整: claude-coder design "修改xxx"');
265
+ log('info', '修复文件: claude-coder design --type fix');
266
+ }
267
+
268
+ module.exports = { executeDesign };