codemini-cli 0.5.10 → 0.5.11

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 (59) hide show
  1. package/OPERATIONS.md +242 -242
  2. package/README.md +588 -588
  3. package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-7HL7yft8.js → highlighted-body-OFNGDK62-CANOG7Xg.js} +1 -1
  4. package/codemini-web/dist/assets/{index-BK75hMb2.js → index-B71xykPM.js} +108 -108
  5. package/codemini-web/dist/assets/index-Dkq1DdDX.css +2 -0
  6. package/codemini-web/dist/assets/mermaid-GHXKKRXX-Z_w7M93P.js +1 -0
  7. package/codemini-web/dist/index.html +23 -23
  8. package/codemini-web/lib/approval-manager.js +32 -32
  9. package/codemini-web/lib/runtime-bridge.js +17 -11
  10. package/codemini-web/server.js +534 -205
  11. package/deployment.md +212 -212
  12. package/package.json +1 -1
  13. package/skills/brainstorm/SKILL.md +77 -77
  14. package/skills/codemini.skills.json +40 -40
  15. package/skills/grill-me/SKILL.md +30 -30
  16. package/skills/superpowers-lite/SKILL.md +82 -82
  17. package/src/cli.js +74 -74
  18. package/src/commands/chat.js +210 -210
  19. package/src/commands/run.js +313 -313
  20. package/src/commands/skill.js +438 -304
  21. package/src/commands/web.js +57 -57
  22. package/src/core/agent-loop.js +980 -980
  23. package/src/core/ast.js +309 -307
  24. package/src/core/chat-runtime.js +6261 -6253
  25. package/src/core/command-evaluator.js +72 -72
  26. package/src/core/command-loader.js +311 -311
  27. package/src/core/command-policy.js +301 -301
  28. package/src/core/command-risk.js +156 -156
  29. package/src/core/config-store.js +289 -289
  30. package/src/core/constants.js +18 -1
  31. package/src/core/context-compact.js +365 -365
  32. package/src/core/default-system-prompt.js +114 -107
  33. package/src/core/dream-audit.js +105 -105
  34. package/src/core/dream-consolidate.js +229 -229
  35. package/src/core/dream-evaluator.js +185 -185
  36. package/src/core/fff-adapter.js +383 -383
  37. package/src/core/memory-store.js +543 -543
  38. package/src/core/project-index.js +737 -548
  39. package/src/core/project-instructions.js +98 -98
  40. package/src/core/provider/anthropic.js +514 -514
  41. package/src/core/provider/openai-compatible.js +501 -501
  42. package/src/core/reflect-skill.js +178 -178
  43. package/src/core/reply-language.js +40 -40
  44. package/src/core/session-store.js +474 -474
  45. package/src/core/shell-profile.js +237 -237
  46. package/src/core/shell.js +323 -323
  47. package/src/core/soul.js +69 -69
  48. package/src/core/system-prompt-composer.js +52 -52
  49. package/src/core/tool-args.js +199 -154
  50. package/src/core/tool-output.js +184 -184
  51. package/src/core/tool-result-store.js +206 -206
  52. package/src/core/tools.js +3024 -2893
  53. package/src/core/version.js +11 -11
  54. package/src/tui/chat-app.js +5171 -5171
  55. package/src/tui/tool-activity/presenters/misc.js +30 -30
  56. package/src/tui/tool-activity/presenters/system.js +20 -20
  57. package/templates/project-requirements/report-shell.html +582 -582
  58. package/codemini-web/dist/assets/index-BSdIdn3L.css +0 -2
  59. package/codemini-web/dist/assets/mermaid-GHXKKRXX-Dg9qh8mg.js +0 -1
