codemini-cli 0.3.8 → 0.4.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/README.md +121 -1
- package/deployment.md +6 -6
- package/package.json +6 -1
- package/skills/brainstorm/SKILL.md +49 -29
- package/skills/superpowers-lite/SKILL.md +82 -90
- package/skills/writing-plans/SKILL.md +67 -0
- package/src/commands/chat.js +51 -47
- package/src/commands/doctor.js +27 -7
- package/src/commands/run.js +36 -28
- package/src/core/agent-loop.js +191 -10
- package/src/core/chat-runtime.js +170 -11
- package/src/core/command-evaluator.js +66 -0
- package/src/core/command-policy.js +16 -0
- package/src/core/command-risk.js +148 -0
- package/src/core/config-store.js +7 -0
- package/src/core/constants.js +0 -1
- package/src/core/default-system-prompt.js +27 -0
- package/src/core/dream-audit.js +93 -0
- package/src/core/dream-consolidate.js +157 -0
- package/src/core/dream-evaluator.js +99 -0
- package/src/core/fff-adapter.js +386 -0
- package/src/core/memory-prompt.js +23 -0
- package/src/core/memory-store.js +228 -1
- package/src/core/paths.js +13 -1
- package/src/core/project-index.js +2 -2
- package/src/core/shell-profile.js +5 -1
- package/src/core/tool-output.js +184 -0
- package/src/core/tools.js +425 -110
- package/src/tui/chat-app.js +376 -47
- package/src/tui/tool-activity/presenters/system.js +1 -1
|
@@ -23,8 +23,10 @@ const SHELL_PROFILES = {
|
|
|
23
23
|
'npm',
|
|
24
24
|
'npx',
|
|
25
25
|
'python',
|
|
26
|
+
'python3',
|
|
26
27
|
'py',
|
|
27
28
|
'pip',
|
|
29
|
+
'pip3',
|
|
28
30
|
'get-childitem',
|
|
29
31
|
'get-content',
|
|
30
32
|
'select-string',
|
|
@@ -70,7 +72,9 @@ const SHELL_PROFILES = {
|
|
|
70
72
|
'npm',
|
|
71
73
|
'npx',
|
|
72
74
|
'python',
|
|
75
|
+
'python3',
|
|
73
76
|
'pip',
|
|
77
|
+
'pip3',
|
|
74
78
|
'ls',
|
|
75
79
|
'cat',
|
|
76
80
|
'sed',
|
|
@@ -164,7 +168,7 @@ Some tools are loaded on demand through tool_search. Common examples:
|
|
|
164
168
|
- glob for pattern-based file lookup
|
|
165
169
|
- ast_query and read_ast_node for advanced AST-scoped reads and edits
|
|
166
170
|
- list_background_tasks, get_background_task, and stop_background_task for managing long-running background commands
|
|
167
|
-
-
|
|
171
|
+
- save_memory, list_memory, search_memory, and forget_memory for persistent memory operations
|
|
168
172
|
|
|
169
173
|
For structural code edits (functions, classes, methods), prefer AST-scoped reads before editing:
|
|
170
174
|
- Common one-shot workflow: read(path, query=..., capture_name=...) → edit with symbol or ast_target
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import cliTruncate from 'cli-truncate';
|
|
2
|
+
import stripAnsi from 'strip-ansi';
|
|
3
|
+
import { classifyCommandIntent } from './shell.js';
|
|
4
|
+
|
|
5
|
+
const CONTROL_CHARS_RE = /[\u0000-\u0008\u000B-\u001F\u007F]/g;
|
|
6
|
+
|
|
7
|
+
export function sanitizeTextForModel(
|
|
8
|
+
value,
|
|
9
|
+
{
|
|
10
|
+
maxChars = 0,
|
|
11
|
+
maxLineLength = 220,
|
|
12
|
+
maxConsecutiveBlankLines = 1
|
|
13
|
+
} = {}
|
|
14
|
+
) {
|
|
15
|
+
if (value == null) return '';
|
|
16
|
+
|
|
17
|
+
const lines = String(value)
|
|
18
|
+
.replace(/\r\n?/g, '\n')
|
|
19
|
+
.split('\n');
|
|
20
|
+
const output = [];
|
|
21
|
+
let blankRun = 0;
|
|
22
|
+
|
|
23
|
+
for (const rawLine of lines) {
|
|
24
|
+
const line = stripAnsi(rawLine).replace(CONTROL_CHARS_RE, '').replace(/[ \t]+$/g, '');
|
|
25
|
+
if (!line.trim()) {
|
|
26
|
+
blankRun += 1;
|
|
27
|
+
if (blankRun > maxConsecutiveBlankLines) continue;
|
|
28
|
+
output.push('');
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
blankRun = 0;
|
|
33
|
+
output.push(
|
|
34
|
+
maxLineLength > 0
|
|
35
|
+
? cliTruncate(line, maxLineLength, { position: 'end' })
|
|
36
|
+
: line
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let sanitized = output.join('\n').trimEnd();
|
|
41
|
+
if (maxChars > 0 && sanitized.length > maxChars) {
|
|
42
|
+
sanitized = `${sanitized.slice(0, maxChars)}\n... [sanitized output truncated ${sanitized.length - maxChars} chars]`;
|
|
43
|
+
}
|
|
44
|
+
return sanitized;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getToolOutputSanitizeOptions(toolName) {
|
|
48
|
+
const name = String(toolName || '').trim();
|
|
49
|
+
if (name === 'read' || name === 'read_ast_node' || name === 'run' || name === 'web_fetch') {
|
|
50
|
+
return {
|
|
51
|
+
maxLineLength: 0
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function sanitizePreviewLines(value, { maxLineLength = 220 } = {}) {
|
|
58
|
+
const sanitized = sanitizeTextForModel(value, {
|
|
59
|
+
maxLineLength,
|
|
60
|
+
maxConsecutiveBlankLines: 0
|
|
61
|
+
});
|
|
62
|
+
if (!sanitized) return [];
|
|
63
|
+
return sanitized
|
|
64
|
+
.split('\n')
|
|
65
|
+
.map((line) => line.trim())
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function summarizeGitStatusPorcelain(stdout) {
|
|
70
|
+
const modified = [];
|
|
71
|
+
const added = [];
|
|
72
|
+
const deleted = [];
|
|
73
|
+
const untracked = [];
|
|
74
|
+
|
|
75
|
+
for (const line of String(stdout || '').split('\n')) {
|
|
76
|
+
const trimmed = line.trimEnd();
|
|
77
|
+
if (!trimmed) continue;
|
|
78
|
+
const status = trimmed.slice(0, 2);
|
|
79
|
+
const file = trimmed.slice(3).trim();
|
|
80
|
+
if (!file) continue;
|
|
81
|
+
if (status === '??') {
|
|
82
|
+
untracked.push(file);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (status.includes('A')) added.push(file);
|
|
86
|
+
else if (status.includes('D')) deleted.push(file);
|
|
87
|
+
else modified.push(file);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const total = modified.length + added.length + deleted.length + untracked.length;
|
|
91
|
+
if (total === 0) return '';
|
|
92
|
+
const lines = [`[git status: ${total} file(s)]`];
|
|
93
|
+
if (modified.length) lines.push(`modified: ${modified.join(', ')}`);
|
|
94
|
+
if (added.length) lines.push(`added: ${added.join(', ')}`);
|
|
95
|
+
if (deleted.length) lines.push(`deleted: ${deleted.join(', ')}`);
|
|
96
|
+
if (untracked.length) lines.push(`untracked: ${untracked.join(', ')}`);
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function summarizeTestFailure(command, code, stdout, stderr) {
|
|
101
|
+
if (classifyCommandIntent(command).kind !== 'test') {
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
if (Number(code ?? 0) === 0) return '';
|
|
105
|
+
|
|
106
|
+
const lines = sanitizePreviewLines([stdout, stderr].filter(Boolean).join('\n'), { maxLineLength: 220 });
|
|
107
|
+
const kept = [];
|
|
108
|
+
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
if (
|
|
111
|
+
/^FAIL\b/.test(line) ||
|
|
112
|
+
/^Test Suites:/.test(line) ||
|
|
113
|
+
/^Tests:/.test(line) ||
|
|
114
|
+
/AssertionError|Error:|Expected|expected .* to /i.test(line) ||
|
|
115
|
+
/^\s*at\b/.test(line) ||
|
|
116
|
+
/:\d+:\d+\)?$/.test(line)
|
|
117
|
+
) {
|
|
118
|
+
kept.push(line);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (kept.length === 0) return '';
|
|
123
|
+
return [`[test failure: exit ${code ?? 1}]`, ...kept.slice(0, 8)].join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function summarizeInstallOutput(command, code, stdout) {
|
|
127
|
+
if (classifyCommandIntent(command).kind !== 'install') return '';
|
|
128
|
+
|
|
129
|
+
const lines = sanitizePreviewLines(stdout, { maxLineLength: 220 });
|
|
130
|
+
const kept = [];
|
|
131
|
+
for (const line of lines) {
|
|
132
|
+
if (
|
|
133
|
+
/\b(?:added|removed|changed|audited) \d+ package/i.test(line) ||
|
|
134
|
+
/\bvulnerabilit(?:y|ies)\b/i.test(line) ||
|
|
135
|
+
/looking for funding/i.test(line)
|
|
136
|
+
) {
|
|
137
|
+
kept.push(line);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (kept.length === 0) return '';
|
|
141
|
+
return [`[install summary: exit ${code ?? 0}]`, ...kept.slice(0, 6)].join('\n');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function summarizeBuildOutput(command, code, stdout, stderr) {
|
|
145
|
+
if (classifyCommandIntent(command).kind !== 'build') return '';
|
|
146
|
+
if (Number(code ?? 0) === 0) return '';
|
|
147
|
+
|
|
148
|
+
const lines = sanitizePreviewLines([stdout, stderr].filter(Boolean).join('\n'), { maxLineLength: 220 });
|
|
149
|
+
const kept = [];
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
if (
|
|
152
|
+
/\berror\b/i.test(line) ||
|
|
153
|
+
/Build failed/i.test(line) ||
|
|
154
|
+
/failed with/i.test(line)
|
|
155
|
+
) {
|
|
156
|
+
kept.push(line);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (kept.length === 0) return '';
|
|
160
|
+
return [`[build failure: exit ${code ?? 1}]`, ...kept.slice(0, 8)].join('\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function summarizeRunOutput(result) {
|
|
164
|
+
const command = String(result?.command || '').trim();
|
|
165
|
+
const stdout = String(result?.stdout || '');
|
|
166
|
+
const stderr = String(result?.stderr || '');
|
|
167
|
+
const code = result?.code ?? 0;
|
|
168
|
+
|
|
169
|
+
if (/^git\s+status\b.*(?:--short|-s)\b/i.test(command)) {
|
|
170
|
+
const gitSummary = summarizeGitStatusPorcelain(stdout);
|
|
171
|
+
if (gitSummary) return gitSummary;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const installSummary = summarizeInstallOutput(command, code, stdout);
|
|
175
|
+
if (installSummary) return installSummary;
|
|
176
|
+
|
|
177
|
+
const buildSummary = summarizeBuildOutput(command, code, stdout, stderr);
|
|
178
|
+
if (buildSummary) return buildSummary;
|
|
179
|
+
|
|
180
|
+
const testSummary = summarizeTestFailure(command, code, stdout, stderr);
|
|
181
|
+
if (testSummary) return testSummary;
|
|
182
|
+
|
|
183
|
+
return '';
|
|
184
|
+
}
|