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.
- package/.ctxman/custom-profiles.example.json +20 -0
- package/.ctxman/llm-profiles.json +131 -0
- package/.ctxman/logs/ctxman-2026-07-02.log +156 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
- package/.ctxman/wizard-profiles/code-review/.contextignore +41 -0
- package/.ctxman/wizard-profiles/code-review/.contextinclude +30 -0
- package/.ctxman/wizard-profiles/code-review/.methodignore +26 -0
- package/.ctxman/wizard-profiles/code-review/.methodinclude +47 -0
- package/.ctxman/wizard-profiles/code-review/profile.json +26 -0
- package/.ctxman/wizard-profiles/documentation/.contextignore +21 -0
- package/.ctxman/wizard-profiles/documentation/.contextinclude +24 -0
- package/.ctxman/wizard-profiles/documentation/.methodignore +19 -0
- package/.ctxman/wizard-profiles/documentation/.methodinclude +44 -0
- package/.ctxman/wizard-profiles/documentation/profile.json +27 -0
- package/.ctxman/wizard-profiles/full/.contextignore +23 -0
- package/.ctxman/wizard-profiles/full/.contextinclude +38 -0
- package/.ctxman/wizard-profiles/full/.methodignore +5 -0
- package/.ctxman/wizard-profiles/full/.methodinclude +5 -0
- package/.ctxman/wizard-profiles/full/profile.json +28 -0
- package/.ctxman/wizard-profiles/llm-explain/.contextignore +25 -0
- package/.ctxman/wizard-profiles/llm-explain/.contextinclude +33 -0
- package/.ctxman/wizard-profiles/llm-explain/.methodignore +19 -0
- package/.ctxman/wizard-profiles/llm-explain/.methodinclude +48 -0
- package/.ctxman/wizard-profiles/llm-explain/profile.json +27 -0
- package/.ctxman/wizard-profiles/minimal/.contextignore +27 -0
- package/.ctxman/wizard-profiles/minimal/.contextinclude +14 -0
- package/.ctxman/wizard-profiles/minimal/.methodignore +22 -0
- package/.ctxman/wizard-profiles/minimal/.methodinclude +18 -0
- package/.ctxman/wizard-profiles/minimal/profile.json +26 -0
- package/.ctxman/wizard-profiles/security-audit/.contextignore +36 -0
- package/.ctxman/wizard-profiles/security-audit/.contextinclude +37 -0
- package/.ctxman/wizard-profiles/security-audit/.methodignore +29 -0
- package/.ctxman/wizard-profiles/security-audit/.methodinclude +60 -0
- package/.ctxman/wizard-profiles/security-audit/profile.json +34 -0
- package/README.md +1167 -0
- package/bin/cli.js +650 -0
- package/bin/cm-ask.js +95 -0
- package/bin/cm-gitingest.js +134 -0
- package/bin/cm-update.js +175 -0
- package/bin/mcp-server.js +31 -0
- package/ctxman.js +236 -0
- package/examples/README.md +298 -0
- package/examples/custom-llm-profiles.example.json +19 -0
- package/examples/wizard-profiles/code-review/.contextignore +41 -0
- package/examples/wizard-profiles/code-review/.contextinclude +30 -0
- package/examples/wizard-profiles/code-review/.methodignore +26 -0
- package/examples/wizard-profiles/code-review/.methodinclude +47 -0
- package/examples/wizard-profiles/code-review/profile.json +26 -0
- package/examples/wizard-profiles/documentation/.contextignore +21 -0
- package/examples/wizard-profiles/documentation/.contextinclude +24 -0
- package/examples/wizard-profiles/documentation/.methodignore +19 -0
- package/examples/wizard-profiles/documentation/.methodinclude +44 -0
- package/examples/wizard-profiles/documentation/profile.json +27 -0
- package/examples/wizard-profiles/full/.contextignore +23 -0
- package/examples/wizard-profiles/full/.contextinclude +38 -0
- package/examples/wizard-profiles/full/.methodignore +5 -0
- package/examples/wizard-profiles/full/.methodinclude +5 -0
- package/examples/wizard-profiles/full/profile.json +28 -0
- package/examples/wizard-profiles/llm-explain/.contextignore +25 -0
- package/examples/wizard-profiles/llm-explain/.contextinclude +33 -0
- package/examples/wizard-profiles/llm-explain/.methodignore +19 -0
- package/examples/wizard-profiles/llm-explain/.methodinclude +48 -0
- package/examples/wizard-profiles/llm-explain/profile.json +27 -0
- package/examples/wizard-profiles/minimal/.contextignore +27 -0
- package/examples/wizard-profiles/minimal/.contextinclude +14 -0
- package/examples/wizard-profiles/minimal/.methodignore +22 -0
- package/examples/wizard-profiles/minimal/.methodinclude +18 -0
- package/examples/wizard-profiles/minimal/profile.json +26 -0
- package/examples/wizard-profiles/security-audit/.contextignore +36 -0
- package/examples/wizard-profiles/security-audit/.contextinclude +37 -0
- package/examples/wizard-profiles/security-audit/.methodignore +29 -0
- package/examples/wizard-profiles/security-audit/.methodinclude +60 -0
- package/examples/wizard-profiles/security-audit/profile.json +34 -0
- package/index.js +68 -0
- package/lib/agents/pr-failure-agent.js +201 -0
- package/lib/analyzers/go-method-analyzer.js +126 -0
- package/lib/analyzers/method-analyzer.js +457 -0
- package/lib/analyzers/token-calculator.js +577 -0
- package/lib/api/mcp/prompts.js +319 -0
- package/lib/api/mcp/resources.js +269 -0
- package/lib/api/mcp/server.js +339 -0
- package/lib/api/mcp/transports/HttpSseTransport.js +63 -0
- package/lib/api/rest/server.js +405 -0
- package/lib/cache/CacheManager.js +388 -0
- package/lib/core/Analyzer.js +180 -0
- package/lib/core/ContextBuilder.js +325 -0
- package/lib/core/Reporter.js +270 -0
- package/lib/core/Scanner.js +129 -0
- package/lib/formatters/format-registry.js +419 -0
- package/lib/formatters/gitingest-formatter.js +746 -0
- package/lib/formatters/toon-formatter-v1.3.js +731 -0
- package/lib/formatters/toon-formatter.js +291 -0
- package/lib/integrations/git/BlameTracker.js +142 -0
- package/lib/integrations/git/DiffAnalyzer.js +260 -0
- package/lib/integrations/git/GitClient.js +336 -0
- package/lib/parsers/gitignore-parser.js +127 -0
- package/lib/parsers/method-filter-parser.js +50 -0
- package/lib/plugins/ExporterPlugin.js +102 -0
- package/lib/plugins/LanguagePlugin.js +100 -0
- package/lib/plugins/PluginManager.js +302 -0
- package/lib/rag/DataSourcePlugin.js +31 -0
- package/lib/rag/EmbeddingProvider.js +75 -0
- package/lib/rag/Indexer.js +83 -0
- package/lib/rag/VectorStoreFactory.js +24 -0
- package/lib/rag/sources/FileDataSource.js +70 -0
- package/lib/rag/stores/BaseVectorStore.js +43 -0
- package/lib/rag/stores/ChromaDBStore.js +85 -0
- package/lib/rag/stores/LanceDBStore.js +109 -0
- package/lib/rag/stores/PineconeStore.js +74 -0
- package/lib/ui/dashboard.js +109 -0
- package/lib/ui/index.js +15 -0
- package/lib/ui/progress-bar.js +110 -0
- package/lib/ui/select-input.js +51 -0
- package/lib/ui/wizard.js +359 -0
- package/lib/utils/clipboard-utils.js +66 -0
- package/lib/utils/config-utils.js +94 -0
- package/lib/utils/error-handler.js +145 -0
- package/lib/utils/file-utils.js +89 -0
- package/lib/utils/format-converter.js +325 -0
- package/lib/utils/git-utils.js +337 -0
- package/lib/utils/llm-detector.js +409 -0
- package/lib/utils/logger.js +278 -0
- package/lib/utils/token-utils.js +200 -0
- package/lib/utils/tokenizer-adapter.js +454 -0
- package/lib/utils/updater.js +565 -0
- package/lib/watch/FileWatcher.js +208 -0
- package/lib/watch/IncrementalAnalyzer.js +206 -0
- 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
|
+
}
|