ctxman 3.0.0 → 3.1.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 (224) hide show
  1. package/.ctxman/llm-profiles.json +1067 -39
  2. package/.ctxman/profiles/backend-team.json +13 -0
  3. package/.ctxman/profiles/default.json +12 -0
  4. package/.ctxman/profiles/devops-team.json +21 -0
  5. package/.ctxman/profiles/frontend-team.json +20 -0
  6. package/.ctxman/templates/bug-fix.json +17 -0
  7. package/.ctxman/templates/code-review.json +9 -0
  8. package/.ctxman/templates/documentation.json +9 -0
  9. package/.ctxman/templates/feature.json +9 -0
  10. package/.ctxman/templates/full-context.json +9 -0
  11. package/.ctxman/templates/refactoring.json +18 -0
  12. package/.ctxman/templates/testing.json +9 -0
  13. package/.ctxman/wizard-profiles/security-audit/profile.json +7 -1
  14. package/LICENSE +21 -0
  15. package/README.md +381 -136
  16. package/bin/cli.js +1880 -529
  17. package/bin/cm-ask.js +18 -67
  18. package/bin/cm-audit.js +121 -0
  19. package/bin/cm-count.js +151 -0
  20. package/bin/cm-gitingest.js +128 -115
  21. package/bin/cm-map.js +242 -0
  22. package/bin/cm-mcp.js +293 -0
  23. package/bin/cm-pack.js +405 -0
  24. package/bin/cm-policy.js +170 -0
  25. package/bin/cm-pr.js +248 -0
  26. package/bin/cm-search.js +178 -0
  27. package/bin/cm-update.js +128 -128
  28. package/bin/cm-why.js +197 -0
  29. package/bin/mcp-server.js +8 -24
  30. package/ctxman.js +237 -183
  31. package/examples/README.md +19 -4
  32. package/examples/github-actions/ctxman-check.yml +49 -0
  33. package/examples/wizard-profiles/security-audit/profile.json +7 -1
  34. package/index.d.ts +1422 -0
  35. package/index.js +55 -31
  36. package/lib/agents/pr-failure-agent.js +48 -45
  37. package/lib/analyzers/ai-suggester.js +451 -0
  38. package/lib/analyzers/dependency-scanner.js +505 -0
  39. package/lib/analyzers/duplicate-detector.js +264 -0
  40. package/lib/analyzers/go-method-analyzer.js +151 -110
  41. package/lib/analyzers/import-tracker.js +431 -0
  42. package/lib/analyzers/method-analyzer.js +1055 -434
  43. package/lib/analyzers/performance-dashboard.js +687 -0
  44. package/lib/analyzers/token-calculator.js +895 -504
  45. package/lib/analyzers/unused-exports-detector.js +336 -0
  46. package/lib/api/mcp/install.js +424 -0
  47. package/lib/api/mcp/prompts.js +281 -281
  48. package/lib/api/mcp/resources.js +300 -241
  49. package/lib/api/mcp/sandbox.js +89 -0
  50. package/lib/api/mcp/server.js +772 -312
  51. package/lib/api/mcp/start.js +125 -0
  52. package/lib/api/mcp/transports/HttpSseTransport.js +60 -52
  53. package/lib/api/rest/server.js +105 -28
  54. package/lib/cache/CacheManager.js +9 -17
  55. package/lib/core/Analyzer.js +16 -10
  56. package/lib/core/ContextBuilder.js +19 -28
  57. package/lib/core/Reporter.js +15 -14
  58. package/lib/core/Scanner.js +81 -12
  59. package/lib/count/index.js +100 -0
  60. package/lib/formatters/format-registry.js +394 -372
  61. package/lib/formatters/gitingest-formatter.js +656 -621
  62. package/lib/formatters/toon-formatter-v1.3.js +637 -632
  63. package/lib/formatters/toon-formatter.js +239 -240
  64. package/lib/integrations/git/BlameTracker.js +4 -4
  65. package/lib/integrations/git/DiffAnalyzer.js +38 -31
  66. package/lib/integrations/git/GitClient.js +71 -53
  67. package/lib/lsp/server.js +311 -0
  68. package/lib/map/graph.js +299 -0
  69. package/lib/map/imports.js +233 -0
  70. package/lib/map/index.js +33 -0
  71. package/lib/map/rank.js +253 -0
  72. package/lib/map/render.js +346 -0
  73. package/lib/map/resolver.js +574 -0
  74. package/lib/pack/budget.js +111 -0
  75. package/lib/pack/code-lexer.js +360 -0
  76. package/lib/pack/compress.js +414 -0
  77. package/lib/pack/index.js +354 -0
  78. package/lib/pack/renderers/common.js +134 -0
  79. package/lib/pack/renderers/index.js +24 -0
  80. package/lib/pack/renderers/json.js +47 -0
  81. package/lib/pack/renderers/markdown.js +54 -0
  82. package/lib/pack/renderers/plain.js +38 -0
  83. package/lib/pack/renderers/xml.js +85 -0
  84. package/lib/pack/select.js +177 -0
  85. package/lib/pack/split.js +182 -0
  86. package/lib/pack/tree.js +35 -0
  87. package/lib/parsers/gitignore-parser.js +116 -107
  88. package/lib/parsers/method-filter-parser.js +50 -33
  89. package/lib/plugins/ExporterPlugin.js +3 -3
  90. package/lib/plugins/LanguagePlugin.js +3 -3
  91. package/lib/plugins/PluginManager.js +7 -13
  92. package/lib/policy/audit.js +312 -0
  93. package/lib/policy/enforce.js +125 -0
  94. package/lib/policy/ignore-files.js +169 -0
  95. package/lib/policy/index.js +53 -0
  96. package/lib/policy/policy.js +489 -0
  97. package/lib/pr/diff-parser.js +267 -0
  98. package/lib/pr/method-mapper.js +148 -0
  99. package/lib/pr/pr-context.js +829 -0
  100. package/lib/pr/pr-git.js +149 -0
  101. package/lib/pr/pr-renderer.js +644 -0
  102. package/lib/pr/related-tests.js +296 -0
  103. package/lib/rag/DataSourcePlugin.js +5 -5
  104. package/lib/rag/EmbeddingProvider.js +19 -8
  105. package/lib/rag/Indexer.js +14 -5
  106. package/lib/rag/VectorStoreFactory.js +3 -3
  107. package/lib/rag/ask.js +122 -0
  108. package/lib/rag/sources/FileDataSource.js +12 -9
  109. package/lib/rag/stores/BaseVectorStore.js +5 -5
  110. package/lib/rag/stores/ChromaDBStore.js +10 -10
  111. package/lib/rag/stores/LanceDBStore.js +29 -19
  112. package/lib/rag/stores/PineconeStore.js +11 -9
  113. package/lib/search/bm25.js +433 -0
  114. package/lib/search/code-units.js +201 -0
  115. package/lib/search/symbols.js +189 -0
  116. package/lib/ui/dashboard.js +120 -89
  117. package/lib/ui/index.js +1 -6
  118. package/lib/ui/init-wizard-ui.js +550 -0
  119. package/lib/ui/progress-bar.js +94 -88
  120. package/lib/ui/select-input.js +40 -32
  121. package/lib/ui/template-selector.js +99 -0
  122. package/lib/ui/wizard.js +374 -329
  123. package/lib/utils/claude-token-counter.js +151 -0
  124. package/lib/utils/cli-output.js +17 -0
  125. package/lib/utils/clipboard-utils.js +49 -50
  126. package/lib/utils/config-utils.js +81 -66
  127. package/lib/utils/context-versioning.js +532 -0
  128. package/lib/utils/error-handler.js +112 -112
  129. package/lib/utils/file-utils.js +153 -69
  130. package/lib/utils/format-converter.js +356 -283
  131. package/lib/utils/git-utils.js +366 -294
  132. package/lib/utils/llm-cost-estimator.js +281 -0
  133. package/lib/utils/llm-detector.js +242 -78
  134. package/lib/utils/logger.js +246 -237
  135. package/lib/utils/messages.js +355 -0
  136. package/lib/utils/multi-repo-manager.js +364 -0
  137. package/lib/utils/profile-manager.js +380 -0
  138. package/lib/utils/safe-read.js +61 -0
  139. package/lib/utils/secret-redactor.js +61 -0
  140. package/lib/utils/snapshot-manager.js +679 -0
  141. package/lib/utils/template-manager.js +378 -0
  142. package/lib/utils/token-utils.js +201 -170
  143. package/lib/utils/tokenizer-adapter.js +421 -377
  144. package/lib/utils/updater.js +539 -506
  145. package/lib/watch/FileWatcher.js +12 -8
  146. package/lib/watch/IncrementalAnalyzer.js +7 -7
  147. package/lib/why/coupling.js +81 -0
  148. package/lib/why/errors.js +23 -0
  149. package/lib/why/git.js +169 -0
  150. package/lib/why/history.js +108 -0
  151. package/lib/why/index.js +720 -0
  152. package/lib/why/ownership.js +79 -0
  153. package/lib/why/paths.js +207 -0
  154. package/lib/why/references.js +259 -0
  155. package/lib/why/render.js +722 -0
  156. package/lib/why/search.js +358 -0
  157. package/lib/why/target.js +389 -0
  158. package/lib/wizards/config-generator.js +257 -0
  159. package/lib/wizards/project-detector.js +572 -0
  160. package/package.json +44 -10
  161. package/.ctxman/logs/ctxman-2026-07-02.log +0 -156
  162. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  163. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  164. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  165. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  166. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  167. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  168. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  169. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  170. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  171. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  172. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  173. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  174. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  175. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  176. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  177. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  178. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  179. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  180. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  181. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  182. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  183. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  184. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  185. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  186. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  187. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  188. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  189. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  190. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  191. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  192. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  193. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  194. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  195. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  196. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  197. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  198. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  199. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  200. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  201. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  202. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  203. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  204. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  205. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  206. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  207. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  208. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  209. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  210. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  211. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  212. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  213. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  214. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  215. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  216. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  217. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  218. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  219. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  220. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  221. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  222. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  223. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  224. package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
