ctxman 3.0.0

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 (190) hide show
  1. package/.ctxman/custom-profiles.example.json +20 -0
  2. package/.ctxman/llm-profiles.json +131 -0
  3. package/.ctxman/logs/ctxman-2026-07-02.log +156 -0
  4. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  5. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  6. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  7. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  8. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  9. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  10. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  11. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  12. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  13. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  14. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  15. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  16. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  17. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  18. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  19. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  20. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  21. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  22. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  23. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  24. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  25. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  26. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  27. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  28. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  29. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  30. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  31. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  32. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  33. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  34. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  35. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  36. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  37. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  38. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  39. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  40. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  41. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  42. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  43. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  44. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  45. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  46. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  47. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  48. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  49. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  50. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  51. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  52. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  53. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  54. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  55. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  56. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  57. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  58. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  59. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  60. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  61. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  62. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  63. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  64. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  65. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  66. package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
  67. package/.ctxman/wizard-profiles/code-review/.contextignore +41 -0
  68. package/.ctxman/wizard-profiles/code-review/.contextinclude +30 -0
  69. package/.ctxman/wizard-profiles/code-review/.methodignore +26 -0
  70. package/.ctxman/wizard-profiles/code-review/.methodinclude +47 -0
  71. package/.ctxman/wizard-profiles/code-review/profile.json +26 -0
  72. package/.ctxman/wizard-profiles/documentation/.contextignore +21 -0
  73. package/.ctxman/wizard-profiles/documentation/.contextinclude +24 -0
  74. package/.ctxman/wizard-profiles/documentation/.methodignore +19 -0
  75. package/.ctxman/wizard-profiles/documentation/.methodinclude +44 -0
  76. package/.ctxman/wizard-profiles/documentation/profile.json +27 -0
  77. package/.ctxman/wizard-profiles/full/.contextignore +23 -0
  78. package/.ctxman/wizard-profiles/full/.contextinclude +38 -0
  79. package/.ctxman/wizard-profiles/full/.methodignore +5 -0
  80. package/.ctxman/wizard-profiles/full/.methodinclude +5 -0
  81. package/.ctxman/wizard-profiles/full/profile.json +28 -0
  82. package/.ctxman/wizard-profiles/llm-explain/.contextignore +25 -0
  83. package/.ctxman/wizard-profiles/llm-explain/.contextinclude +33 -0
  84. package/.ctxman/wizard-profiles/llm-explain/.methodignore +19 -0
  85. package/.ctxman/wizard-profiles/llm-explain/.methodinclude +48 -0
  86. package/.ctxman/wizard-profiles/llm-explain/profile.json +27 -0
  87. package/.ctxman/wizard-profiles/minimal/.contextignore +27 -0
  88. package/.ctxman/wizard-profiles/minimal/.contextinclude +14 -0
  89. package/.ctxman/wizard-profiles/minimal/.methodignore +22 -0
  90. package/.ctxman/wizard-profiles/minimal/.methodinclude +18 -0
  91. package/.ctxman/wizard-profiles/minimal/profile.json +26 -0
  92. package/.ctxman/wizard-profiles/security-audit/.contextignore +36 -0
  93. package/.ctxman/wizard-profiles/security-audit/.contextinclude +37 -0
  94. package/.ctxman/wizard-profiles/security-audit/.methodignore +29 -0
  95. package/.ctxman/wizard-profiles/security-audit/.methodinclude +60 -0
  96. package/.ctxman/wizard-profiles/security-audit/profile.json +34 -0
  97. package/README.md +1167 -0
  98. package/bin/cli.js +650 -0
  99. package/bin/cm-ask.js +95 -0
  100. package/bin/cm-gitingest.js +134 -0
  101. package/bin/cm-update.js +175 -0
  102. package/bin/mcp-server.js +31 -0
  103. package/ctxman.js +236 -0
  104. package/examples/README.md +298 -0
  105. package/examples/custom-llm-profiles.example.json +19 -0
  106. package/examples/wizard-profiles/code-review/.contextignore +41 -0
  107. package/examples/wizard-profiles/code-review/.contextinclude +30 -0
  108. package/examples/wizard-profiles/code-review/.methodignore +26 -0
  109. package/examples/wizard-profiles/code-review/.methodinclude +47 -0
  110. package/examples/wizard-profiles/code-review/profile.json +26 -0
  111. package/examples/wizard-profiles/documentation/.contextignore +21 -0
  112. package/examples/wizard-profiles/documentation/.contextinclude +24 -0
  113. package/examples/wizard-profiles/documentation/.methodignore +19 -0
  114. package/examples/wizard-profiles/documentation/.methodinclude +44 -0
  115. package/examples/wizard-profiles/documentation/profile.json +27 -0
  116. package/examples/wizard-profiles/full/.contextignore +23 -0
  117. package/examples/wizard-profiles/full/.contextinclude +38 -0
  118. package/examples/wizard-profiles/full/.methodignore +5 -0
  119. package/examples/wizard-profiles/full/.methodinclude +5 -0
  120. package/examples/wizard-profiles/full/profile.json +28 -0
  121. package/examples/wizard-profiles/llm-explain/.contextignore +25 -0
  122. package/examples/wizard-profiles/llm-explain/.contextinclude +33 -0
  123. package/examples/wizard-profiles/llm-explain/.methodignore +19 -0
  124. package/examples/wizard-profiles/llm-explain/.methodinclude +48 -0
  125. package/examples/wizard-profiles/llm-explain/profile.json +27 -0
  126. package/examples/wizard-profiles/minimal/.contextignore +27 -0
  127. package/examples/wizard-profiles/minimal/.contextinclude +14 -0
  128. package/examples/wizard-profiles/minimal/.methodignore +22 -0
  129. package/examples/wizard-profiles/minimal/.methodinclude +18 -0
  130. package/examples/wizard-profiles/minimal/profile.json +26 -0
  131. package/examples/wizard-profiles/security-audit/.contextignore +36 -0
  132. package/examples/wizard-profiles/security-audit/.contextinclude +37 -0
  133. package/examples/wizard-profiles/security-audit/.methodignore +29 -0
  134. package/examples/wizard-profiles/security-audit/.methodinclude +60 -0
  135. package/examples/wizard-profiles/security-audit/profile.json +34 -0
  136. package/index.js +68 -0
  137. package/lib/agents/pr-failure-agent.js +201 -0
  138. package/lib/analyzers/go-method-analyzer.js +126 -0
  139. package/lib/analyzers/method-analyzer.js +457 -0
  140. package/lib/analyzers/token-calculator.js +577 -0
  141. package/lib/api/mcp/prompts.js +319 -0
  142. package/lib/api/mcp/resources.js +269 -0
  143. package/lib/api/mcp/server.js +339 -0
  144. package/lib/api/mcp/transports/HttpSseTransport.js +63 -0
  145. package/lib/api/rest/server.js +405 -0
  146. package/lib/cache/CacheManager.js +388 -0
  147. package/lib/core/Analyzer.js +180 -0
  148. package/lib/core/ContextBuilder.js +325 -0
  149. package/lib/core/Reporter.js +270 -0
  150. package/lib/core/Scanner.js +129 -0
  151. package/lib/formatters/format-registry.js +419 -0
  152. package/lib/formatters/gitingest-formatter.js +746 -0
  153. package/lib/formatters/toon-formatter-v1.3.js +731 -0
  154. package/lib/formatters/toon-formatter.js +291 -0
  155. package/lib/integrations/git/BlameTracker.js +142 -0
  156. package/lib/integrations/git/DiffAnalyzer.js +260 -0
  157. package/lib/integrations/git/GitClient.js +336 -0
  158. package/lib/parsers/gitignore-parser.js +127 -0
  159. package/lib/parsers/method-filter-parser.js +50 -0
  160. package/lib/plugins/ExporterPlugin.js +102 -0
  161. package/lib/plugins/LanguagePlugin.js +100 -0
  162. package/lib/plugins/PluginManager.js +302 -0
  163. package/lib/rag/DataSourcePlugin.js +31 -0
  164. package/lib/rag/EmbeddingProvider.js +75 -0
  165. package/lib/rag/Indexer.js +83 -0
  166. package/lib/rag/VectorStoreFactory.js +24 -0
  167. package/lib/rag/sources/FileDataSource.js +70 -0
  168. package/lib/rag/stores/BaseVectorStore.js +43 -0
  169. package/lib/rag/stores/ChromaDBStore.js +85 -0
  170. package/lib/rag/stores/LanceDBStore.js +109 -0
  171. package/lib/rag/stores/PineconeStore.js +74 -0
  172. package/lib/ui/dashboard.js +109 -0
  173. package/lib/ui/index.js +15 -0
  174. package/lib/ui/progress-bar.js +110 -0
  175. package/lib/ui/select-input.js +51 -0
  176. package/lib/ui/wizard.js +359 -0
  177. package/lib/utils/clipboard-utils.js +66 -0
  178. package/lib/utils/config-utils.js +94 -0
  179. package/lib/utils/error-handler.js +145 -0
  180. package/lib/utils/file-utils.js +89 -0
  181. package/lib/utils/format-converter.js +325 -0
  182. package/lib/utils/git-utils.js +337 -0
  183. package/lib/utils/llm-detector.js +409 -0
  184. package/lib/utils/logger.js +278 -0
  185. package/lib/utils/token-utils.js +200 -0
  186. package/lib/utils/tokenizer-adapter.js +454 -0
  187. package/lib/utils/updater.js +565 -0
  188. package/lib/watch/FileWatcher.js +208 -0
  189. package/lib/watch/IncrementalAnalyzer.js +206 -0
  190. package/package.json +118 -0
