codeep 1.1.36 → 1.2.1
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 +90 -4
- package/dist/api/index.js +64 -2
- package/dist/renderer/App.d.ts +5 -0
- package/dist/renderer/App.js +164 -227
- package/dist/renderer/components/Export.d.ts +22 -0
- package/dist/renderer/components/Export.js +64 -0
- package/dist/renderer/components/Help.js +5 -1
- package/dist/renderer/components/Logout.d.ts +29 -0
- package/dist/renderer/components/Logout.js +91 -0
- package/dist/renderer/components/Search.d.ts +30 -0
- package/dist/renderer/components/Search.js +83 -0
- package/dist/renderer/components/Settings.js +20 -0
- package/dist/renderer/components/Status.d.ts +6 -0
- package/dist/renderer/components/Status.js +20 -1
- package/dist/renderer/main.js +316 -141
- package/dist/utils/agent.d.ts +5 -0
- package/dist/utils/agent.js +238 -3
- package/dist/utils/agent.test.d.ts +1 -0
- package/dist/utils/agent.test.js +250 -0
- package/dist/utils/diffPreview.js +104 -35
- package/dist/utils/gitignore.d.ts +24 -0
- package/dist/utils/gitignore.js +161 -0
- package/dist/utils/gitignore.test.d.ts +1 -0
- package/dist/utils/gitignore.test.js +167 -0
- package/dist/utils/skills.d.ts +21 -0
- package/dist/utils/skills.js +51 -0
- package/dist/utils/smartContext.js +8 -0
- package/dist/utils/smartContext.test.d.ts +1 -0
- package/dist/utils/smartContext.test.js +382 -0
- package/dist/utils/tokenTracker.d.ts +52 -0
- package/dist/utils/tokenTracker.js +86 -0
- package/dist/utils/tools.d.ts +16 -0
- package/dist/utils/tools.js +146 -19
- package/dist/utils/tools.test.d.ts +1 -0
- package/dist/utils/tools.test.js +664 -0
- package/package.json +1 -1
package/dist/renderer/main.js
CHANGED
|
@@ -15,12 +15,15 @@ import { config, loadApiKey, loadAllApiKeys, getCurrentProvider, getModelsForCur
|
|
|
15
15
|
import { isProjectDirectory, getProjectContext } from '../utils/project.js';
|
|
16
16
|
import { getCurrentVersion } from '../utils/update.js';
|
|
17
17
|
import { getProviderList } from '../config/providers.js';
|
|
18
|
+
import { getSessionStats } from '../utils/tokenTracker.js';
|
|
18
19
|
// State
|
|
19
20
|
let projectPath = process.cwd();
|
|
20
21
|
let projectContext = null;
|
|
21
22
|
let hasWriteAccess = false;
|
|
22
23
|
let sessionId = getCurrentSessionId();
|
|
23
24
|
let app;
|
|
25
|
+
// Added file context (/add, /drop)
|
|
26
|
+
const addedFiles = new Map();
|
|
24
27
|
/**
|
|
25
28
|
* Get current status
|
|
26
29
|
*/
|
|
@@ -28,6 +31,7 @@ function getStatus() {
|
|
|
28
31
|
const provider = getCurrentProvider();
|
|
29
32
|
const providers = getProviderList();
|
|
30
33
|
const providerInfo = providers.find(p => p.id === provider.id);
|
|
34
|
+
const stats = getSessionStats();
|
|
31
35
|
return {
|
|
32
36
|
version: getCurrentVersion(),
|
|
33
37
|
provider: providerInfo?.name || 'Unknown',
|
|
@@ -37,14 +41,29 @@ function getStatus() {
|
|
|
37
41
|
hasWriteAccess,
|
|
38
42
|
sessionId,
|
|
39
43
|
messageCount: 0, // Will be updated
|
|
44
|
+
tokenStats: {
|
|
45
|
+
totalTokens: stats.totalTokens,
|
|
46
|
+
promptTokens: stats.totalPromptTokens,
|
|
47
|
+
completionTokens: stats.totalCompletionTokens,
|
|
48
|
+
requestCount: stats.requestCount,
|
|
49
|
+
},
|
|
40
50
|
};
|
|
41
51
|
}
|
|
42
52
|
// Agent state
|
|
43
53
|
let isAgentRunning = false;
|
|
44
54
|
let agentAbortController = null;
|
|
45
55
|
/**
|
|
46
|
-
*
|
|
56
|
+
* Format added files as context to prepend to user messages
|
|
47
57
|
*/
|
|
58
|
+
function formatAddedFilesContext() {
|
|
59
|
+
if (addedFiles.size === 0)
|
|
60
|
+
return '';
|
|
61
|
+
const parts = ['[Attached files]'];
|
|
62
|
+
for (const [, file] of addedFiles) {
|
|
63
|
+
parts.push(`\nFile: ${file.relativePath}\n\`\`\`\n${file.content}\n\`\`\``);
|
|
64
|
+
}
|
|
65
|
+
return parts.join('\n') + '\n\n';
|
|
66
|
+
}
|
|
48
67
|
async function handleSubmit(message) {
|
|
49
68
|
// Check if we're waiting for interactive mode answers
|
|
50
69
|
if (pendingInteractiveContext) {
|
|
@@ -112,7 +131,10 @@ async function handleSubmit(message) {
|
|
|
112
131
|
app.startStreaming();
|
|
113
132
|
// Get conversation history for context
|
|
114
133
|
const history = app.getChatHistory();
|
|
115
|
-
|
|
134
|
+
// Prepend added file context if any
|
|
135
|
+
const fileContext = formatAddedFilesContext();
|
|
136
|
+
const enrichedMessage = fileContext ? fileContext + message : message;
|
|
137
|
+
const response = await chat(enrichedMessage, history, (chunk) => {
|
|
116
138
|
app.addStreamChunk(chunk);
|
|
117
139
|
}, undefined, projectContext, undefined);
|
|
118
140
|
app.endStreaming();
|
|
@@ -290,7 +312,10 @@ async function executeAgentTask(task, dryRun = false) {
|
|
|
290
312
|
// Store context in local variable for TypeScript narrowing
|
|
291
313
|
const context = projectContext;
|
|
292
314
|
try {
|
|
293
|
-
|
|
315
|
+
// Enrich task with added file context if any
|
|
316
|
+
const fileContext = formatAddedFilesContext();
|
|
317
|
+
const enrichedTask = fileContext ? fileContext + task : task;
|
|
318
|
+
const result = await runAgent(enrichedTask, context, {
|
|
294
319
|
dryRun,
|
|
295
320
|
onIteration: (iteration) => {
|
|
296
321
|
app.updateAgentProgress(iteration);
|
|
@@ -312,21 +337,62 @@ async function executeAgentTask(task, dryRun = false) {
|
|
|
312
337
|
// Update agent thinking
|
|
313
338
|
const shortTarget = target.length > 50 ? '...' + target.slice(-47) : target;
|
|
314
339
|
app.setAgentThinking(`${actionType}: ${shortTarget}`);
|
|
315
|
-
// Add chat message for write/edit operations
|
|
340
|
+
// Add chat message with diff preview for write/edit operations
|
|
316
341
|
if (actionType === 'write' && tool.parameters.content) {
|
|
317
342
|
const filePath = tool.parameters.path;
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
343
|
+
try {
|
|
344
|
+
const { createFileDiff, formatDiffForDisplay } = require('../utils/diffPreview');
|
|
345
|
+
const diff = createFileDiff(filePath, tool.parameters.content, context.root);
|
|
346
|
+
const diffText = formatDiffForDisplay(diff);
|
|
347
|
+
const additions = diff.hunks.reduce((sum, h) => sum + h.lines.filter((l) => l.type === 'add').length, 0);
|
|
348
|
+
const deletions = diff.hunks.reduce((sum, h) => sum + h.lines.filter((l) => l.type === 'remove').length, 0);
|
|
349
|
+
app.addMessage({
|
|
350
|
+
role: 'system',
|
|
351
|
+
content: `**${diff.type === 'create' ? 'Create' : 'Write'}** \`${filePath}\` (+${additions} -${deletions})\n\n\`\`\`diff\n${diffText}\n\`\`\``,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
const ext = filePath.split('.').pop() || '';
|
|
356
|
+
app.addMessage({
|
|
357
|
+
role: 'system',
|
|
358
|
+
content: `**Write** \`${filePath}\`\n\n\`\`\`${ext}\n${tool.parameters.content}\n\`\`\``,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
323
361
|
}
|
|
324
362
|
else if (actionType === 'edit' && tool.parameters.new_text) {
|
|
325
363
|
const filePath = tool.parameters.path;
|
|
326
|
-
|
|
364
|
+
try {
|
|
365
|
+
const { createEditDiff, formatDiffForDisplay } = require('../utils/diffPreview');
|
|
366
|
+
const diff = createEditDiff(filePath, tool.parameters.old_text, tool.parameters.new_text, context.root);
|
|
367
|
+
if (diff) {
|
|
368
|
+
const additions = diff.hunks.reduce((sum, h) => sum + h.lines.filter((l) => l.type === 'add').length, 0);
|
|
369
|
+
const deletions = diff.hunks.reduce((sum, h) => sum + h.lines.filter((l) => l.type === 'remove').length, 0);
|
|
370
|
+
app.addMessage({
|
|
371
|
+
role: 'system',
|
|
372
|
+
content: `**Edit** \`${filePath}\` (+${additions} -${deletions})\n\n\`\`\`diff\n${formatDiffForDisplay(diff)}\n\`\`\``,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
const ext = filePath.split('.').pop() || '';
|
|
377
|
+
app.addMessage({
|
|
378
|
+
role: 'system',
|
|
379
|
+
content: `**Edit** \`${filePath}\`\n\n\`\`\`${ext}\n${tool.parameters.new_text}\n\`\`\``,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
const ext = filePath.split('.').pop() || '';
|
|
385
|
+
app.addMessage({
|
|
386
|
+
role: 'system',
|
|
387
|
+
content: `**Edit** \`${filePath}\`\n\n\`\`\`${ext}\n${tool.parameters.new_text}\n\`\`\``,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else if (actionType === 'delete') {
|
|
392
|
+
const filePath = tool.parameters.path;
|
|
327
393
|
app.addMessage({
|
|
328
394
|
role: 'system',
|
|
329
|
-
content: `**
|
|
395
|
+
content: `**Delete** \`${filePath}\``,
|
|
330
396
|
});
|
|
331
397
|
}
|
|
332
398
|
},
|
|
@@ -360,6 +426,34 @@ async function executeAgentTask(task, dryRun = false) {
|
|
|
360
426
|
const summary = result.finalResponse || `Completed ${result.actions.length} actions in ${result.iterations} steps.`;
|
|
361
427
|
app.addMessage({ role: 'assistant', content: summary });
|
|
362
428
|
app.notify(`Agent completed: ${result.actions.length} actions`);
|
|
429
|
+
// Auto-commit if enabled and there were file changes
|
|
430
|
+
if (!dryRun && config.get('agentAutoCommit') && result.actions.length > 0) {
|
|
431
|
+
try {
|
|
432
|
+
const { autoCommitAgentChanges, createBranchAndCommit } = await import('../utils/git.js');
|
|
433
|
+
const useBranch = config.get('agentAutoCommitBranch');
|
|
434
|
+
if (useBranch) {
|
|
435
|
+
const commitResult = createBranchAndCommit(task, result.actions, context.root);
|
|
436
|
+
if (commitResult.success) {
|
|
437
|
+
app.addMessage({ role: 'system', content: `Auto-committed on branch \`${commitResult.branch}\` (${commitResult.hash?.slice(0, 7)})` });
|
|
438
|
+
}
|
|
439
|
+
else if (commitResult.error !== 'No changes detected by git') {
|
|
440
|
+
app.addMessage({ role: 'system', content: `Auto-commit failed: ${commitResult.error}` });
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
const commitResult = autoCommitAgentChanges(task, result.actions, context.root);
|
|
445
|
+
if (commitResult.success) {
|
|
446
|
+
app.addMessage({ role: 'system', content: `Auto-committed: ${commitResult.hash?.slice(0, 7)}` });
|
|
447
|
+
}
|
|
448
|
+
else if (commitResult.error !== 'No changes detected by git') {
|
|
449
|
+
app.addMessage({ role: 'system', content: `Auto-commit failed: ${commitResult.error}` });
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
// Silently ignore commit errors
|
|
455
|
+
}
|
|
456
|
+
}
|
|
363
457
|
}
|
|
364
458
|
else if (result.aborted) {
|
|
365
459
|
app.addMessage({ role: 'assistant', content: 'Agent stopped by user.' });
|
|
@@ -383,6 +477,110 @@ async function executeAgentTask(task, dryRun = false) {
|
|
|
383
477
|
app.setAgentRunning(false);
|
|
384
478
|
}
|
|
385
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Run a skill by name or shortcut with the given args.
|
|
482
|
+
* Wires the skill execution engine to App's UI.
|
|
483
|
+
*/
|
|
484
|
+
async function runSkill(nameOrShortcut, args) {
|
|
485
|
+
const { findSkill, parseSkillArgs, executeSkill, trackSkillUsage } = await import('../utils/skills.js');
|
|
486
|
+
const skill = findSkill(nameOrShortcut);
|
|
487
|
+
if (!skill)
|
|
488
|
+
return false;
|
|
489
|
+
// Pre-flight checks
|
|
490
|
+
if (skill.requiresGit) {
|
|
491
|
+
const { getGitStatus } = await import('../utils/git.js');
|
|
492
|
+
if (!projectPath || !getGitStatus(projectPath).isRepo) {
|
|
493
|
+
app.notify('This skill requires a git repository');
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (skill.requiresWriteAccess && !hasWriteAccess) {
|
|
498
|
+
app.notify('This skill requires write access. Use /grant first.');
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
const params = parseSkillArgs(args.join(' '), skill);
|
|
502
|
+
app.addMessage({ role: 'user', content: `/${skill.name}${args.length ? ' ' + args.join(' ') : ''}` });
|
|
503
|
+
trackSkillUsage(skill.name);
|
|
504
|
+
const { execSync } = await import('child_process');
|
|
505
|
+
try {
|
|
506
|
+
const result = await executeSkill(skill, params, {
|
|
507
|
+
onCommand: async (cmd) => {
|
|
508
|
+
try {
|
|
509
|
+
const output = execSync(cmd, {
|
|
510
|
+
cwd: projectPath || process.cwd(),
|
|
511
|
+
encoding: 'utf-8',
|
|
512
|
+
timeout: 60000,
|
|
513
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
514
|
+
});
|
|
515
|
+
const result = output.trim();
|
|
516
|
+
if (result) {
|
|
517
|
+
app.addMessage({ role: 'system', content: `\`${cmd}\`\n\`\`\`\n${result}\n\`\`\`` });
|
|
518
|
+
}
|
|
519
|
+
return result;
|
|
520
|
+
}
|
|
521
|
+
catch (err) {
|
|
522
|
+
const error = err;
|
|
523
|
+
const stderr = (error.stderr || '').trim();
|
|
524
|
+
const stdout = (error.stdout || '').trim();
|
|
525
|
+
// Git commands output progress/info to stderr even on success
|
|
526
|
+
if (error.status === 0 || stderr.includes('up-to-date') || stderr.includes('up to date') || stderr.includes('Already up to date')) {
|
|
527
|
+
const result = stdout || stderr;
|
|
528
|
+
if (result) {
|
|
529
|
+
app.addMessage({ role: 'system', content: `\`${cmd}\`\n\`\`\`\n${result}\n\`\`\`` });
|
|
530
|
+
}
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
const errOutput = stderr || stdout;
|
|
534
|
+
if (errOutput) {
|
|
535
|
+
app.addMessage({ role: 'system', content: `\`${cmd}\` failed:\n\`\`\`\n${errOutput}\n\`\`\`` });
|
|
536
|
+
}
|
|
537
|
+
throw new Error(errOutput || error.message || 'Command failed');
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
onPrompt: (prompt) => {
|
|
541
|
+
return new Promise((resolve, reject) => {
|
|
542
|
+
handleSubmit(prompt).then(() => {
|
|
543
|
+
// The AI response will be displayed in chat.
|
|
544
|
+
// We resolve with an empty string since the response is already shown.
|
|
545
|
+
resolve('');
|
|
546
|
+
}).catch(reject);
|
|
547
|
+
});
|
|
548
|
+
},
|
|
549
|
+
onAgent: (task) => {
|
|
550
|
+
return new Promise((resolve, reject) => {
|
|
551
|
+
if (!projectContext) {
|
|
552
|
+
reject(new Error('Agent requires project context'));
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
runAgentTask(task).then(() => resolve('Agent completed')).catch(reject);
|
|
556
|
+
});
|
|
557
|
+
},
|
|
558
|
+
onConfirm: (message) => {
|
|
559
|
+
return new Promise((resolve) => {
|
|
560
|
+
app.showConfirm({
|
|
561
|
+
title: 'Confirm',
|
|
562
|
+
message: [message],
|
|
563
|
+
confirmLabel: 'Yes',
|
|
564
|
+
cancelLabel: 'No',
|
|
565
|
+
onConfirm: () => resolve(true),
|
|
566
|
+
onCancel: () => resolve(false),
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
},
|
|
570
|
+
onNotify: (message) => {
|
|
571
|
+
app.notify(message);
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
if (!result.success && result.output !== 'Cancelled by user') {
|
|
575
|
+
app.notify(`Skill failed: ${result.output}`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
catch (err) {
|
|
579
|
+
app.notify(`Skill error: ${err.message}`);
|
|
580
|
+
trackSkillUsage(skill.name, false);
|
|
581
|
+
}
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
386
584
|
/**
|
|
387
585
|
* Run a chain of commands sequentially
|
|
388
586
|
*/
|
|
@@ -546,28 +744,6 @@ function handleCommand(command, args) {
|
|
|
546
744
|
});
|
|
547
745
|
break;
|
|
548
746
|
}
|
|
549
|
-
case 'commit': {
|
|
550
|
-
if (!projectContext) {
|
|
551
|
-
app.notify('No project context');
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
import('../utils/git.js').then(({ getGitDiff, getGitStatus, suggestCommitMessage }) => {
|
|
555
|
-
const status = getGitStatus(projectPath);
|
|
556
|
-
if (!status.isRepo) {
|
|
557
|
-
app.notify('Not a git repository');
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
const diff = getGitDiff(true, projectPath);
|
|
561
|
-
if (!diff.success || !diff.diff) {
|
|
562
|
-
app.notify('No staged changes. Use git add first.');
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
const suggestion = suggestCommitMessage(diff.diff);
|
|
566
|
-
app.addMessage({ role: 'user', content: '/commit' });
|
|
567
|
-
handleSubmit(`Generate a commit message for these staged changes. Suggestion: "${suggestion}"\n\nDiff:\n\`\`\`diff\n${diff.diff.slice(0, 2000)}\n\`\`\``);
|
|
568
|
-
});
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
747
|
case 'undo': {
|
|
572
748
|
import('../utils/agent.js').then(({ undoLastAction }) => {
|
|
573
749
|
const result = undoLastAction();
|
|
@@ -966,6 +1142,81 @@ function handleCommand(command, args) {
|
|
|
966
1142
|
});
|
|
967
1143
|
break;
|
|
968
1144
|
}
|
|
1145
|
+
// File context commands
|
|
1146
|
+
case 'add': {
|
|
1147
|
+
if (!args.length) {
|
|
1148
|
+
if (addedFiles.size === 0) {
|
|
1149
|
+
app.notify('Usage: /add <file-path> [file2] ... | No files added');
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
const fileList = Array.from(addedFiles.values()).map(f => f.relativePath).join(', ');
|
|
1153
|
+
app.notify(`Added files (${addedFiles.size}): ${fileList}`);
|
|
1154
|
+
}
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
const path = require('path');
|
|
1158
|
+
const fs = require('fs');
|
|
1159
|
+
const root = projectContext?.root || projectPath;
|
|
1160
|
+
let added = 0;
|
|
1161
|
+
const errors = [];
|
|
1162
|
+
for (const filePath of args) {
|
|
1163
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(root, filePath);
|
|
1164
|
+
const relativePath = path.isAbsolute(filePath) ? path.relative(root, filePath) : filePath;
|
|
1165
|
+
try {
|
|
1166
|
+
const stat = fs.statSync(fullPath);
|
|
1167
|
+
if (!stat.isFile()) {
|
|
1168
|
+
errors.push(`${filePath}: not a file`);
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
if (stat.size > 100000) {
|
|
1172
|
+
errors.push(`${filePath}: too large (${Math.round(stat.size / 1024)}KB, max 100KB)`);
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
1176
|
+
addedFiles.set(fullPath, { relativePath, content });
|
|
1177
|
+
added++;
|
|
1178
|
+
}
|
|
1179
|
+
catch {
|
|
1180
|
+
errors.push(`${filePath}: file not found`);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (added > 0) {
|
|
1184
|
+
app.notify(`Added ${added} file(s) to context (${addedFiles.size} total)`);
|
|
1185
|
+
}
|
|
1186
|
+
if (errors.length > 0) {
|
|
1187
|
+
app.notify(errors.join(', '));
|
|
1188
|
+
}
|
|
1189
|
+
break;
|
|
1190
|
+
}
|
|
1191
|
+
case 'drop': {
|
|
1192
|
+
if (!args.length) {
|
|
1193
|
+
if (addedFiles.size === 0) {
|
|
1194
|
+
app.notify('No files in context');
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
const count = addedFiles.size;
|
|
1198
|
+
addedFiles.clear();
|
|
1199
|
+
app.notify(`Dropped all ${count} file(s) from context`);
|
|
1200
|
+
}
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
const path = require('path');
|
|
1204
|
+
const root = projectContext?.root || projectPath;
|
|
1205
|
+
let dropped = 0;
|
|
1206
|
+
for (const filePath of args) {
|
|
1207
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(root, filePath);
|
|
1208
|
+
if (addedFiles.delete(fullPath)) {
|
|
1209
|
+
dropped++;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
if (dropped > 0) {
|
|
1213
|
+
app.notify(`Dropped ${dropped} file(s) (${addedFiles.size} remaining)`);
|
|
1214
|
+
}
|
|
1215
|
+
else {
|
|
1216
|
+
app.notify('File not found in context. Use /add to see added files.');
|
|
1217
|
+
}
|
|
1218
|
+
break;
|
|
1219
|
+
}
|
|
969
1220
|
// Agent history and changes
|
|
970
1221
|
case 'history': {
|
|
971
1222
|
import('../utils/agent.js').then(({ getAgentHistory }) => {
|
|
@@ -1107,119 +1358,38 @@ function handleCommand(command, args) {
|
|
|
1107
1358
|
break;
|
|
1108
1359
|
}
|
|
1109
1360
|
// Skills shortcuts
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
case 't': {
|
|
1115
|
-
if (!projectContext) {
|
|
1116
|
-
app.notify('No project context');
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
app.addMessage({ role: 'user', content: '/test' });
|
|
1120
|
-
handleSubmit('Generate and run tests for the current project. Focus on untested code.');
|
|
1121
|
-
break;
|
|
1122
|
-
}
|
|
1123
|
-
case 'd': {
|
|
1124
|
-
if (!projectContext) {
|
|
1125
|
-
app.notify('No project context');
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
app.addMessage({ role: 'user', content: '/docs' });
|
|
1129
|
-
handleSubmit('Add documentation to the code. Focus on functions and classes that lack proper documentation.');
|
|
1130
|
-
break;
|
|
1131
|
-
}
|
|
1132
|
-
case 'r': {
|
|
1133
|
-
if (!projectContext) {
|
|
1134
|
-
app.notify('No project context');
|
|
1135
|
-
return;
|
|
1136
|
-
}
|
|
1137
|
-
app.addMessage({ role: 'user', content: '/refactor' });
|
|
1138
|
-
handleSubmit('Refactor the code to improve quality, readability, and maintainability.');
|
|
1139
|
-
break;
|
|
1140
|
-
}
|
|
1141
|
-
case 'f': {
|
|
1142
|
-
if (!projectContext) {
|
|
1143
|
-
app.notify('No project context');
|
|
1144
|
-
return;
|
|
1145
|
-
}
|
|
1146
|
-
app.addMessage({ role: 'user', content: '/fix' });
|
|
1147
|
-
handleSubmit('Debug and fix any issues in the current code. Look for bugs, errors, and potential problems.');
|
|
1148
|
-
break;
|
|
1149
|
-
}
|
|
1150
|
-
case 'e': {
|
|
1151
|
-
if (!args.length) {
|
|
1152
|
-
app.notify('Usage: /e <file or code to explain>');
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
app.addMessage({ role: 'user', content: `/explain ${args.join(' ')}` });
|
|
1156
|
-
handleSubmit(`Explain this code or concept: ${args.join(' ')}`);
|
|
1157
|
-
break;
|
|
1158
|
-
}
|
|
1159
|
-
case 'o': {
|
|
1160
|
-
if (!projectContext) {
|
|
1161
|
-
app.notify('No project context');
|
|
1162
|
-
return;
|
|
1163
|
-
}
|
|
1164
|
-
app.addMessage({ role: 'user', content: '/optimize' });
|
|
1165
|
-
handleSubmit('Optimize the code for better performance. Focus on efficiency and speed improvements.');
|
|
1166
|
-
break;
|
|
1167
|
-
}
|
|
1168
|
-
case 'b': {
|
|
1169
|
-
if (!projectContext) {
|
|
1170
|
-
app.notify('No project context');
|
|
1171
|
-
return;
|
|
1172
|
-
}
|
|
1173
|
-
app.addMessage({ role: 'user', content: '/debug' });
|
|
1174
|
-
handleSubmit('Help debug the current issue. Analyze the code and identify the root cause of problems.');
|
|
1175
|
-
break;
|
|
1176
|
-
}
|
|
1177
|
-
case 'p': {
|
|
1178
|
-
// Push shortcut
|
|
1179
|
-
import('child_process').then(({ execSync }) => {
|
|
1180
|
-
try {
|
|
1181
|
-
execSync('git push', { cwd: projectPath, encoding: 'utf-8' });
|
|
1182
|
-
app.notify('Pushed successfully');
|
|
1183
|
-
}
|
|
1184
|
-
catch (err) {
|
|
1185
|
-
app.notify(`Push failed: ${err.message}`);
|
|
1186
|
-
}
|
|
1187
|
-
});
|
|
1188
|
-
break;
|
|
1189
|
-
}
|
|
1190
|
-
// Full skill names
|
|
1361
|
+
// Skill shortcuts and full names — delegated to skill execution engine
|
|
1362
|
+
case 'c':
|
|
1363
|
+
case 'commit':
|
|
1364
|
+
case 't':
|
|
1191
1365
|
case 'test':
|
|
1366
|
+
case 'd':
|
|
1192
1367
|
case 'docs':
|
|
1368
|
+
case 'r':
|
|
1193
1369
|
case 'refactor':
|
|
1370
|
+
case 'f':
|
|
1194
1371
|
case 'fix':
|
|
1372
|
+
case 'e':
|
|
1195
1373
|
case 'explain':
|
|
1374
|
+
case 'o':
|
|
1196
1375
|
case 'optimize':
|
|
1197
|
-
case '
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
case '
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
case 'pull': {
|
|
1215
|
-
import('child_process').then(({ execSync }) => {
|
|
1216
|
-
try {
|
|
1217
|
-
execSync('git pull', { cwd: projectPath, encoding: 'utf-8' });
|
|
1218
|
-
app.notify('Pulled successfully');
|
|
1219
|
-
}
|
|
1220
|
-
catch (err) {
|
|
1221
|
-
app.notify(`Pull failed: ${err.message}`);
|
|
1222
|
-
}
|
|
1376
|
+
case 'b':
|
|
1377
|
+
case 'debug':
|
|
1378
|
+
case 'p':
|
|
1379
|
+
case 'push':
|
|
1380
|
+
case 'pull':
|
|
1381
|
+
case 'amend':
|
|
1382
|
+
case 'pr':
|
|
1383
|
+
case 'changelog':
|
|
1384
|
+
case 'branch':
|
|
1385
|
+
case 'stash':
|
|
1386
|
+
case 'unstash':
|
|
1387
|
+
case 'build':
|
|
1388
|
+
case 'deploy':
|
|
1389
|
+
case 'release':
|
|
1390
|
+
case 'publish': {
|
|
1391
|
+
runSkill(command, args).catch((err) => {
|
|
1392
|
+
app.notify(`Skill error: ${err.message}`);
|
|
1223
1393
|
});
|
|
1224
1394
|
break;
|
|
1225
1395
|
}
|
|
@@ -1322,7 +1492,12 @@ function handleCommand(command, args) {
|
|
|
1322
1492
|
break;
|
|
1323
1493
|
}
|
|
1324
1494
|
default:
|
|
1325
|
-
|
|
1495
|
+
// Try to run as a skill (handles custom skills and any built-in not in the switch)
|
|
1496
|
+
runSkill(command, args).then(handled => {
|
|
1497
|
+
if (!handled) {
|
|
1498
|
+
app.notify(`Unknown command: /${command}`);
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1326
1501
|
}
|
|
1327
1502
|
}
|
|
1328
1503
|
/**
|
package/dist/utils/agent.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ export interface AgentResult {
|
|
|
30
30
|
error?: string;
|
|
31
31
|
aborted?: boolean;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Load project rules from .codeep/rules.md or CODEEP.md
|
|
35
|
+
* Returns the rules content formatted for system prompt, or empty string if no rules found
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadProjectRules(projectRoot: string): string;
|
|
33
38
|
/**
|
|
34
39
|
* Run the agent loop
|
|
35
40
|
*/
|