codeep 1.2.66 → 1.2.68
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/dist/renderer/App.d.ts +2 -0
- package/dist/renderer/App.js +14 -3
- package/dist/renderer/Screen.js +28 -9
- package/dist/renderer/agentExecution.js +2 -0
- package/dist/utils/agent.js +9 -5
- package/dist/utils/shell.d.ts +8 -0
- package/dist/utils/shell.js +103 -2
- package/dist/utils/toolExecution.js +5 -5
- package/dist/utils/verify.d.ts +5 -5
- package/dist/utils/verify.js +50 -14
- package/package.json +1 -1
package/dist/renderer/App.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare class App {
|
|
|
44
44
|
private agentMaxIterations;
|
|
45
45
|
private agentActions;
|
|
46
46
|
private agentThinking;
|
|
47
|
+
private agentWaitingForAI;
|
|
47
48
|
private pasteInfo;
|
|
48
49
|
private pasteInfoOpen;
|
|
49
50
|
private codeBlockCounter;
|
|
@@ -179,6 +180,7 @@ export declare class App {
|
|
|
179
180
|
* Set agent thinking text
|
|
180
181
|
*/
|
|
181
182
|
setAgentThinking(text: string): void;
|
|
183
|
+
setAgentWaitingForAI(waiting: boolean): void;
|
|
182
184
|
/**
|
|
183
185
|
* Paste from system clipboard (Ctrl+V)
|
|
184
186
|
*/
|
package/dist/renderer/App.js
CHANGED
|
@@ -102,6 +102,7 @@ export class App {
|
|
|
102
102
|
agentMaxIterations = 0;
|
|
103
103
|
agentActions = [];
|
|
104
104
|
agentThinking = '';
|
|
105
|
+
agentWaitingForAI = false;
|
|
105
106
|
// Paste detection state
|
|
106
107
|
pasteInfo = null;
|
|
107
108
|
pasteInfoOpen = false;
|
|
@@ -365,6 +366,7 @@ export class App {
|
|
|
365
366
|
this.agentMaxIterations = 0;
|
|
366
367
|
this.agentActions = [];
|
|
367
368
|
this.agentThinking = '';
|
|
369
|
+
this.agentWaitingForAI = true;
|
|
368
370
|
this.isLoading = false; // Clear loading state when agent takes over
|
|
369
371
|
this.startSpinner();
|
|
370
372
|
}
|
|
@@ -394,6 +396,10 @@ export class App {
|
|
|
394
396
|
this.agentThinking = text;
|
|
395
397
|
this.render();
|
|
396
398
|
}
|
|
399
|
+
setAgentWaitingForAI(waiting) {
|
|
400
|
+
this.agentWaitingForAI = waiting;
|
|
401
|
+
this.render();
|
|
402
|
+
}
|
|
397
403
|
/**
|
|
398
404
|
* Paste from system clipboard (Ctrl+V)
|
|
399
405
|
*/
|
|
@@ -1892,8 +1898,12 @@ export class App {
|
|
|
1892
1898
|
this.screen.write(titlePadLeft, y, title, PRIMARY_COLOR + style.bold);
|
|
1893
1899
|
this.screen.write(titlePadLeft + title.length, y, '─'.repeat(Math.max(0, titlePadRight)), PRIMARY_COLOR);
|
|
1894
1900
|
y++;
|
|
1895
|
-
// Current action line (
|
|
1896
|
-
|
|
1901
|
+
// Current action line (clear first to avoid stale text from longer previous paths)
|
|
1902
|
+
this.screen.writeLine(y, '');
|
|
1903
|
+
if (this.agentWaitingForAI) {
|
|
1904
|
+
this.screen.write(1, y, 'Thinking...', fg.gray);
|
|
1905
|
+
}
|
|
1906
|
+
else if (this.agentActions.length > 0) {
|
|
1897
1907
|
const lastAction = this.agentActions[this.agentActions.length - 1];
|
|
1898
1908
|
const actionLabel = this.getActionLabel(lastAction.type);
|
|
1899
1909
|
const actionColor = this.getActionColor(lastAction.type);
|
|
@@ -1906,7 +1916,8 @@ export class App {
|
|
|
1906
1916
|
this.screen.write(1, y, 'Starting...', fg.gray);
|
|
1907
1917
|
}
|
|
1908
1918
|
y++;
|
|
1909
|
-
// Stats line: Files and step info
|
|
1919
|
+
// Stats line: Files and step info (clear line first to avoid stale text)
|
|
1920
|
+
this.screen.writeLine(y, '');
|
|
1910
1921
|
let x = 1;
|
|
1911
1922
|
// File changes
|
|
1912
1923
|
if (stats.writes > 0) {
|
package/dist/renderer/Screen.js
CHANGED
|
@@ -119,6 +119,7 @@ export class Screen {
|
|
|
119
119
|
this.buffer[y][x] = { char: ' ', style: '' };
|
|
120
120
|
}
|
|
121
121
|
// Parse text character by character, tracking ANSI escape sequences
|
|
122
|
+
// Use index-based loop to handle ANSI sequences, but read full Unicode code points
|
|
122
123
|
let col = 0;
|
|
123
124
|
let i = 0;
|
|
124
125
|
let currentStyle = prefixStyle;
|
|
@@ -132,8 +133,8 @@ export class Screen {
|
|
|
132
133
|
}
|
|
133
134
|
if (escEnd < text.length) {
|
|
134
135
|
const escSeq = text.slice(i, escEnd + 1);
|
|
135
|
-
// Reset code clears style
|
|
136
|
-
if (escSeq === '\x1b[0m') {
|
|
136
|
+
// Reset code clears accumulated style back to prefix
|
|
137
|
+
if (escSeq === '\x1b[0m' || escSeq === '\x1b[m') {
|
|
137
138
|
currentStyle = prefixStyle;
|
|
138
139
|
}
|
|
139
140
|
else {
|
|
@@ -146,17 +147,20 @@ export class Screen {
|
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
else {
|
|
149
|
-
//
|
|
150
|
-
const
|
|
150
|
+
// Read full Unicode code point (handles surrogate pairs for emoji)
|
|
151
|
+
const codePoint = text.codePointAt(i) ?? text.charCodeAt(i);
|
|
152
|
+
const char = String.fromCodePoint(codePoint);
|
|
153
|
+
const charLen = char.length; // surrogate pair = 2, BMP char = 1
|
|
154
|
+
const w = charWidth(char);
|
|
151
155
|
if (col < this.width) {
|
|
152
|
-
this.buffer[y][col] = { char
|
|
156
|
+
this.buffer[y][col] = { char, style: currentStyle };
|
|
153
157
|
// Wide char: fill next cell with empty placeholder
|
|
154
158
|
if (w === 2 && col + 1 < this.width) {
|
|
155
159
|
this.buffer[y][col + 1] = { char: '', style: currentStyle };
|
|
156
160
|
}
|
|
157
161
|
}
|
|
158
162
|
col += w;
|
|
159
|
-
i
|
|
163
|
+
i += charLen;
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
}
|
|
@@ -192,8 +196,14 @@ export class Screen {
|
|
|
192
196
|
lineLength = wordLength;
|
|
193
197
|
}
|
|
194
198
|
else {
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
if (line) {
|
|
200
|
+
line += ' ' + word;
|
|
201
|
+
lineLength += 1 + wordLength;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
line = word;
|
|
205
|
+
lineLength = wordLength;
|
|
206
|
+
}
|
|
197
207
|
}
|
|
198
208
|
}
|
|
199
209
|
if (line && currentY < this.height) {
|
|
@@ -236,8 +246,17 @@ export class Screen {
|
|
|
236
246
|
if (cell.char === renderedCell.char && cell.style === renderedCell.style) {
|
|
237
247
|
continue;
|
|
238
248
|
}
|
|
239
|
-
//
|
|
249
|
+
// Wide-char placeholder cell (right half of emoji/CJK):
|
|
250
|
+
// If previously rendered something here, clear it with a space so there's no ghost.
|
|
251
|
+
// The wide char itself (left cell) already moved the terminal cursor past this cell.
|
|
240
252
|
if (cell.char === '') {
|
|
253
|
+
if (renderedCell.char !== '') {
|
|
254
|
+
// Previous char here was something visible — overwrite with space to clear ghost
|
|
255
|
+
output += cursor.to(y + 1, x + 1);
|
|
256
|
+
output += style.reset;
|
|
257
|
+
lastStyle = '';
|
|
258
|
+
output += ' ';
|
|
259
|
+
}
|
|
241
260
|
this.rendered[y][x] = { ...cell };
|
|
242
261
|
continue;
|
|
243
262
|
}
|
|
@@ -142,12 +142,14 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
142
142
|
chatHistory: app.getChatHistory(),
|
|
143
143
|
onIteration: (iteration, message) => {
|
|
144
144
|
app.updateAgentProgress(iteration);
|
|
145
|
+
app.setAgentWaitingForAI(true); // Waiting for AI response between tool calls
|
|
145
146
|
// Show special status messages (timeout retries, verification) but not generic iteration messages
|
|
146
147
|
if (message && !message.startsWith('Iteration ')) {
|
|
147
148
|
app.addMessage({ role: 'system', content: `_${message}_` });
|
|
148
149
|
}
|
|
149
150
|
},
|
|
150
151
|
onToolCall: (tool) => {
|
|
152
|
+
app.setAgentWaitingForAI(false); // AI responded, executing tool
|
|
151
153
|
const toolName = tool.tool.toLowerCase();
|
|
152
154
|
const target = tool.parameters.path ||
|
|
153
155
|
tool.parameters.command ||
|
package/dist/utils/agent.js
CHANGED
|
@@ -283,12 +283,16 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
283
283
|
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
|
|
284
284
|
continue;
|
|
285
285
|
}
|
|
286
|
-
//
|
|
287
|
-
|
|
286
|
+
// Retry on transient errors: rate-limit, server errors, network failures
|
|
287
|
+
const isRateLimit = err.message.includes('429');
|
|
288
|
+
const isServerError = err.message.includes('500') || err.message.includes('502') || err.message.includes('503') || err.message.includes('529');
|
|
289
|
+
const isNetworkError = ['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND', 'EPIPE', 'EAI_AGAIN'].some(code => err.message.includes(code));
|
|
290
|
+
if (isRateLimit || isServerError || isNetworkError) {
|
|
288
291
|
retryCount++;
|
|
289
292
|
const waitSec = Math.min(5 * retryCount, 30); // 5s, 10s, 15s … max 30s
|
|
290
|
-
|
|
291
|
-
|
|
293
|
+
const code = isRateLimit ? '429' : isServerError ? '5xx' : 'network';
|
|
294
|
+
debug(`${code} error (retry ${retryCount}/${maxTimeoutRetries}), waiting ${waitSec}s`);
|
|
295
|
+
opts.onIteration?.(iteration, `Server error (${code}), retrying in ${waitSec}s... (${retryCount}/${maxTimeoutRetries})`);
|
|
292
296
|
if (retryCount >= maxTimeoutRetries) {
|
|
293
297
|
throw error; // Give up after max retries
|
|
294
298
|
}
|
|
@@ -468,7 +472,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
468
472
|
}
|
|
469
473
|
opts.onIteration?.(iteration, `Verification attempt ${fixAttempt + 1}/${maxFixAttempts}`);
|
|
470
474
|
// Run verifications
|
|
471
|
-
const verifyResults = runAllVerifications(projectContext.root || process.cwd(), {
|
|
475
|
+
const verifyResults = await runAllVerifications(projectContext.root || process.cwd(), {
|
|
472
476
|
runBuild: true,
|
|
473
477
|
runTest: true,
|
|
474
478
|
runTypecheck: true,
|
package/dist/utils/shell.d.ts
CHANGED
|
@@ -27,10 +27,18 @@ export declare function validateCommand(command: string, args: string[], options
|
|
|
27
27
|
* Execute a shell command with safety checks
|
|
28
28
|
*/
|
|
29
29
|
export declare function executeCommand(command: string, args?: string[], options?: CommandOptions): CommandResult;
|
|
30
|
+
/**
|
|
31
|
+
* Execute a shell command asynchronously (non-blocking)
|
|
32
|
+
*/
|
|
33
|
+
export declare function executeCommandAsync(command: string, args?: string[], options?: CommandOptions): Promise<CommandResult>;
|
|
30
34
|
/**
|
|
31
35
|
* Execute a command and return only stdout if successful
|
|
32
36
|
*/
|
|
33
37
|
export declare function execSimple(command: string, args?: string[], options?: CommandOptions): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Execute a command asynchronously and return only stdout if successful
|
|
40
|
+
*/
|
|
41
|
+
export declare function execSimpleAsync(command: string, args?: string[], options?: CommandOptions): Promise<string | null>;
|
|
34
42
|
/**
|
|
35
43
|
* Check if a command exists in PATH
|
|
36
44
|
*/
|
package/dist/utils/shell.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shell command execution utilities with safety checks
|
|
3
3
|
*/
|
|
4
|
-
import { spawnSync } from 'child_process';
|
|
4
|
+
import { spawnSync, spawn } from 'child_process';
|
|
5
5
|
import { resolve, relative, isAbsolute } from 'path';
|
|
6
6
|
import { existsSync } from 'fs';
|
|
7
7
|
// Dangerous command patterns that should never be executed
|
|
@@ -68,7 +68,7 @@ const ALLOWED_COMMANDS = new Set([
|
|
|
68
68
|
// Linting/Formatting
|
|
69
69
|
'eslint', 'prettier', 'black', 'rustfmt',
|
|
70
70
|
// Other common tools
|
|
71
|
-
'echo', 'pwd', 'which', 'env', 'date',
|
|
71
|
+
'echo', 'pwd', 'which', 'env', 'date', 'sleep',
|
|
72
72
|
'curl', 'wget', // allowed but patterns checked
|
|
73
73
|
'tar', 'unzip', 'zip',
|
|
74
74
|
// HTTP tools
|
|
@@ -205,6 +205,100 @@ export function executeCommand(command, args = [], options) {
|
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Execute a shell command asynchronously (non-blocking)
|
|
210
|
+
*/
|
|
211
|
+
export function executeCommandAsync(command, args = [], options) {
|
|
212
|
+
return new Promise((resolve) => {
|
|
213
|
+
const startTime = Date.now();
|
|
214
|
+
const cwd = options?.cwd || process.cwd();
|
|
215
|
+
const timeout = options?.timeout || 60000;
|
|
216
|
+
// Validate command first (synchronous, fast)
|
|
217
|
+
const validation = validateCommand(command, args, options);
|
|
218
|
+
if (!validation.valid) {
|
|
219
|
+
resolve({
|
|
220
|
+
success: false,
|
|
221
|
+
stdout: '',
|
|
222
|
+
stderr: validation.reason || 'Command validation failed',
|
|
223
|
+
exitCode: -1,
|
|
224
|
+
duration: 0,
|
|
225
|
+
command,
|
|
226
|
+
args,
|
|
227
|
+
});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Ensure cwd exists
|
|
231
|
+
if (!existsSync(cwd)) {
|
|
232
|
+
resolve({
|
|
233
|
+
success: false,
|
|
234
|
+
stdout: '',
|
|
235
|
+
stderr: `Working directory does not exist: ${cwd}`,
|
|
236
|
+
exitCode: -1,
|
|
237
|
+
duration: 0,
|
|
238
|
+
command,
|
|
239
|
+
args,
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const child = spawn(command, args, {
|
|
244
|
+
cwd,
|
|
245
|
+
env: { ...process.env, ...options?.env },
|
|
246
|
+
});
|
|
247
|
+
let stdout = '';
|
|
248
|
+
let stderr = '';
|
|
249
|
+
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
250
|
+
child.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
251
|
+
let settled = false;
|
|
252
|
+
const timer = setTimeout(() => {
|
|
253
|
+
if (settled)
|
|
254
|
+
return;
|
|
255
|
+
settled = true;
|
|
256
|
+
child.kill('SIGTERM');
|
|
257
|
+
const duration = Date.now() - startTime;
|
|
258
|
+
resolve({
|
|
259
|
+
success: false,
|
|
260
|
+
stdout,
|
|
261
|
+
stderr: `Command timed out after ${timeout}ms`,
|
|
262
|
+
exitCode: -1,
|
|
263
|
+
duration,
|
|
264
|
+
command,
|
|
265
|
+
args,
|
|
266
|
+
});
|
|
267
|
+
}, timeout);
|
|
268
|
+
child.on('close', (code) => {
|
|
269
|
+
if (settled)
|
|
270
|
+
return;
|
|
271
|
+
settled = true;
|
|
272
|
+
clearTimeout(timer);
|
|
273
|
+
const duration = Date.now() - startTime;
|
|
274
|
+
resolve({
|
|
275
|
+
success: code === 0,
|
|
276
|
+
stdout,
|
|
277
|
+
stderr,
|
|
278
|
+
exitCode: code ?? -1,
|
|
279
|
+
duration,
|
|
280
|
+
command,
|
|
281
|
+
args,
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
child.on('error', (err) => {
|
|
285
|
+
if (settled)
|
|
286
|
+
return;
|
|
287
|
+
settled = true;
|
|
288
|
+
clearTimeout(timer);
|
|
289
|
+
const duration = Date.now() - startTime;
|
|
290
|
+
resolve({
|
|
291
|
+
success: false,
|
|
292
|
+
stdout: '',
|
|
293
|
+
stderr: err.message,
|
|
294
|
+
exitCode: -1,
|
|
295
|
+
duration,
|
|
296
|
+
command,
|
|
297
|
+
args,
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
208
302
|
/**
|
|
209
303
|
* Execute a command and return only stdout if successful
|
|
210
304
|
*/
|
|
@@ -212,6 +306,13 @@ export function execSimple(command, args = [], options) {
|
|
|
212
306
|
const result = executeCommand(command, args, options);
|
|
213
307
|
return result.success ? result.stdout.trim() : null;
|
|
214
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Execute a command asynchronously and return only stdout if successful
|
|
311
|
+
*/
|
|
312
|
+
export async function execSimpleAsync(command, args = [], options) {
|
|
313
|
+
const result = await executeCommandAsync(command, args, options);
|
|
314
|
+
return result.success ? result.stdout.trim() : null;
|
|
315
|
+
}
|
|
215
316
|
/**
|
|
216
317
|
* Check if a command exists in PATH
|
|
217
318
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync, readdirSync, statSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, rmSync, realpathSync } from 'fs';
|
|
10
10
|
import { join, dirname, relative, resolve, isAbsolute } from 'path';
|
|
11
|
-
import {
|
|
11
|
+
import { executeCommandAsync } from './shell.js';
|
|
12
12
|
import { recordWrite, recordEdit, recordDelete, recordMkdir, recordCommand } from './history.js';
|
|
13
13
|
import { loadIgnoreRules, isIgnored } from './gitignore.js';
|
|
14
14
|
import { normalizeToolName } from './toolParsing.js';
|
|
@@ -276,7 +276,7 @@ export async function executeTool(toolCall, projectRoot) {
|
|
|
276
276
|
if (!command)
|
|
277
277
|
return { success: false, output: '', error: 'Missing required parameter: command', tool, parameters };
|
|
278
278
|
recordCommand(command, args);
|
|
279
|
-
const result =
|
|
279
|
+
const result = await executeCommandAsync(command, args, {
|
|
280
280
|
cwd: projectRoot,
|
|
281
281
|
projectRoot,
|
|
282
282
|
timeout: 120000,
|
|
@@ -293,7 +293,7 @@ export async function executeTool(toolCall, projectRoot) {
|
|
|
293
293
|
const validation = validatePath(searchPath, projectRoot);
|
|
294
294
|
if (!validation.valid)
|
|
295
295
|
return { success: false, output: '', error: validation.error, tool, parameters };
|
|
296
|
-
const result =
|
|
296
|
+
const result = await executeCommandAsync('grep', ['-rn', '--include=*.{ts,tsx,js,jsx,json,md,css,html,py,go,rs,rb,kt,kts,swift,php,java,cs,c,cpp,h,hpp,vue,svelte,yaml,yml,toml,sh,sql,xml,scss,less}', pattern, validation.absolutePath], {
|
|
297
297
|
cwd: projectRoot,
|
|
298
298
|
projectRoot,
|
|
299
299
|
timeout: 30000,
|
|
@@ -322,7 +322,7 @@ export async function executeTool(toolCall, projectRoot) {
|
|
|
322
322
|
else {
|
|
323
323
|
findArgs.push('-name', pattern, '-print');
|
|
324
324
|
}
|
|
325
|
-
const result =
|
|
325
|
+
const result = await executeCommandAsync('find', findArgs, { cwd: projectRoot, projectRoot, timeout: 15000 });
|
|
326
326
|
if (result.exitCode === 0 || result.stdout) {
|
|
327
327
|
const files = result.stdout.split('\n').filter(Boolean);
|
|
328
328
|
const relativePaths = files.map(f => relative(projectRoot, f) || f).slice(0, 100);
|
|
@@ -342,7 +342,7 @@ export async function executeTool(toolCall, projectRoot) {
|
|
|
342
342
|
catch {
|
|
343
343
|
return { success: false, output: '', error: 'Invalid URL format', tool, parameters };
|
|
344
344
|
}
|
|
345
|
-
const result =
|
|
345
|
+
const result = await executeCommandAsync('curl', ['-s', '-L', '-m', '30', '-A', 'Codeep/1.0', '--max-filesize', '1000000', url], {
|
|
346
346
|
cwd: projectRoot,
|
|
347
347
|
projectRoot,
|
|
348
348
|
timeout: 35000,
|
package/dist/utils/verify.d.ts
CHANGED
|
@@ -38,23 +38,23 @@ export declare function detectProjectScripts(projectRoot: string): {
|
|
|
38
38
|
/**
|
|
39
39
|
* Run build verification
|
|
40
40
|
*/
|
|
41
|
-
export declare function runBuildVerification(projectRoot: string, timeout?: number): VerifyResult | null
|
|
41
|
+
export declare function runBuildVerification(projectRoot: string, timeout?: number): Promise<VerifyResult | null>;
|
|
42
42
|
/**
|
|
43
43
|
* Run test verification
|
|
44
44
|
*/
|
|
45
|
-
export declare function runTestVerification(projectRoot: string, timeout?: number): VerifyResult | null
|
|
45
|
+
export declare function runTestVerification(projectRoot: string, timeout?: number): Promise<VerifyResult | null>;
|
|
46
46
|
/**
|
|
47
47
|
* Run TypeScript type checking
|
|
48
48
|
*/
|
|
49
|
-
export declare function runTypecheckVerification(projectRoot: string, timeout?: number): VerifyResult | null
|
|
49
|
+
export declare function runTypecheckVerification(projectRoot: string, timeout?: number): Promise<VerifyResult | null>;
|
|
50
50
|
/**
|
|
51
51
|
* Run lint verification
|
|
52
52
|
*/
|
|
53
|
-
export declare function runLintVerification(projectRoot: string, timeout?: number): VerifyResult | null
|
|
53
|
+
export declare function runLintVerification(projectRoot: string, timeout?: number): Promise<VerifyResult | null>;
|
|
54
54
|
/**
|
|
55
55
|
* Run all verifications
|
|
56
56
|
*/
|
|
57
|
-
export declare function runAllVerifications(projectRoot: string, options?: Partial<VerifyOptions>): VerifyResult[]
|
|
57
|
+
export declare function runAllVerifications(projectRoot: string, options?: Partial<VerifyOptions>): Promise<VerifyResult[]>;
|
|
58
58
|
/**
|
|
59
59
|
* Format verification results for display
|
|
60
60
|
*/
|
package/dist/utils/verify.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, readFileSync } from 'fs';
|
|
6
6
|
import { join } from 'path';
|
|
7
|
-
import {
|
|
7
|
+
import { executeCommandAsync } from './shell.js';
|
|
8
8
|
const DEFAULT_OPTIONS = {
|
|
9
9
|
runBuild: true,
|
|
10
10
|
runTest: true,
|
|
@@ -123,9 +123,9 @@ export function detectProjectScripts(projectRoot) {
|
|
|
123
123
|
/**
|
|
124
124
|
* Run a verification command
|
|
125
125
|
*/
|
|
126
|
-
function runVerifyCommand(type, command, args, projectRoot, timeout) {
|
|
126
|
+
async function runVerifyCommand(type, command, args, projectRoot, timeout) {
|
|
127
127
|
const startTime = Date.now();
|
|
128
|
-
const result =
|
|
128
|
+
const result = await executeCommandAsync(command, args, {
|
|
129
129
|
cwd: projectRoot,
|
|
130
130
|
projectRoot,
|
|
131
131
|
timeout,
|
|
@@ -134,6 +134,15 @@ function runVerifyCommand(type, command, args, projectRoot, timeout) {
|
|
|
134
134
|
const output = result.stdout + '\n' + result.stderr;
|
|
135
135
|
// Parse errors from output
|
|
136
136
|
const errors = parseErrors(output, type);
|
|
137
|
+
// If command failed but no errors were parsed, surface the failure reason explicitly
|
|
138
|
+
if (!result.success && errors.length === 0) {
|
|
139
|
+
const reason = result.stderr?.includes('timed out')
|
|
140
|
+
? `Command timed out after ${Math.round(duration / 1000)}s. This build tool may be too slow for verification. Consider adding a faster typecheck script to package.json.`
|
|
141
|
+
: result.stderr?.includes('not in the allowed list') || result.stderr?.includes('not allowed')
|
|
142
|
+
? `Command '${command}' is not allowed. Check shell.ts ALLOWED_COMMANDS.`
|
|
143
|
+
: result.stderr?.trim() || result.stdout?.trim() || 'Command failed with no output';
|
|
144
|
+
errors.push({ severity: 'error', message: reason });
|
|
145
|
+
}
|
|
137
146
|
return {
|
|
138
147
|
success: result.success,
|
|
139
148
|
type,
|
|
@@ -246,7 +255,7 @@ function parseErrors(output, type) {
|
|
|
246
255
|
/**
|
|
247
256
|
* Run build verification
|
|
248
257
|
*/
|
|
249
|
-
export function runBuildVerification(projectRoot, timeout = 120000) {
|
|
258
|
+
export async function runBuildVerification(projectRoot, timeout = 120000) {
|
|
250
259
|
const scripts = detectProjectScripts(projectRoot);
|
|
251
260
|
if (!scripts.build) {
|
|
252
261
|
return null;
|
|
@@ -266,6 +275,16 @@ export function runBuildVerification(projectRoot, timeout = 120000) {
|
|
|
266
275
|
args = ['run', 'build'];
|
|
267
276
|
}
|
|
268
277
|
else {
|
|
278
|
+
if (!existsSync(join(projectRoot, 'node_modules'))) {
|
|
279
|
+
return {
|
|
280
|
+
success: false,
|
|
281
|
+
type: 'build',
|
|
282
|
+
command: `${scripts.packageManager} run ${scripts.build}`,
|
|
283
|
+
output: 'node_modules not found. Run npm install first.',
|
|
284
|
+
errors: [{ severity: 'error', message: 'node_modules not found. Run npm install first.' }],
|
|
285
|
+
duration: 0,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
269
288
|
command = scripts.packageManager;
|
|
270
289
|
args = ['run', scripts.build];
|
|
271
290
|
}
|
|
@@ -274,7 +293,7 @@ export function runBuildVerification(projectRoot, timeout = 120000) {
|
|
|
274
293
|
/**
|
|
275
294
|
* Run test verification
|
|
276
295
|
*/
|
|
277
|
-
export function runTestVerification(projectRoot, timeout = 120000) {
|
|
296
|
+
export async function runTestVerification(projectRoot, timeout = 120000) {
|
|
278
297
|
const scripts = detectProjectScripts(projectRoot);
|
|
279
298
|
if (!scripts.test) {
|
|
280
299
|
return null;
|
|
@@ -306,6 +325,16 @@ export function runTestVerification(projectRoot, timeout = 120000) {
|
|
|
306
325
|
args = ['artisan', 'test'];
|
|
307
326
|
}
|
|
308
327
|
else {
|
|
328
|
+
if (!existsSync(join(projectRoot, 'node_modules'))) {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
type: 'test',
|
|
332
|
+
command: `${scripts.packageManager} run ${scripts.test}`,
|
|
333
|
+
output: 'node_modules not found. Run npm install first.',
|
|
334
|
+
errors: [{ severity: 'error', message: 'node_modules not found. Run npm install first.' }],
|
|
335
|
+
duration: 0,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
309
338
|
command = scripts.packageManager;
|
|
310
339
|
args = ['run', scripts.test];
|
|
311
340
|
}
|
|
@@ -314,7 +343,7 @@ export function runTestVerification(projectRoot, timeout = 120000) {
|
|
|
314
343
|
/**
|
|
315
344
|
* Run TypeScript type checking
|
|
316
345
|
*/
|
|
317
|
-
export function runTypecheckVerification(projectRoot, timeout = 60000) {
|
|
346
|
+
export async function runTypecheckVerification(projectRoot, timeout = 60000) {
|
|
318
347
|
const scripts = detectProjectScripts(projectRoot);
|
|
319
348
|
if (!scripts.typecheck) {
|
|
320
349
|
return null;
|
|
@@ -322,8 +351,15 @@ export function runTypecheckVerification(projectRoot, timeout = 60000) {
|
|
|
322
351
|
let command;
|
|
323
352
|
let args;
|
|
324
353
|
if (scripts.typecheck === '__tsc_direct__') {
|
|
325
|
-
|
|
326
|
-
|
|
354
|
+
const localTsc = join(projectRoot, 'node_modules', '.bin', 'tsc');
|
|
355
|
+
if (existsSync(localTsc)) {
|
|
356
|
+
command = localTsc;
|
|
357
|
+
args = ['--noEmit'];
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
command = 'npx';
|
|
361
|
+
args = ['tsc', '--noEmit'];
|
|
362
|
+
}
|
|
327
363
|
}
|
|
328
364
|
else if (scripts.typecheck === '__php_lint__') {
|
|
329
365
|
// PHP syntax check on all PHP files
|
|
@@ -339,7 +375,7 @@ export function runTypecheckVerification(projectRoot, timeout = 60000) {
|
|
|
339
375
|
/**
|
|
340
376
|
* Run lint verification
|
|
341
377
|
*/
|
|
342
|
-
export function runLintVerification(projectRoot, timeout = 60000) {
|
|
378
|
+
export async function runLintVerification(projectRoot, timeout = 60000) {
|
|
343
379
|
const scripts = detectProjectScripts(projectRoot);
|
|
344
380
|
if (!scripts.lint) {
|
|
345
381
|
return null;
|
|
@@ -351,30 +387,30 @@ export function runLintVerification(projectRoot, timeout = 60000) {
|
|
|
351
387
|
/**
|
|
352
388
|
* Run all verifications
|
|
353
389
|
*/
|
|
354
|
-
export function runAllVerifications(projectRoot, options = {}) {
|
|
390
|
+
export async function runAllVerifications(projectRoot, options = {}) {
|
|
355
391
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
356
392
|
const results = [];
|
|
357
393
|
// Run typecheck first (fastest feedback)
|
|
358
394
|
if (opts.runTypecheck) {
|
|
359
|
-
const result = runTypecheckVerification(projectRoot, opts.timeout);
|
|
395
|
+
const result = await runTypecheckVerification(projectRoot, opts.timeout);
|
|
360
396
|
if (result)
|
|
361
397
|
results.push(result);
|
|
362
398
|
}
|
|
363
399
|
// Run build
|
|
364
400
|
if (opts.runBuild) {
|
|
365
|
-
const result = runBuildVerification(projectRoot, opts.timeout);
|
|
401
|
+
const result = await runBuildVerification(projectRoot, opts.timeout);
|
|
366
402
|
if (result)
|
|
367
403
|
results.push(result);
|
|
368
404
|
}
|
|
369
405
|
// Run lint
|
|
370
406
|
if (opts.runLint) {
|
|
371
|
-
const result = runLintVerification(projectRoot, opts.timeout);
|
|
407
|
+
const result = await runLintVerification(projectRoot, opts.timeout);
|
|
372
408
|
if (result)
|
|
373
409
|
results.push(result);
|
|
374
410
|
}
|
|
375
411
|
// Run tests last (slowest)
|
|
376
412
|
if (opts.runTest) {
|
|
377
|
-
const result = runTestVerification(projectRoot, opts.timeout);
|
|
413
|
+
const result = await runTestVerification(projectRoot, opts.timeout);
|
|
378
414
|
if (result)
|
|
379
415
|
results.push(result);
|
|
380
416
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.68",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|