@@ -0,0 +1,319 @@
1
+ import { getLogger } from "../../utils/logger.js";
2
+ import { Analyzer } from "../../core/Analyzer.js";
3
+ import { ContextBuilder } from "../../core/ContextBuilder.js";
4
+ import fs from "fs";
5
+
6
+ const logger = getLogger("MCP:Prompts");
7
+
8
+ /**
9
+ * PromptProvider manages MCP prompt templates
10
+ * Provides reusable templates for common development workflows
11
+ */
12
+ export class PromptProvider {
13
+ constructor(analyzer = null, contextBuilder = null) {
14
+ this.analyzer = analyzer || new Analyzer();
15
+ this.contextBuilder = contextBuilder || new ContextBuilder();
16
+ this.templates = this.initializeTemplates();
17
+ }
18
+
19
+ /**
20
+ * Initialize default prompt templates
21
+ * @private
22
+ */
23
+ initializeTemplates() {
24
+ return {
25
+ code_review: {
26
+ name: "code_review",
27
+ title: "Code Review",
28
+ description: "Analyze code quality, identify issues, and suggest improvements",
29
+ arguments: [
30
+ {
31
+ name: "code",
32
+ description: "The code to review (optional if filePath provided)",
33
+ required: false
34
+ },
35
+ {
36
+ name: "filePath",
37
+ description: "Path to the file to review",
38
+ required: false
39
+ },
40
+ {
41
+ name: "focus",
42
+ description: "Specific aspect to focus on (security, performance, readability, etc.)",
43
+ required: false
44
+ }
45
+ ],
46
+ handler: this.codeReviewHandler.bind(this)
47
+ },
48
+
49
+ refactor_code: {
50
+ name: "refactor_code",
51
+ title: "Refactor Code",
52
+ description: "Suggest refactoring strategies to improve code structure and maintainability",
53
+ arguments: [
54
+ {
55
+ name: "filePath",
56
+ description: "Path to the file to refactor",
57
+ required: true
58
+ },
59
+ {
60
+ name: "focus",
61
+ description: "Refactoring focus (modularity, testability, performance, etc.)",
62
+ required: false
63
+ }
64
+ ],
65
+ handler: this.refactorHandler.bind(this)
66
+ },
67
+
68
+ explain_codebase: {
69
+ name: "explain_codebase",
70
+ title: "Explain Codebase",
71
+ description: "Explain the architecture and structure of the codebase",
72
+ arguments: [
73
+ {
74
+ name: "projectPath",
75
+ description: "Path to the project root",
76
+ required: true
77
+ },
78
+ {
79
+ name: "aspect",
80
+ description: "Specific aspect to explain (architecture, dependencies, patterns, etc.)",
81
+ required: false
82
+ }
83
+ ],
84
+ handler: this.explainCodebaseHandler.bind(this)
85
+ },
86
+
87
+ debug_issue: {
88
+ name: "debug_issue",
89
+ title: "Debug Issue",
90
+ description: "Help debug and diagnose code issues",
91
+ arguments: [
92
+ {
93
+ name: "error",
94
+ description: "Error message or description of the issue",
95
+ required: true
96
+ },
97
+ {
98
+ name: "stackTrace",
99
+ description: "Stack trace (if available)",
100
+ required: false
101
+ },
102
+ {
103
+ name: "filePath",
104
+ description: "Path to the file where the issue occurs",
105
+ required: false
106
+ }
107
+ ],
108
+ handler: this.debugHandler.bind(this)
109
+ },
110
+
111
+ write_tests: {
112
+ name: "write_tests",
113
+ title: "Write Tests",
114
+ description: "Generate test cases for functions or modules",
115
+ arguments: [
116
+ {
117
+ name: "filePath",
118
+ description: "Path to the file to test",
119
+ required: true
120
+ },
121
+ {
122
+ name: "functions",
123
+ description: "Specific functions to test (comma-separated)",
124
+ required: false
125
+ },
126
+ {
127
+ name: "framework",
128
+ description: "Testing framework (vitest, jest, mocha, etc.)",
129
+ required: false
130
+ }
131
+ ],
132
+ handler: this.writeTestsHandler.bind(this)
133
+ }
134
+ };
135
+ }
136
+
137
+ /**
138
+ * List available prompts
139
+ * @param {string} cursor - Optional cursor for pagination
140
+ * @returns {Object} List of prompts
141
+ */
142
+ listPrompts(cursor = null) {
143
+ const prompts = Object.values(this.templates).map(template => ({
144
+ name: template.name,
145
+ title: template.title,
146
+ description: template.description,
147
+ arguments: template.arguments
148
+ }));
149
+
150
+ logger.info(`Listed ${prompts.length} prompts`);
151
+ return { prompts };
152
+ }
153
+
154
+ /**
155
+ * Get a specific prompt with arguments
156
+ * @param {string} name - Prompt name
157
+ * @param {Object} args - Prompt arguments
158
+ * @returns {Promise<Object>} Rendered prompt
159
+ */
160
+ async getPrompt(name, args = {}) {
161
+ logger.info(`Getting prompt: ${name}`);
162
+
163
+ const template = this.templates[name];
164
+ if (!template) {
165
+ throw new Error(`Prompt template not found: ${name}`);
166
+ }
167
+
168
+ // Validate required arguments
169
+ this.validateArguments(template, args);
170
+
171
+ // Execute template handler
172
+ const messages = await template.handler(args);
173
+
174
+ return {
175
+ description: template.description,
176
+ messages
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Validate prompt arguments
182
+ * @private
183
+ */
184
+ validateArguments(template, args) {
185
+ for (const arg of template.arguments) {
186
+ if (arg.required && !(arg.name in args)) {
187
+ throw new Error(`Missing required argument: ${arg.name}`);
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Code review prompt handler
194
+ * @private
195
+ */
196
+ async codeReviewHandler(args) {
197
+ let code = args.code;
198
+
199
+ // If filePath provided, read the file
200
+ if (args.filePath && !code) {
201
+ try {
202
+ code = fs.readFileSync(args.filePath, "utf-8");
203
+ } catch (error) {
204
+ throw new Error(`Failed to read file: ${error.message}`);
205
+ }
206
+ }
207
+
208
+ if (!code) {
209
+ throw new Error("Either 'code' or 'filePath' must be provided");
210
+ }
211
+
212
+ const focusText = args.focus ? ` with focus on ${args.focus}` : "";
213
+ const fileInfo = args.filePath ? ` from ${args.filePath}` : "";
214
+
215
+ return [
216
+ {
217
+ role: "user",
218
+ content: {
219
+ type: "text",
220
+ text: `Please review the following code${fileInfo}${focusText}:\n\n\`\`\`\n${code}\n\`\`\`\n\nProvide feedback on:\n1. Code quality and best practices\n2. Potential bugs or issues\n3. Performance considerations\n4. Readability and maintainability\n5. Suggested improvements`
221
+ }
222
+ }
223
+ ];
224
+ }
225
+
226
+ /**
227
+ * Refactor prompt handler
228
+ * @private
229
+ */
230
+ async refactorHandler(args) {
231
+ const code = fs.readFileSync(args.filePath, "utf-8");
232
+ const focusText = args.focus ? ` focusing on ${args.focus}` : "";
233
+
234
+ return [
235
+ {
236
+ role: "user",
237
+ content: {
238
+ type: "text",
239
+ text: `Please suggest refactoring strategies for the following code from ${args.filePath}${focusText}:\n\n\`\`\`\n${code}\n\`\`\`\n\nProvide:\n1. Identified code smells\n2. Refactoring suggestions\n3. Expected benefits\n4. Implementation steps`
240
+ }
241
+ }
242
+ ];
243
+ }
244
+
245
+ /**
246
+ * Explain codebase prompt handler
247
+ * @private
248
+ */
249
+ async explainCodebaseHandler(args) {
250
+ const Scanner = (await import("../../core/Scanner.js")).Scanner;
251
+ const scanner = new Scanner(args.projectPath);
252
+ const files = scanner.scan();
253
+ const analysis = await this.analyzer.analyze(files);
254
+
255
+ const aspectText = args.aspect ? ` with focus on ${args.aspect}` : "";
256
+ const stats = analysis.stats;
257
+
258
+ return [
259
+ {
260
+ role: "user",
261
+ content: {
262
+ type: "text",
263
+ text: `Please explain the codebase at ${args.projectPath}${aspectText}.\n\nCodebase Statistics:\n- Total Files: ${stats.totalFiles}\n- Total Lines: ${stats.totalLines}\n- Total Tokens: ${stats.totalTokens}\n- Languages: ${Object.keys(stats.byLanguage || {}).join(", ")}\n\nProvide:\n1. Architecture overview\n2. Key components and their relationships\n3. Design patterns used\n4. Technology stack\n5. Notable characteristics`
264
+ }
265
+ }
266
+ ];
267
+ }
268
+
269
+ /**
270
+ * Debug prompt handler
271
+ * @private
272
+ */
273
+ async debugHandler(args) {
274
+ let debugContext = `Error: ${args.error}`;
275
+
276
+ if (args.stackTrace) {
277
+ debugContext += `\n\nStack Trace:\n${args.stackTrace}`;
278
+ }
279
+
280
+ if (args.filePath) {
281
+ try {
282
+ const code = fs.readFileSync(args.filePath, "utf-8");
283
+ debugContext += `\n\nRelevant Code (${args.filePath}):\n\`\`\`\n${code}\n\`\`\``;
284
+ } catch (error) {
285
+ // File read failed, continue without code
286
+ }
287
+ }
288
+
289
+ return [
290
+ {
291
+ role: "user",
292
+ content: {
293
+ type: "text",
294
+ text: `Please help debug the following issue:\n\n${debugContext}\n\nProvide:\n1. Root cause analysis\n2. Possible fixes\n3. Prevention strategies\n4. Related best practices`
295
+ }
296
+ }
297
+ ];
298
+ }
299
+
300
+ /**
301
+ * Write tests prompt handler
302
+ * @private
303
+ */
304
+ async writeTestsHandler(args) {
305
+ const code = fs.readFileSync(args.filePath, "utf-8");
306
+ const functionsText = args.functions ? ` for functions: ${args.functions}` : "";
307
+ const frameworkText = args.framework ? ` using ${args.framework}` : "";
308
+
309
+ return [
310
+ {
311
+ role: "user",
312
+ content: {
313
+ type: "text",
314
+ text: `Please write comprehensive tests${functionsText}${frameworkText} for the following code from ${args.filePath}:\n\n\`\`\`\n${code}\n\`\`\`\n\nProvide:\n1. Unit tests for individual functions\n2. Edge case tests\n3. Integration tests (if applicable)\n4. Mock/stub examples\n5. Test organization suggestions`
315
+ }
316
+ }
317
+ ];
318
+ }
319
+ }
@@ -0,0 +1,269 @@
1
+ import { getLogger } from "../../utils/logger.js";
2
+ import { Scanner } from "../../core/Scanner.js";
3
+ import path from "path";
4
+ import fs from "fs";
5
+
6
+ const logger = getLogger("MCP:Resources");
7
+
8
+ /**
9
+ * ResourceProvider manages MCP resources for codebase access
10
+ * Provides list, read, and template operations for resources
11
+ */
12
+ export class ResourceProvider {
13
+ constructor(projectPath) {
14
+ this.projectPath = projectPath;
15
+ this.scanner = null;
16
+ this.cache = new Map();
17
+ this.supportedTypes = {
18
+ FILE: "file",
19
+ ANALYSIS: "analysis",
20
+ CONTEXT: "context"
21
+ };
22
+ }
23
+
24
+ /**
25
+ * List available resources with optional pagination
26
+ * @param {string} cursor - Optional cursor for pagination
27
+ * @returns {Promise<{resources: Array, nextCursor?: string}>}
28
+ */
29
+ async listResources(cursor = null) {
30
+ try {
31
+ logger.info(`Listing resources${cursor ? ` from cursor: ${cursor}` : ""}`);
32
+
33
+ // Initialize scanner if needed
34
+ if (!this.scanner && this.projectPath) {
35
+ this.scanner = new Scanner(this.projectPath);
36
+ }
37
+
38
+ const resources = [];
39
+
40
+ // Add file resources if project path is set
41
+ if (this.scanner) {
42
+ const files = this.scanner.scan();
43
+ const PAGE_SIZE = 100;
44
+ const startIdx = cursor ? parseInt(cursor, 10) : 0;
45
+ const endIdx = Math.min(startIdx + PAGE_SIZE, files.length);
46
+
47
+ for (let i = startIdx; i < endIdx; i++) {
48
+ const file = files[i];
49
+ resources.push({
50
+ uri: `file://codebase/${file.relativePath}`,
51
+ name: path.basename(file.relativePath),
52
+ description: `Source file: ${file.relativePath}`,
53
+ mimeType: this.getMimeType(file.relativePath)
54
+ });
55
+ }
56
+
57
+ // Return next cursor if there are more results
58
+ const nextCursor = endIdx < files.length ? endIdx.toString() : undefined;
59
+
60
+ logger.info(`Listed ${resources.length} resources`);
61
+ return { resources, nextCursor };
62
+ }
63
+
64
+ return { resources };
65
+ } catch (error) {
66
+ logger.error(`Error listing resources: ${error.message}`);
67
+ throw error;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Read a specific resource by URI
73
+ * @param {string} uri - Resource URI
74
+ * @returns {Promise<{contents: Array}>}
75
+ */
76
+ async readResource(uri) {
77
+ try {
78
+ logger.info(`Reading resource: ${uri}`);
79
+
80
+ // Parse URI
81
+ const parsed = this.parseUri(uri);
82
+
83
+ switch (parsed.type) {
84
+ case this.supportedTypes.FILE:
85
+ return await this.readFileResource(parsed.path);
86
+
87
+ case this.supportedTypes.ANALYSIS:
88
+ return await this.readAnalysisResource(parsed.id);
89
+
90
+ case this.supportedTypes.CONTEXT:
91
+ return await this.readContextResource(parsed.name);
92
+
93
+ default:
94
+ throw new Error(`Unsupported resource type: ${parsed.type}`);
95
+ }
96
+ } catch (error) {
97
+ logger.error(`Error reading resource ${uri}: ${error.message}`);
98
+ throw error;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Get resource templates for dynamic resources
104
+ * @returns {Array}
105
+ */
106
+ getResourceTemplates() {
107
+ return [
108
+ {
109
+ uriTemplate: "file://codebase/{path}",
110
+ name: "Codebase Files",
111
+ description: "Access source files in the project",
112
+ mimeType: "text/plain"
113
+ },
114
+ {
115
+ uriTemplate: "analysis://recent/{id}",
116
+ name: "Analysis Results",
117
+ description: "Cached analysis results",
118
+ mimeType: "application/json"
119
+ },
120
+ {
121
+ uriTemplate: "context://template/{name}",
122
+ name: "Context Templates",
123
+ description: "Pre-generated context for common workflows",
124
+ mimeType: "text/plain"
125
+ }
126
+ ];
127
+ }
128
+
129
+ /**
130
+ * Read a file resource
131
+ * @private
132
+ */
133
+ async readFileResource(relativePath) {
134
+ const fullPath = path.join(this.projectPath || "", relativePath);
135
+
136
+ // Security check: ensure path is within project
137
+ const resolvedPath = path.resolve(fullPath);
138
+ const resolvedProject = path.resolve(this.projectPath || "");
139
+
140
+ if (!resolvedPath.startsWith(resolvedProject)) {
141
+ throw new Error("Access denied: path outside project directory");
142
+ }
143
+
144
+ const content = fs.readFileSync(fullPath, "utf-8");
145
+
146
+ return {
147
+ contents: [{
148
+ uri: `file://codebase/${relativePath}`,
149
+ mimeType: this.getMimeType(relativePath),
150
+ text: content
151
+ }]
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Read an analysis resource from cache
157
+ * @private
158
+ */
159
+ async readAnalysisResource(id) {
160
+ const cached = this.cache.get(`analysis:${id}`);
161
+
162
+ if (!cached) {
163
+ throw new Error(`Analysis result not found: ${id}`);
164
+ }
165
+
166
+ return {
167
+ contents: [{
168
+ uri: `analysis://recent/${id}`,
169
+ mimeType: "application/json",
170
+ text: JSON.stringify(cached, null, 2)
171
+ }]
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Read a context template resource
177
+ * @private
178
+ */
179
+ async readContextResource(name) {
180
+ const cached = this.cache.get(`context:${name}`);
181
+
182
+ if (!cached) {
183
+ throw new Error(`Context template not found: ${name}`);
184
+ }
185
+
186
+ return {
187
+ contents: [{
188
+ uri: `context://template/${name}`,
189
+ mimeType: "text/plain",
190
+ text: cached
191
+ }]
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Cache an analysis result
197
+ * @param {string} id - Resource ID
198
+ * @param {Object} data - Analysis data
199
+ */
200
+ cacheAnalysis(id, data) {
201
+ this.cache.set(`analysis:${id}`, data);
202
+ logger.info(`Cached analysis: ${id}`);
203
+ }
204
+
205
+ /**
206
+ * Cache a context template
207
+ * @param {string} name - Template name
208
+ * @param {string} content - Context content
209
+ */
210
+ cacheContext(name, content) {
211
+ this.cache.set(`context:${name}`, content);
212
+ logger.info(`Cached context: ${name}`);
213
+ }
214
+
215
+ /**
216
+ * Parse a resource URI
217
+ * @private
218
+ */
219
+ parseUri(uri) {
220
+ const fileMatch = uri.match(/^file:\/\/codebase\/(.+)$/);
221
+ if (fileMatch) {
222
+ return { type: this.supportedTypes.FILE, path: fileMatch[1] };
223
+ }
224
+
225
+ const analysisMatch = uri.match(/^analysis:\/\/recent\/(.+)$/);
226
+ if (analysisMatch) {
227
+ return { type: this.supportedTypes.ANALYSIS, id: analysisMatch[1] };
228
+ }
229
+
230
+ const contextMatch = uri.match(/^context:\/\/template\/(.+)$/);
231
+ if (contextMatch) {
232
+ return { type: this.supportedTypes.CONTEXT, name: contextMatch[1] };
233
+ }
234
+
235
+ throw new Error(`Invalid resource URI: ${uri}`);
236
+ }
237
+
238
+ /**
239
+ * Get MIME type for a file
240
+ * @private
241
+ */
242
+ getMimeType(filePath) {
243
+ const ext = path.extname(filePath).toLowerCase();
244
+
245
+ const mimeTypes = {
246
+ ".js": "text/javascript",
247
+ ".ts": "text/typescript",
248
+ ".jsx": "text/javascript",
249
+ ".tsx": "text/typescript",
250
+ ".py": "text/x-python",
251
+ ".java": "text/x-java",
252
+ ".go": "text/x-go",
253
+ ".rs": "text/x-rust",
254
+ ".c": "text/x-c",
255
+ ".cpp": "text/x-c++",
256
+ ".h": "text/x-c",
257
+ ".json": "application/json",
258
+ ".md": "text/markdown",
259
+ ".txt": "text/plain",
260
+ ".html": "text/html",
261
+ ".css": "text/css",
262
+ ".xml": "application/xml",
263
+ ".yaml": "text/yaml",
264
+ ".yml": "text/yaml"
265
+ };
266
+
267
+ return mimeTypes[ext] || "text/plain";
268
+ }
269
+ }