@@ -1,206 +1,206 @@
1
- import fs from 'node:fs/promises';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { BoundedCache } from './bounded-cache.js';
5
- import { trimInline } from './string-utils.js';
6
-
7
- const TOOL_RESULT_DISK_THRESHOLD = 6000;
8
- const PREVIEW_SIZE_BYTES = 2000;
9
- const TOOL_RESULTS_SUBDIR = 'tool-results';
10
-
11
- let currentResultDir = null;
12
- let resultDirReady = false;
13
-
14
- const storedResults = new BoundedCache({
15
- maxSize: 64,
16
- ttlMs: 30 * 60 * 1000,
17
- onEvict(_key, value) {
18
- if (value?.filePath) {
19
- fs.unlink(value.filePath).catch(() => {});
20
- }
21
- }
22
- });
23
-
24
- const readCache = new BoundedCache({ maxSize: 128, ttlMs: 10 * 60 * 1000 });
25
-
26
- function generatePreview(content) {
27
- if (content.length <= PREVIEW_SIZE_BYTES) {
28
- return { preview: content, hasMore: false };
29
- }
30
- const truncated = content.slice(0, PREVIEW_SIZE_BYTES);
31
- const lastNewline = truncated.lastIndexOf('\n');
32
- const cutPoint = lastNewline > PREVIEW_SIZE_BYTES * 0.5 ? lastNewline : PREVIEW_SIZE_BYTES;
33
- return { preview: content.slice(0, cutPoint), hasMore: true };
34
- }
35
-
36
- function formatFileSize(chars) {
37
- if (chars < 1024) return `${chars} B`;
38
- return `${(chars / 1024).toFixed(1)} KB`;
39
- }
40
-
41
- export function setResultDir(dir) {
42
- currentResultDir = dir ? path.join(dir, TOOL_RESULTS_SUBDIR) : null;
43
- resultDirReady = false;
44
- }
45
-
46
- async function ensureResultDir() {
47
- if (!currentResultDir) return false;
48
- if (!resultDirReady) {
49
- await fs.mkdir(currentResultDir, { recursive: true });
50
- resultDirReady = true;
51
- }
52
- return true;
53
- }
54
-
55
- export async function storeResultIfNeeded(callId, formattedContent, rawResult) {
56
- if (formattedContent.length <= TOOL_RESULT_DISK_THRESHOLD) {
57
- return formattedContent;
58
- }
59
- try {
60
- const ready = await ensureResultDir();
61
- const dir = ready ? currentResultDir : path.join(os.tmpdir(), 'codemini-results');
62
- if (!resultDirReady && dir === currentResultDir) {
63
- await fs.mkdir(dir, { recursive: true });
64
- } else if (!resultDirReady) {
65
- await fs.mkdir(dir, { recursive: true });
66
- }
67
- const filePath = path.join(dir, `${callId}.txt`);
68
- const payload = typeof rawResult === 'string' ? rawResult : JSON.stringify(rawResult, null, 2);
69
- await fs.writeFile(filePath, payload, 'utf-8');
70
- const summary = summarizeToolResult(rawResult);
71
- const { preview, hasMore } = generatePreview(payload);
72
- storedResults.set(callId, { filePath, summary });
73
-
74
- return `<persisted-output>
75
- Output too large (${formatFileSize(payload.length)}). Full output saved to: ${filePath}
76
-
77
- Preview (first ${formatFileSize(PREVIEW_SIZE_BYTES)}):
78
- ${preview}${hasMore ? '\n...' : ''}
79
-
80
- Summary: ${summary}
81
- </persisted-output>`;
82
- } catch {
83
- return formattedContent;
84
- }
85
- }
86
-
87
- export function clearResultStore() {
88
- const files = [];
89
- for (const [, val] of storedResults.entries()) {
90
- files.push(val.filePath);
91
- }
92
- storedResults.clear();
93
- readCache.clear();
94
- return Promise.allSettled(files.map((filePath) => fs.unlink(filePath).catch(() => {})));
95
- }
96
-
97
- export function checkReadDedup(filePath, startLine, endLine, mtimeMs) {
98
- const key = `${filePath}:${startLine || 0}:${endLine || 0}:${mtimeMs}`;
99
- if (readCache.has(key)) {
100
- return true;
101
- }
102
- readCache.set(key, true);
103
- return false;
104
- }
105
-
106
- export function summarizeToolResult(result) {
107
- if (result === null || result === undefined) return 'no output';
108
- if (typeof result === 'string') {
109
- const oneLine = result.replace(/\s+/g, ' ').trim();
110
- return oneLine.length > 90 ? `${oneLine.slice(0, 87)}...` : oneLine || 'empty string';
111
- }
112
- if (typeof result === 'object') {
113
- const obj = result;
114
- if (Array.isArray(obj)) return `array(${obj.length})`;
115
- if ('deleted' in obj && 'path' in obj) {
116
- const kind = trimInline(obj.type || 'item', 16);
117
- const target = trimInline(obj.path || '', 96);
118
- if (obj.deleted) return target ? `deleted ${kind} ${target}` : `deleted ${kind}`;
119
- if (obj.cancelled) return target ? `cancelled delete ${target}` : 'cancelled delete';
120
- }
121
- if ('path' in obj && 'action' in obj) {
122
- const p = String(obj.path || '');
123
- const action = String(obj.action || 'write');
124
- const line = Number(obj.changed_line || 1);
125
- const suffix =
126
- action === 'delete'
127
- ? 'deleted'
128
- : action === 'create'
129
- ? 'created'
130
- : action === 'patch'
131
- ? 'patched'
132
- : action === 'replace_block' || action === 'replace_text'
133
- ? 'edited'
134
- : action === 'append'
135
- ? 'appended'
136
- : 'updated';
137
- return p ? `${suffix} ${p}${line > 0 ? ` @L${line}` : ''}` : suffix;
138
- }
139
- if ('path' in obj && 'phase' in obj) {
140
- const phase = String(obj.phase || '');
141
- const p = String(obj.path || '');
142
- const total = Number(obj.total_lines);
143
- const start =
144
- Number(obj.suggested_start_line || obj.start_line) > 0
145
- ? Number(obj.suggested_start_line || obj.start_line)
146
- : 1;
147
- const end =
148
- Number(obj.suggested_end_line || obj.end_line) >= start
149
- ? Number(obj.suggested_end_line || obj.end_line)
150
- : start;
151
- const rangeText = start > 0 && end >= start ? ` lines ${start}-${end}` : '';
152
- const totalText = total > 0 ? ` of ${total}` : '';
153
- const enclosingText = obj.enclosing_symbol ? ` in ${obj.enclosing_symbol}` : '';
154
- const errorText = obj.error ? ` (${trimInline(obj.error, 64)})` : '';
155
- const truncatedText = obj.truncated ? ' [truncated]' : '';
156
- return phase === 'metadata'
157
- ? `metadata for ${p}${rangeText}${totalText}${errorText}`
158
- : `content from ${p}${rangeText}${totalText}${enclosingText}${truncatedText}`;
159
- }
160
- if ('stdout' in obj || 'stderr' in obj || 'code' in obj) {
161
- const stdout = trimInline(obj.stdout || '', 96);
162
- const stderr = trimInline(obj.stderr || '', 96);
163
- const command = trimInline(obj.command || '', 72);
164
- const lead = command ? `${command} -> ` : '';
165
- if (stdout) return `${lead}exit ${obj.code ?? 0}\nstdout: ${stdout}`;
166
- if (stderr) return `${lead}exit ${obj.code ?? 0}\nstderr: ${stderr}`;
167
- return `${lead}exit ${obj.code ?? 0}`;
168
- }
169
- if ('task_id' in obj && 'startup_confirmed' in obj) {
170
- const status = trimInline(obj.status || 'unknown', 32);
171
- const taskId = trimInline(obj.task_id || '', 24);
172
- const source = trimInline(obj.startup_source || '', 24);
173
- const outputFile = trimInline(obj.output_file || '', 72);
174
- const output = Array.isArray(obj.recent_output) ? trimInline(obj.recent_output.slice(-1)[0] || '', 96) : '';
175
- return `${taskId || 'task'} ${status}${source ? ` (${source})` : ''}${outputFile ? ` -> ${outputFile}` : ''}${output ? `\n${output}` : ''}`;
176
- }
177
- if ('tasks' in obj && Array.isArray(obj.tasks)) {
178
- const count = obj.tasks.length;
179
- const first = obj.tasks[0];
180
- const lead = first?.task_id ? `${trimInline(first.task_id, 24)} ${trimInline(first.status || 'unknown', 24)}` : '';
181
- return `tasks(${count})${lead ? `\n${lead}` : ''}`;
182
- }
183
- if ('files' in obj && Array.isArray(obj.files)) {
184
- return `patched ${obj.files.length} file(s)`;
185
- }
186
- if ('diff' in obj && 'new_hash' in obj && 'path' in obj) {
187
- const p = String(obj.path || '');
188
- return p ? `diff preview for ${p}` : 'diff preview';
189
- }
190
- if ('created' in obj && Array.isArray(obj.created)) {
191
- return `created ${obj.created.length} task(s)`;
192
- }
193
- if ('tasks' in obj && Array.isArray(obj.tasks)) {
194
- return `${obj.tasks.length} task(s)`;
195
- }
196
- if ('newTodos' in obj && Array.isArray(obj.newTodos)) {
197
- return obj.newTodos.length > 0 ? `updated ${obj.newTodos.length} todo item(s)` : 'cleared todo list';
198
- }
199
- if ('newPlan' in obj) {
200
- return obj.newPlan ? `updated plan state (${String(obj.newPlan.status || 'draft')})` : 'cleared plan state';
201
- }
202
- const keys = Object.keys(obj);
203
- return keys.length > 0 ? `keys: ${keys.slice(0, 5).join(',')}` : 'object';
204
- }
205
- return String(result);
206
- }
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { BoundedCache } from './bounded-cache.js';
5
+ import { trimInline } from './string-utils.js';
6
+
7
+ const TOOL_RESULT_DISK_THRESHOLD = 6000;
8
+ const PREVIEW_SIZE_BYTES = 2000;
9
+ const TOOL_RESULTS_SUBDIR = 'tool-results';
10
+
11
+ let currentResultDir = null;
12
+ let resultDirReady = false;
13
+
14
+ const storedResults = new BoundedCache({
15
+ maxSize: 64,
16
+ ttlMs: 30 * 60 * 1000,
17
+ onEvict(_key, value) {
18
+ if (value?.filePath) {
19
+ fs.unlink(value.filePath).catch(() => {});
20
+ }
21
+ }
22
+ });
23
+
24
+ const readCache = new BoundedCache({ maxSize: 128, ttlMs: 10 * 60 * 1000 });
25
+
26
+ function generatePreview(content) {
27
+ if (content.length <= PREVIEW_SIZE_BYTES) {
28
+ return { preview: content, hasMore: false };
29
+ }
30
+ const truncated = content.slice(0, PREVIEW_SIZE_BYTES);
31
+ const lastNewline = truncated.lastIndexOf('\n');
32
+ const cutPoint = lastNewline > PREVIEW_SIZE_BYTES * 0.5 ? lastNewline : PREVIEW_SIZE_BYTES;
33
+ return { preview: content.slice(0, cutPoint), hasMore: true };
34
+ }
35
+
36
+ function formatFileSize(chars) {
37
+ if (chars < 1024) return `${chars} B`;
38
+ return `${(chars / 1024).toFixed(1)} KB`;
39
+ }
40
+
41
+ export function setResultDir(dir) {
42
+ currentResultDir = dir ? path.join(dir, TOOL_RESULTS_SUBDIR) : null;
43
+ resultDirReady = false;
44
+ }
45
+
46
+ async function ensureResultDir() {
47
+ if (!currentResultDir) return false;
48
+ if (!resultDirReady) {
49
+ await fs.mkdir(currentResultDir, { recursive: true });
50
+ resultDirReady = true;
51
+ }
52
+ return true;
53
+ }
54
+
55
+ export async function storeResultIfNeeded(callId, formattedContent, rawResult) {
56
+ if (formattedContent.length <= TOOL_RESULT_DISK_THRESHOLD) {
57
+ return formattedContent;
58
+ }
59
+ try {
60
+ const ready = await ensureResultDir();
61
+ const dir = ready ? currentResultDir : path.join(os.tmpdir(), 'codemini-results');
62
+ if (!resultDirReady && dir === currentResultDir) {
63
+ await fs.mkdir(dir, { recursive: true });
64
+ } else if (!resultDirReady) {
65
+ await fs.mkdir(dir, { recursive: true });
66
+ }
67
+ const filePath = path.join(dir, `${callId}.txt`);
68
+ const payload = typeof rawResult === 'string' ? rawResult : JSON.stringify(rawResult, null, 2);
69
+ await fs.writeFile(filePath, payload, 'utf-8');
70
+ const summary = summarizeToolResult(rawResult);
71
+ const { preview, hasMore } = generatePreview(payload);
72
+ storedResults.set(callId, { filePath, summary });
73
+
74
+ return `<persisted-output>
75
+ Output too large (${formatFileSize(payload.length)}). Full output saved to: ${filePath}
76
+
77
+ Preview (first ${formatFileSize(PREVIEW_SIZE_BYTES)}):
78
+ ${preview}${hasMore ? '\n...' : ''}
79
+
80
+ Summary: ${summary}
81
+ </persisted-output>`;
82
+ } catch {
83
+ return formattedContent;
84
+ }
85
+ }
86
+
87
+ export function clearResultStore() {
88
+ const files = [];
89
+ for (const [, val] of storedResults.entries()) {
90
+ files.push(val.filePath);
91
+ }
92
+ storedResults.clear();
93
+ readCache.clear();
94
+ return Promise.allSettled(files.map((filePath) => fs.unlink(filePath).catch(() => {})));
95
+ }
96
+
97
+ export function checkReadDedup(filePath, startLine, endLine, mtimeMs) {
98
+ const key = `${filePath}:${startLine || 0}:${endLine || 0}:${mtimeMs}`;
99
+ if (readCache.has(key)) {
100
+ return true;
101
+ }
102
+ readCache.set(key, true);
103
+ return false;
104
+ }
105
+
106
+ export function summarizeToolResult(result) {
107
+ if (result === null || result === undefined) return 'no output';
108
+ if (typeof result === 'string') {
109
+ const oneLine = result.replace(/\s+/g, ' ').trim();
110
+ return oneLine.length > 90 ? `${oneLine.slice(0, 87)}...` : oneLine || 'empty string';
111
+ }
112
+ if (typeof result === 'object') {
113
+ const obj = result;
114
+ if (Array.isArray(obj)) return `array(${obj.length})`;
115
+ if ('deleted' in obj && 'path' in obj) {
116
+ const kind = trimInline(obj.type || 'item', 16);
117
+ const target = trimInline(obj.path || '', 96);
118
+ if (obj.deleted) return target ? `deleted ${kind} ${target}` : `deleted ${kind}`;
119
+ if (obj.cancelled) return target ? `cancelled delete ${target}` : 'cancelled delete';
120
+ }
121
+ if ('path' in obj && 'action' in obj) {
122
+ const p = String(obj.path || '');
123
+ const action = String(obj.action || 'write');
124
+ const line = Number(obj.changed_line || 1);
125
+ const suffix =
126
+ action === 'delete'
127
+ ? 'deleted'
128
+ : action === 'create'
129
+ ? 'created'
130
+ : action === 'patch'
131
+ ? 'patched'
132
+ : action === 'replace_block' || action === 'replace_text'
133
+ ? 'edited'
134
+ : action === 'append'
135
+ ? 'appended'
136
+ : 'updated';
137
+ return p ? `${suffix} ${p}${line > 0 ? ` @L${line}` : ''}` : suffix;
138
+ }
139
+ if ('path' in obj && 'phase' in obj) {
140
+ const phase = String(obj.phase || '');
141
+ const p = String(obj.path || '');
142
+ const total = Number(obj.total_lines);
143
+ const start =
144
+ Number(obj.suggested_start_line || obj.start_line) > 0
145
+ ? Number(obj.suggested_start_line || obj.start_line)
146
+ : 1;
147
+ const end =
148
+ Number(obj.suggested_end_line || obj.end_line) >= start
149
+ ? Number(obj.suggested_end_line || obj.end_line)
150
+ : start;
151
+ const rangeText = start > 0 && end >= start ? ` lines ${start}-${end}` : '';
152
+ const totalText = total > 0 ? ` of ${total}` : '';
153
+ const enclosingText = obj.enclosing_symbol ? ` in ${obj.enclosing_symbol}` : '';
154
+ const errorText = obj.error ? ` (${trimInline(obj.error, 64)})` : '';
155
+ const truncatedText = obj.truncated ? ' [truncated]' : '';
156
+ return phase === 'metadata'
157
+ ? `metadata for ${p}${rangeText}${totalText}${errorText}`
158
+ : `content from ${p}${rangeText}${totalText}${enclosingText}${truncatedText}`;
159
+ }
160
+ if ('stdout' in obj || 'stderr' in obj || 'code' in obj) {
161
+ const stdout = trimInline(obj.stdout || '', 96);
162
+ const stderr = trimInline(obj.stderr || '', 96);
163
+ const command = trimInline(obj.command || '', 72);
164
+ const lead = command ? `${command} -> ` : '';
165
+ if (stdout) return `${lead}exit ${obj.code ?? 0}\nstdout: ${stdout}`;
166
+ if (stderr) return `${lead}exit ${obj.code ?? 0}\nstderr: ${stderr}`;
167
+ return `${lead}exit ${obj.code ?? 0}`;
168
+ }
169
+ if ('task_id' in obj && 'startup_confirmed' in obj) {
170
+ const status = trimInline(obj.status || 'unknown', 32);
171
+ const taskId = trimInline(obj.task_id || '', 24);
172
+ const source = trimInline(obj.startup_source || '', 24);
173
+ const outputFile = trimInline(obj.output_file || '', 72);
174
+ const output = Array.isArray(obj.recent_output) ? trimInline(obj.recent_output.slice(-1)[0] || '', 96) : '';
175
+ return `${taskId || 'task'} ${status}${source ? ` (${source})` : ''}${outputFile ? ` -> ${outputFile}` : ''}${output ? `\n${output}` : ''}`;
176
+ }
177
+ if ('tasks' in obj && Array.isArray(obj.tasks)) {
178
+ const count = obj.tasks.length;
179
+ const first = obj.tasks[0];
180
+ const lead = first?.task_id ? `${trimInline(first.task_id, 24)} ${trimInline(first.status || 'unknown', 24)}` : '';
181
+ return `tasks(${count})${lead ? `\n${lead}` : ''}`;
182
+ }
183
+ if ('files' in obj && Array.isArray(obj.files)) {
184
+ return `patched ${obj.files.length} file(s)`;
185
+ }
186
+ if ('diff' in obj && 'new_hash' in obj && 'path' in obj) {
187
+ const p = String(obj.path || '');
188
+ return p ? `diff preview for ${p}` : 'diff preview';
189
+ }
190
+ if ('created' in obj && Array.isArray(obj.created)) {
191
+ return `created ${obj.created.length} task(s)`;
192
+ }
193
+ if ('tasks' in obj && Array.isArray(obj.tasks)) {
194
+ return `${obj.tasks.length} task(s)`;
195
+ }
196
+ if ('newTodos' in obj && Array.isArray(obj.newTodos)) {
197
+ return obj.newTodos.length > 0 ? `updated ${obj.newTodos.length} todo item(s)` : 'cleared todo list';
198
+ }
199
+ if ('newPlan' in obj) {
200
+ return obj.newPlan ? `updated plan state (${String(obj.newPlan.status || 'draft')})` : 'cleared plan state';
201
+ }
202
+ const keys = Object.keys(obj);
203
+ return keys.length > 0 ? `keys: ${keys.slice(0, 5).join(',')}` : 'object';
204
+ }
205
+ return String(result);
206
+ }