@@ -6,140 +6,140 @@
6
6
  import fs from 'fs';
7
7
 
8
8
  class ErrorHandler {
9
- constructor(options = {}) {
10
- this.verbose = options.verbose || false;
11
- this.logFile = options.logFile || null;
9
+ constructor(options = {}) {
10
+ this.verbose = options.verbose || false;
11
+ this.logFile = options.logFile || null;
12
+ }
13
+
14
+ /**
15
+ * Handle format errors
16
+ */
17
+ handleFormatError(error, format) {
18
+ const message = `Format error (${format}): ${error.message}`;
19
+
20
+ if (this.verbose) {
21
+ console.error('❌ ' + message);
22
+ console.error(' Stack:', error.stack);
23
+ } else {
24
+ console.error('❌ ' + message);
25
+ console.error(' Use --verbose for detailed error information');
12
26
  }
13
27
 
14
- /**
15
- * Handle format errors
16
- */
17
- handleFormatError(error, format) {
18
- const message = `Format error (${format}): ${error.message}`;
28
+ this.logError(message, error);
29
+ }
19
30
 
20
- if (this.verbose) {
21
- console.error('❌ ' + message);
22
- console.error(' Stack:', error.stack);
23
- } else {
24
- console.error('❌ ' + message);
25
- console.error(' Use --verbose for detailed error information');
26
- }
27
-
28
- this.logError(message, error);
29
- }
30
-
31
- /**
32
- * Handle file errors
33
- */
34
- handleFileError(error, filePath) {
35
- const message = `File error (${filePath}): ${error.message}`;
31
+ /**
32
+ * Handle file errors
33
+ */
34
+ handleFileError(error, filePath) {
35
+ const message = `File error (${filePath}): ${error.message}`;
36
36
 
37
- console.error('❌ ' + message);
37
+ console.error('❌ ' + message);
38
38
 
39
- // Provide helpful suggestions
40
- if (error.code === 'ENOENT') {
41
- console.error(' Suggestion: Check if the file path is correct');
42
- } else if (error.code === 'EACCES') {
43
- console.error(' Suggestion: Check file permissions');
44
- }
45
-
46
- this.logError(message, error);
39
+ // Provide helpful suggestions
40
+ if (error.code === 'ENOENT') {
41
+ console.error(' Suggestion: Check if the file path is correct');
42
+ } else if (error.code === 'EACCES') {
43
+ console.error(' Suggestion: Check file permissions');
47
44
  }
48
45
 
49
- /**
50
- * Handle parse errors
51
- */
52
- handleParseError(error, format, content) {
53
- const message = `Parse error (${format}): ${error.message}`;
46
+ this.logError(message, error);
47
+ }
54
48
 
55
- console.error('❌ ' + message);
49
+ /**
50
+ * Handle parse errors
51
+ */
52
+ handleParseError(error, format, content) {
53
+ const message = `Parse error (${format}): ${error.message}`;
56
54
 
57
- if (this.verbose && content) {
58
- console.error(' Content preview:', content.substring(0, 200) + '...');
59
- }
55
+ console.error('❌ ' + message);
60
56
 
61
- this.logError(message, error);
57
+ if (this.verbose && content) {
58
+ console.error(' Content preview:', content.substring(0, 200) + '...');
62
59
  }
63
60
 
64
- /**
65
- * Handle validation errors
66
- */
67
- handleValidationError(errors, context) {
68
- console.error(`❌ Validation failed (${context}):`);
61
+ this.logError(message, error);
62
+ }
69
63
 
70
- for (const error of errors) {
71
- console.error(` • ${error}`);
72
- }
64
+ /**
65
+ * Handle validation errors
66
+ */
67
+ handleValidationError(errors, context) {
68
+ console.error(`❌ Validation failed (${context}):`);
73
69
 
74
- this.logError(`Validation errors in ${context}`, { errors });
70
+ for (const error of errors) {
71
+ console.error(` • ${error}`);
75
72
  }
76
73
 
77
- /**
78
- * Log error to file (if configured)
79
- */
80
- logError(message, error) {
81
- if (!this.logFile) return;
74
+ this.logError(`Validation errors in ${context}`, { errors });
75
+ }
82
76
 
83
- try {
84
- const timestamp = new Date().toISOString();
85
- const logEntry = `[${timestamp}] ${message}\n${error.stack || ''}\n\n`;
77
+ /**
78
+ * Log error to file (if configured)
79
+ */
80
+ logError(message, error) {
81
+ if (!this.logFile) return;
86
82
 
87
- fs.appendFileSync(this.logFile, logEntry, 'utf8');
88
- } catch (logError) {
89
- // Silently fail if logging fails
90
- }
91
- }
83
+ try {
84
+ const timestamp = new Date().toISOString();
85
+ const logEntry = `[${timestamp}] ${message}\n${error.stack || ''}\n\n`;
92
86
 
93
- /**
94
- * Wrap async function with error handling
95
- */
96
- wrapAsync(fn, context) {
97
- return async (...args) => {
98
- try {
99
- return await fn(...args);
100
- } catch (error) {
101
- console.error(`❌ Error in ${context}:`, error.message);
102
- if (this.verbose) {
103
- console.error(' Stack:', error.stack);
104
- }
105
- this.logError(`Error in ${context}`, error);
106
- throw error;
107
- }
108
- };
87
+ fs.appendFileSync(this.logFile, logEntry, 'utf8');
88
+ } catch (_logError) {
89
+ // Silently fail if logging fails
109
90
  }
110
-
111
- /**
112
- * Validate format support
113
- */
114
- validateFormat(format, supportedFormats) {
115
- if (!supportedFormats.includes(format)) {
116
- const message = `Unsupported format: ${format}`;
117
- const suggestion = `Supported formats: ${supportedFormats.join(', ')}`;
118
-
119
- console.error(`❌ ${message}`);
120
- console.error(` ${suggestion}`);
121
-
122
- throw new Error(message);
91
+ }
92
+
93
+ /**
94
+ * Wrap async function with error handling
95
+ */
96
+ wrapAsync(fn, context) {
97
+ return async (...args) => {
98
+ try {
99
+ return await fn(...args);
100
+ } catch (error) {
101
+ console.error(`❌ Error in ${context}:`, error.message);
102
+ if (this.verbose) {
103
+ console.error(' Stack:', error.stack);
123
104
  }
105
+ this.logError(`Error in ${context}`, error);
106
+ throw error;
107
+ }
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Validate format support
113
+ */
114
+ validateFormat(format, supportedFormats) {
115
+ if (!supportedFormats.includes(format)) {
116
+ const message = `Unsupported format: ${format}`;
117
+ const suggestion = `Supported formats: ${supportedFormats.join(', ')}`;
118
+
119
+ console.error(`❌ ${message}`);
120
+ console.error(` ${suggestion}`);
121
+
122
+ throw new Error(message);
124
123
  }
125
-
126
- /**
127
- * Create user-friendly error message
128
- */
129
- createUserMessage(error, context = '') {
130
- const prefix = context ? `${context}: ` : '';
131
-
132
- // Map common errors to user-friendly messages
133
- const errorMap = {
134
- 'ENOENT': 'File not found',
135
- 'EACCES': 'Permission denied',
136
- 'EISDIR': 'Expected file, got directory',
137
- 'ENOTDIR': 'Expected directory, got file'
138
- };
139
-
140
- const userMessage = errorMap[error.code] || error.message;
141
- return `${prefix}${userMessage}`;
142
- }
124
+ }
125
+
126
+ /**
127
+ * Create user-friendly error message
128
+ */
129
+ createUserMessage(error, context = '') {
130
+ const prefix = context ? `${context}: ` : '';
131
+
132
+ // Map common errors to user-friendly messages
133
+ const errorMap = {
134
+ ENOENT: 'File not found',
135
+ EACCES: 'Permission denied',
136
+ EISDIR: 'Expected file, got directory',
137
+ ENOTDIR: 'Expected directory, got file',
138
+ };
139
+
140
+ const userMessage = errorMap[error.code] || error.message;
141
+ return `${prefix}${userMessage}`;
142
+ }
143
143
  }
144
144
 
145
145
  export default ErrorHandler;
@@ -5,85 +5,169 @@
5
5
 
6
6
  import path from 'path';
7
7
 
8
- class FileUtils {
9
- /**
10
- * Check if file is a text file
11
- * @param {string} filePath - File path
12
- * @returns {boolean}
13
- */
14
- static isText(filePath) {
15
- const ext = path.extname(filePath).toLowerCase();
16
- const basename = path.basename(filePath).toLowerCase();
8
+ // Dependency, VCS and build-output directories that are never scanned, whatever the
9
+ // ignore files say
10
+ const SKIPPED_DIRECTORIES = new Set([
11
+ 'node_modules',
12
+ '.git',
13
+ '.svn',
14
+ '.hg',
15
+ 'coverage',
16
+ 'dist',
17
+ 'build',
18
+ ]);
17
19
 
18
- const textExtensions = new Set([
19
- '.js', '.ts', '.jsx', '.tsx', '.json', '.md', '.txt', '.yml', '.yaml',
20
- '.html', '.css', '.scss', '.sass', '.less', '.xml', '.svg',
21
- '.sh', '.bash', '.zsh', '.py', '.rb', '.php', '.java', '.c', '.cpp', '.cc', '.h', '.hpp',
22
- '.go', '.rs', '.swift', '.kt', '.kts', '.cs', '.scala', '.sql', '.toml', '.ini', '.conf'
23
- ]);
20
+ class FileUtils {
21
+ /**
22
+ * Check if file is a text file
23
+ * @param {string} filePath - File path
24
+ * @returns {boolean}
25
+ */
26
+ static isText(filePath) {
27
+ const ext = path.extname(filePath).toLowerCase();
28
+ const basename = path.basename(filePath).toLowerCase();
24
29
 
25
- const textFiles = ['dockerfile', 'makefile', 'license', 'readme', 'changelog'];
30
+ const textExtensions = new Set([
31
+ '.js',
32
+ '.ts',
33
+ '.jsx',
34
+ '.tsx',
35
+ '.json',
36
+ '.md',
37
+ '.txt',
38
+ '.yml',
39
+ '.yaml',
40
+ '.html',
41
+ '.css',
42
+ '.scss',
43
+ '.sass',
44
+ '.less',
45
+ '.xml',
46
+ '.svg',
47
+ '.sh',
48
+ '.bash',
49
+ '.zsh',
50
+ '.py',
51
+ '.rb',
52
+ '.php',
53
+ '.java',
54
+ '.c',
55
+ '.cpp',
56
+ '.cc',
57
+ '.h',
58
+ '.hpp',
59
+ '.go',
60
+ '.rs',
61
+ '.swift',
62
+ '.kt',
63
+ '.kts',
64
+ '.cs',
65
+ '.scala',
66
+ '.sql',
67
+ '.toml',
68
+ '.ini',
69
+ '.conf',
70
+ ]);
26
71
 
27
- return textExtensions.has(ext) ||
28
- textFiles.some(name => basename.includes(name));
29
- }
72
+ const textFiles = ['dockerfile', 'makefile', 'license', 'readme', 'changelog'];
30
73
 
31
- /**
32
- * Check if file is a code file (supports method extraction)
33
- * @param {string} filePath - File path
34
- * @returns {boolean}
35
- */
36
- static isCode(filePath) {
37
- const ext = path.extname(filePath).toLowerCase();
38
- return [
39
- '.js', '.ts', '.jsx', '.tsx', // JavaScript/TypeScript
40
- '.rs', // Rust
41
- '.cs', // C#
42
- '.go', // Go
43
- '.java', // Java
44
- '.py', // Python
45
- '.php', // PHP
46
- '.rb', // Ruby
47
- '.kt', '.kts', // Kotlin
48
- '.swift', // Swift
49
- '.c', '.cpp', '.cc', '.h', '.hpp', // C/C++
50
- '.scala' // Scala
51
- ].includes(ext);
52
- }
74
+ return textExtensions.has(ext) || textFiles.some((name) => basename.includes(name));
75
+ }
53
76
 
54
- /**
55
- * Get file type category
56
- * @param {string} filePath - File path
57
- * @returns {string} Type: 'code', 'config', 'doc', 'style', 'other'
58
- */
59
- static getType(filePath) {
60
- const ext = path.extname(filePath).toLowerCase();
77
+ /**
78
+ * Check if file is a code file (supports method extraction)
79
+ * @param {string} filePath - File path
80
+ * @returns {boolean}
81
+ */
82
+ static isCode(filePath) {
83
+ const ext = path.extname(filePath).toLowerCase();
84
+ return [
85
+ '.js',
86
+ '.ts',
87
+ '.jsx',
88
+ '.tsx', // JavaScript/TypeScript
89
+ '.rs', // Rust
90
+ '.cs', // C#
91
+ '.go', // Go
92
+ '.java', // Java
93
+ '.py', // Python
94
+ '.php', // PHP
95
+ '.rb', // Ruby
96
+ '.kt',
97
+ '.kts', // Kotlin
98
+ '.swift', // Swift
99
+ '.c',
100
+ '.cpp',
101
+ '.cc',
102
+ '.h',
103
+ '.hpp', // C/C++
104
+ '.scala', // Scala
105
+ ].includes(ext);
106
+ }
61
107
 
62
- const categories = {
63
- code: ['.js', '.ts', '.jsx', '.tsx', '.py', '.rb', '.php', '.java', '.c', '.cpp', '.cc', '.h', '.hpp', '.go', '.rs', '.cs', '.kt', '.kts', '.swift', '.scala'],
64
- config: ['.json', '.yml', '.yaml', '.toml', '.ini', '.conf', '.xml'],
65
- doc: ['.md', '.txt', '.pdf', '.doc', '.docx'],
66
- style: ['.css', '.scss', '.sass', '.less'],
67
- };
108
+ /**
109
+ * Get file type category
110
+ * @param {string} filePath - File path
111
+ * @returns {string} Type: 'code', 'config', 'doc', 'style', 'other'
112
+ */
113
+ static getType(filePath) {
114
+ const ext = path.extname(filePath).toLowerCase();
68
115
 
69
- for (const [type, extensions] of Object.entries(categories)) {
70
- if (extensions.includes(ext)) {
71
- return type;
72
- }
73
- }
116
+ const categories = {
117
+ code: [
118
+ '.js',
119
+ '.ts',
120
+ '.jsx',
121
+ '.tsx',
122
+ '.py',
123
+ '.rb',
124
+ '.php',
125
+ '.java',
126
+ '.c',
127
+ '.cpp',
128
+ '.cc',
129
+ '.h',
130
+ '.hpp',
131
+ '.go',
132
+ '.rs',
133
+ '.cs',
134
+ '.kt',
135
+ '.kts',
136
+ '.swift',
137
+ '.scala',
138
+ ],
139
+ config: ['.json', '.yml', '.yaml', '.toml', '.ini', '.conf', '.xml'],
140
+ doc: ['.md', '.txt', '.pdf', '.doc', '.docx'],
141
+ style: ['.css', '.scss', '.sass', '.less'],
142
+ };
74
143
 
75
- return 'other';
144
+ for (const [type, extensions] of Object.entries(categories)) {
145
+ if (extensions.includes(ext)) {
146
+ return type;
147
+ }
76
148
  }
77
149
 
78
- /**
79
- * Get file extension without dot
80
- * @param {string} filePath - File path
81
- * @returns {string} Extension or 'no-extension'
82
- */
83
- static getExtension(filePath) {
84
- const ext = path.extname(filePath).toLowerCase();
85
- return ext ? ext.slice(1) : 'no-extension';
86
- }
150
+ return 'other';
151
+ }
152
+
153
+ /**
154
+ * Check if a directory is always skipped when scanning a project
155
+ * @param {string} name - Directory name (not a path)
156
+ * @returns {boolean}
157
+ */
158
+ static isSkippedDirectory(name) {
159
+ return SKIPPED_DIRECTORIES.has(name);
160
+ }
161
+
162
+ /**
163
+ * Get file extension without dot
164
+ * @param {string} filePath - File path
165
+ * @returns {string} Extension or 'no-extension'
166
+ */
167
+ static getExtension(filePath) {
168
+ const ext = path.extname(filePath).toLowerCase();
169
+ return ext ? ext.slice(1) : 'no-extension';
170
+ }
87
171
  }
88
172
 
89
173
  export default FileUtils;