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/utils/tools.js
CHANGED
|
@@ -11,6 +11,7 @@ const debug = (...args) => {
|
|
|
11
11
|
import { join, dirname, relative, resolve, isAbsolute } from 'path';
|
|
12
12
|
import { executeCommand } from './shell.js';
|
|
13
13
|
import { recordWrite, recordEdit, recordDelete, recordMkdir, recordCommand } from './history.js';
|
|
14
|
+
import { loadIgnoreRules, isIgnored } from './gitignore.js';
|
|
14
15
|
// Tool definitions for system prompt
|
|
15
16
|
export const AGENT_TOOLS = {
|
|
16
17
|
read_file: {
|
|
@@ -30,10 +31,10 @@ export const AGENT_TOOLS = {
|
|
|
30
31
|
},
|
|
31
32
|
edit_file: {
|
|
32
33
|
name: 'edit_file',
|
|
33
|
-
description: 'Edit an existing file by replacing specific text.
|
|
34
|
+
description: 'Edit an existing file by replacing specific text. The old_text must match exactly ONE location in the file. If it matches multiple locations, include more surrounding context to make it unique.',
|
|
34
35
|
parameters: {
|
|
35
36
|
path: { type: 'string', description: 'Path to the file relative to project root', required: true },
|
|
36
|
-
old_text: { type: 'string', description: 'The exact text to find and replace', required: true },
|
|
37
|
+
old_text: { type: 'string', description: 'The exact text to find and replace. Must be unique in the file - include enough context lines.', required: true },
|
|
37
38
|
new_text: { type: 'string', description: 'The new text to replace with', required: true },
|
|
38
39
|
},
|
|
39
40
|
},
|
|
@@ -69,12 +70,20 @@ export const AGENT_TOOLS = {
|
|
|
69
70
|
},
|
|
70
71
|
search_code: {
|
|
71
72
|
name: 'search_code',
|
|
72
|
-
description: 'Search for a text pattern in the codebase.
|
|
73
|
+
description: 'Search for a text pattern in the codebase. Searches across common file types: TypeScript, JavaScript, JSON, Markdown, CSS, HTML, Python, Go, Rust, Ruby, Kotlin, Swift, PHP, Java, C#, C/C++, Vue, Svelte, YAML, TOML, Shell, SQL, XML, SCSS, LESS.',
|
|
73
74
|
parameters: {
|
|
74
75
|
pattern: { type: 'string', description: 'Text or regex pattern to search for', required: true },
|
|
75
76
|
path: { type: 'string', description: 'Path to search in (default: entire project)', required: false },
|
|
76
77
|
},
|
|
77
78
|
},
|
|
79
|
+
find_files: {
|
|
80
|
+
name: 'find_files',
|
|
81
|
+
description: 'Find files matching a glob pattern. Use to find files by name or extension (e.g., "**/*.test.ts", "src/**/*.css", "*.json").',
|
|
82
|
+
parameters: {
|
|
83
|
+
pattern: { type: 'string', description: 'Glob pattern to match (e.g., "**/*.test.ts", "src/**/*.css")', required: true },
|
|
84
|
+
path: { type: 'string', description: 'Directory to search in relative to project root (default: ".")', required: false },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
78
87
|
fetch_url: {
|
|
79
88
|
name: 'fetch_url',
|
|
80
89
|
description: 'Fetch content from a URL (documentation, APIs, web pages). Returns text content.',
|
|
@@ -201,6 +210,8 @@ function normalizeToolName(name) {
|
|
|
201
210
|
'search_code': 'search_code',
|
|
202
211
|
'createdirectory': 'create_directory',
|
|
203
212
|
'create_directory': 'create_directory',
|
|
213
|
+
'findfiles': 'find_files',
|
|
214
|
+
'find_files': 'find_files',
|
|
204
215
|
'fetchurl': 'fetch_url',
|
|
205
216
|
'fetch_url': 'fetch_url',
|
|
206
217
|
};
|
|
@@ -402,6 +413,8 @@ export function parseToolCalls(response) {
|
|
|
402
413
|
'list_files': 'list_files',
|
|
403
414
|
'searchcode': 'search_code',
|
|
404
415
|
'search_code': 'search_code',
|
|
416
|
+
'findfiles': 'find_files',
|
|
417
|
+
'find_files': 'find_files',
|
|
405
418
|
};
|
|
406
419
|
const actualToolName = toolNameMap[toolName] || toolName;
|
|
407
420
|
try {
|
|
@@ -436,6 +449,7 @@ export function parseToolCalls(response) {
|
|
|
436
449
|
'deletefile': 'delete_file',
|
|
437
450
|
'listfiles': 'list_files',
|
|
438
451
|
'searchcode': 'search_code',
|
|
452
|
+
'findfiles': 'find_files',
|
|
439
453
|
};
|
|
440
454
|
const actualToolName = toolNameMap[toolName] || toolName;
|
|
441
455
|
// Check if we already have this tool call
|
|
@@ -671,6 +685,16 @@ export function executeTool(toolCall, projectRoot) {
|
|
|
671
685
|
if (!content.includes(oldText)) {
|
|
672
686
|
return { success: false, output: '', error: `Text not found in file. Make sure old_text matches exactly.`, tool, parameters };
|
|
673
687
|
}
|
|
688
|
+
// Count occurrences to prevent ambiguous replacements
|
|
689
|
+
let matchCount = 0;
|
|
690
|
+
let searchPos = 0;
|
|
691
|
+
while ((searchPos = content.indexOf(oldText, searchPos)) !== -1) {
|
|
692
|
+
matchCount++;
|
|
693
|
+
searchPos += oldText.length;
|
|
694
|
+
}
|
|
695
|
+
if (matchCount > 1) {
|
|
696
|
+
return { success: false, output: '', error: `old_text matches ${matchCount} locations in the file. Provide more surrounding context to make it unique (only 1 match allowed).`, tool, parameters };
|
|
697
|
+
}
|
|
674
698
|
// Record for undo
|
|
675
699
|
recordEdit(validation.absolutePath);
|
|
676
700
|
const newContent = content.replace(oldText, newText);
|
|
@@ -773,7 +797,7 @@ export function executeTool(toolCall, projectRoot) {
|
|
|
773
797
|
return { success: false, output: '', error: validation.error, tool, parameters };
|
|
774
798
|
}
|
|
775
799
|
// Use grep for search
|
|
776
|
-
const result = executeCommand('grep', ['-rn', '--include=*.{ts,tsx,js,jsx,json,md,css,html,py,go,rs}', pattern, validation.absolutePath], {
|
|
800
|
+
const result = executeCommand('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], {
|
|
777
801
|
cwd: projectRoot,
|
|
778
802
|
projectRoot,
|
|
779
803
|
timeout: 30000,
|
|
@@ -790,6 +814,50 @@ export function executeTool(toolCall, projectRoot) {
|
|
|
790
814
|
return { success: false, output: '', error: result.stderr || 'Search failed', tool, parameters };
|
|
791
815
|
}
|
|
792
816
|
}
|
|
817
|
+
case 'find_files': {
|
|
818
|
+
const pattern = parameters.pattern;
|
|
819
|
+
const searchPath = parameters.path || '.';
|
|
820
|
+
if (!pattern) {
|
|
821
|
+
return { success: false, output: '', error: 'Missing required parameter: pattern', tool, parameters };
|
|
822
|
+
}
|
|
823
|
+
const validation = validatePath(searchPath, projectRoot);
|
|
824
|
+
if (!validation.valid) {
|
|
825
|
+
return { success: false, output: '', error: validation.error, tool, parameters };
|
|
826
|
+
}
|
|
827
|
+
// Use find with -name or -path for glob matching
|
|
828
|
+
// Convert glob pattern to find-compatible format
|
|
829
|
+
const findArgs = [validation.absolutePath];
|
|
830
|
+
// Ignore common directories
|
|
831
|
+
findArgs.push('(', '-name', 'node_modules', '-o', '-name', '.git', '-o', '-name', '.codeep', '-o', '-name', 'dist', '-o', '-name', 'build', '-o', '-name', '.next', ')', '-prune', '-o');
|
|
832
|
+
if (pattern.includes('/')) {
|
|
833
|
+
// Path-based pattern: use -path
|
|
834
|
+
findArgs.push('-path', `*/${pattern}`, '-print');
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
// Name-based pattern: use -name
|
|
838
|
+
findArgs.push('-name', pattern, '-print');
|
|
839
|
+
}
|
|
840
|
+
const result = executeCommand('find', findArgs, {
|
|
841
|
+
cwd: projectRoot,
|
|
842
|
+
projectRoot,
|
|
843
|
+
timeout: 15000,
|
|
844
|
+
});
|
|
845
|
+
if (result.exitCode === 0 || result.stdout) {
|
|
846
|
+
const files = result.stdout.split('\n').filter(Boolean);
|
|
847
|
+
// Make paths relative to project root
|
|
848
|
+
const relativePaths = files.map(f => {
|
|
849
|
+
const rel = relative(projectRoot, f);
|
|
850
|
+
return rel || f;
|
|
851
|
+
}).slice(0, 100); // Limit to 100 results
|
|
852
|
+
if (relativePaths.length === 0) {
|
|
853
|
+
return { success: true, output: `No files matching "${pattern}"`, tool, parameters };
|
|
854
|
+
}
|
|
855
|
+
return { success: true, output: `Found ${relativePaths.length} file(s):\n${relativePaths.join('\n')}`, tool, parameters };
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
return { success: false, output: '', error: result.stderr || 'Find failed', tool, parameters };
|
|
859
|
+
}
|
|
860
|
+
}
|
|
793
861
|
case 'fetch_url': {
|
|
794
862
|
const url = parameters.url;
|
|
795
863
|
if (!url) {
|
|
@@ -817,15 +885,9 @@ export function executeTool(toolCall, projectRoot) {
|
|
|
817
885
|
if (result.success) {
|
|
818
886
|
// Try to extract text content (strip HTML tags for basic display)
|
|
819
887
|
let content = result.stdout;
|
|
820
|
-
// If it looks like HTML,
|
|
888
|
+
// If it looks like HTML, convert to readable text
|
|
821
889
|
if (content.includes('<html') || content.includes('<!DOCTYPE')) {
|
|
822
|
-
|
|
823
|
-
content = content
|
|
824
|
-
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
825
|
-
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
826
|
-
.replace(/<[^>]+>/g, ' ')
|
|
827
|
-
.replace(/\s+/g, ' ')
|
|
828
|
-
.trim();
|
|
890
|
+
content = htmlToText(content);
|
|
829
891
|
}
|
|
830
892
|
// Limit output
|
|
831
893
|
if (content.length > 10000) {
|
|
@@ -849,19 +911,19 @@ export function executeTool(toolCall, projectRoot) {
|
|
|
849
911
|
/**
|
|
850
912
|
* List directory contents
|
|
851
913
|
*/
|
|
852
|
-
function listDirectory(dir, projectRoot, recursive, prefix = '') {
|
|
914
|
+
function listDirectory(dir, projectRoot, recursive, prefix = '', ignoreRules) {
|
|
853
915
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
854
916
|
const files = [];
|
|
855
|
-
|
|
856
|
-
const skipDirs = new Set(['node_modules', '.git', 'dist', 'build', '.next', '__pycache__', '.venv', 'venv']);
|
|
917
|
+
const rules = ignoreRules || loadIgnoreRules(projectRoot);
|
|
857
918
|
for (const entry of entries) {
|
|
858
|
-
const
|
|
919
|
+
const fullPath = join(dir, entry.name);
|
|
920
|
+
// Skip ignored paths
|
|
921
|
+
if (isIgnored(fullPath, rules))
|
|
922
|
+
continue;
|
|
859
923
|
if (entry.isDirectory()) {
|
|
860
|
-
if (skipDirs.has(entry.name))
|
|
861
|
-
continue;
|
|
862
924
|
files.push(`${prefix}${entry.name}/`);
|
|
863
925
|
if (recursive) {
|
|
864
|
-
const subFiles = listDirectory(
|
|
926
|
+
const subFiles = listDirectory(fullPath, projectRoot, true, prefix + ' ', rules);
|
|
865
927
|
files.push(...subFiles);
|
|
866
928
|
}
|
|
867
929
|
}
|
|
@@ -871,6 +933,70 @@ function listDirectory(dir, projectRoot, recursive, prefix = '') {
|
|
|
871
933
|
}
|
|
872
934
|
return files;
|
|
873
935
|
}
|
|
936
|
+
/**
|
|
937
|
+
* Convert HTML to readable plain text, preserving structure.
|
|
938
|
+
*/
|
|
939
|
+
function htmlToText(html) {
|
|
940
|
+
// Remove invisible elements
|
|
941
|
+
let text = html
|
|
942
|
+
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
943
|
+
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
944
|
+
.replace(/<noscript[^>]*>[\s\S]*?<\/noscript>/gi, '')
|
|
945
|
+
.replace(/<svg[^>]*>[\s\S]*?<\/svg>/gi, '')
|
|
946
|
+
.replace(/<!--[\s\S]*?-->/g, '');
|
|
947
|
+
// Try to extract <main>, <article>, or <body> content for less noise
|
|
948
|
+
const mainMatch = text.match(/<main[^>]*>([\s\S]*?)<\/main>/i);
|
|
949
|
+
const articleMatch = text.match(/<article[^>]*>([\s\S]*?)<\/article>/i);
|
|
950
|
+
const bodyMatch = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
951
|
+
text = mainMatch?.[1] || articleMatch?.[1] || bodyMatch?.[1] || text;
|
|
952
|
+
// Headings → prefix with # markers + newlines
|
|
953
|
+
text = text.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, '\n\n# $1\n\n');
|
|
954
|
+
text = text.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, '\n\n## $1\n\n');
|
|
955
|
+
text = text.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, '\n\n### $1\n\n');
|
|
956
|
+
text = text.replace(/<h[4-6][^>]*>([\s\S]*?)<\/h[4-6]>/gi, '\n\n#### $1\n\n');
|
|
957
|
+
// Links → [text](href)
|
|
958
|
+
text = text.replace(/<a[^>]+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)');
|
|
959
|
+
// Code blocks
|
|
960
|
+
text = text.replace(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/gi, '\n```\n$1\n```\n');
|
|
961
|
+
text = text.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, '\n```\n$1\n```\n');
|
|
962
|
+
text = text.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, '`$1`');
|
|
963
|
+
// Lists
|
|
964
|
+
text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '\n- $1');
|
|
965
|
+
text = text.replace(/<\/[uo]l>/gi, '\n');
|
|
966
|
+
text = text.replace(/<[uo]l[^>]*>/gi, '\n');
|
|
967
|
+
// Block elements → newlines
|
|
968
|
+
text = text.replace(/<\/p>/gi, '\n\n');
|
|
969
|
+
text = text.replace(/<br\s*\/?>/gi, '\n');
|
|
970
|
+
text = text.replace(/<\/div>/gi, '\n');
|
|
971
|
+
text = text.replace(/<\/tr>/gi, '\n');
|
|
972
|
+
text = text.replace(/<\/th>/gi, '\t');
|
|
973
|
+
text = text.replace(/<\/td>/gi, '\t');
|
|
974
|
+
text = text.replace(/<hr[^>]*>/gi, '\n---\n');
|
|
975
|
+
text = text.replace(/<\/blockquote>/gi, '\n');
|
|
976
|
+
text = text.replace(/<blockquote[^>]*>/gi, '\n> ');
|
|
977
|
+
// Bold/italic
|
|
978
|
+
text = text.replace(/<(strong|b)[^>]*>([\s\S]*?)<\/\1>/gi, '**$2**');
|
|
979
|
+
text = text.replace(/<(em|i)[^>]*>([\s\S]*?)<\/\1>/gi, '*$2*');
|
|
980
|
+
// Strip remaining tags
|
|
981
|
+
text = text.replace(/<[^>]+>/g, '');
|
|
982
|
+
// Decode common HTML entities
|
|
983
|
+
text = text
|
|
984
|
+
.replace(/&/g, '&')
|
|
985
|
+
.replace(/</g, '<')
|
|
986
|
+
.replace(/>/g, '>')
|
|
987
|
+
.replace(/"/g, '"')
|
|
988
|
+
.replace(/'/g, "'")
|
|
989
|
+
.replace(/ /g, ' ')
|
|
990
|
+
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code)))
|
|
991
|
+
.replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCharCode(parseInt(code, 16)));
|
|
992
|
+
// Clean up whitespace
|
|
993
|
+
text = text
|
|
994
|
+
.replace(/[ \t]+/g, ' ') // Collapse horizontal whitespace
|
|
995
|
+
.replace(/ *\n/g, '\n') // Trim trailing spaces on lines
|
|
996
|
+
.replace(/\n{3,}/g, '\n\n') // Max 2 consecutive newlines
|
|
997
|
+
.trim();
|
|
998
|
+
return text;
|
|
999
|
+
}
|
|
874
1000
|
/**
|
|
875
1001
|
* Create action log from tool result
|
|
876
1002
|
*/
|
|
@@ -886,6 +1012,7 @@ export function createActionLog(toolCall, result) {
|
|
|
886
1012
|
search_code: 'search',
|
|
887
1013
|
list_files: 'list',
|
|
888
1014
|
create_directory: 'mkdir',
|
|
1015
|
+
find_files: 'search',
|
|
889
1016
|
fetch_url: 'fetch',
|
|
890
1017
|
};
|
|
891
1018
|
const target = toolCall.parameters.path ||
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|