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.
Files changed (36) hide show
  1. package/README.md +90 -4
  2. package/dist/api/index.js +64 -2
  3. package/dist/renderer/App.d.ts +5 -0
  4. package/dist/renderer/App.js +164 -227
  5. package/dist/renderer/components/Export.d.ts +22 -0
  6. package/dist/renderer/components/Export.js +64 -0
  7. package/dist/renderer/components/Help.js +5 -1
  8. package/dist/renderer/components/Logout.d.ts +29 -0
  9. package/dist/renderer/components/Logout.js +91 -0
  10. package/dist/renderer/components/Search.d.ts +30 -0
  11. package/dist/renderer/components/Search.js +83 -0
  12. package/dist/renderer/components/Settings.js +20 -0
  13. package/dist/renderer/components/Status.d.ts +6 -0
  14. package/dist/renderer/components/Status.js +20 -1
  15. package/dist/renderer/main.js +316 -141
  16. package/dist/utils/agent.d.ts +5 -0
  17. package/dist/utils/agent.js +238 -3
  18. package/dist/utils/agent.test.d.ts +1 -0
  19. package/dist/utils/agent.test.js +250 -0
  20. package/dist/utils/diffPreview.js +104 -35
  21. package/dist/utils/gitignore.d.ts +24 -0
  22. package/dist/utils/gitignore.js +161 -0
  23. package/dist/utils/gitignore.test.d.ts +1 -0
  24. package/dist/utils/gitignore.test.js +167 -0
  25. package/dist/utils/skills.d.ts +21 -0
  26. package/dist/utils/skills.js +51 -0
  27. package/dist/utils/smartContext.js +8 -0
  28. package/dist/utils/smartContext.test.d.ts +1 -0
  29. package/dist/utils/smartContext.test.js +382 -0
  30. package/dist/utils/tokenTracker.d.ts +52 -0
  31. package/dist/utils/tokenTracker.js +86 -0
  32. package/dist/utils/tools.d.ts +16 -0
  33. package/dist/utils/tools.js +146 -19
  34. package/dist/utils/tools.test.d.ts +1 -0
  35. package/dist/utils/tools.test.js +664 -0
  36. package/package.json +1 -1
@@ -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. Use for targeted changes.',
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. Returns matching files and lines.',
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, try to extract text
888
+ // If it looks like HTML, convert to readable text
821
889
  if (content.includes('<html') || content.includes('<!DOCTYPE')) {
822
- // Simple HTML to text - remove script/style and tags
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
- // Skip common directories
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 relativePath = relative(projectRoot, join(dir, entry.name));
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(join(dir, entry.name), projectRoot, true, prefix + ' ');
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(/&amp;/g, '&')
985
+ .replace(/&lt;/g, '<')
986
+ .replace(/&gt;/g, '>')
987
+ .replace(/&quot;/g, '"')
988
+ .replace(/&#39;/g, "'")
989
+ .replace(/&nbsp;/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 {};