dev-mcp-server 0.0.3 → 1.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/.env.example +23 -55
- package/README.md +609 -219
- package/cli.js +486 -160
- package/package.json +2 -2
- package/src/agents/BaseAgent.js +113 -0
- package/src/agents/dreamer.js +165 -0
- package/src/agents/improver.js +175 -0
- package/src/agents/specialists.js +202 -0
- package/src/agents/taskDecomposer.js +176 -0
- package/src/agents/teamCoordinator.js +153 -0
- package/src/api/routes/agents.js +172 -0
- package/src/api/routes/extras.js +115 -0
- package/src/api/routes/git.js +72 -0
- package/src/api/routes/ingest.js +60 -40
- package/src/api/routes/knowledge.js +59 -41
- package/src/api/routes/memory.js +41 -0
- package/src/api/routes/newRoutes.js +168 -0
- package/src/api/routes/pipelines.js +41 -0
- package/src/api/routes/planner.js +54 -0
- package/src/api/routes/query.js +24 -0
- package/src/api/routes/sessions.js +54 -0
- package/src/api/routes/tasks.js +67 -0
- package/src/api/routes/tools.js +85 -0
- package/src/api/routes/v5routes.js +196 -0
- package/src/api/server.js +134 -6
- package/src/context/compactor.js +151 -0
- package/src/context/contextEngineer.js +181 -0
- package/src/context/contextVisualizer.js +140 -0
- package/src/core/conversationEngine.js +231 -0
- package/src/core/indexer.js +169 -143
- package/src/core/ingester.js +141 -126
- package/src/core/queryEngine.js +286 -236
- package/src/cron/cronScheduler.js +260 -0
- package/src/dashboard/index.html +1181 -0
- package/src/lsp/symbolNavigator.js +220 -0
- package/src/memory/memoryManager.js +186 -0
- package/src/memory/teamMemory.js +111 -0
- package/src/messaging/messageBus.js +177 -0
- package/src/monitor/proactiveMonitor.js +337 -0
- package/src/pipelines/pipelineEngine.js +230 -0
- package/src/planner/plannerEngine.js +202 -0
- package/src/plugins/builtin/stats-plugin.js +29 -0
- package/src/plugins/pluginManager.js +144 -0
- package/src/prompts/promptEngineer.js +289 -0
- package/src/sessions/sessionManager.js +166 -0
- package/src/skills/skillsManager.js +263 -0
- package/src/storage/store.js +127 -105
- package/src/tasks/taskManager.js +151 -0
- package/src/tools/BashTool.js +154 -0
- package/src/tools/FileEditTool.js +280 -0
- package/src/tools/GitTool.js +212 -0
- package/src/tools/GrepTool.js +199 -0
- package/src/tools/registry.js +1380 -0
- package/src/utils/costTracker.js +69 -0
- package/src/utils/fileParser.js +176 -153
- package/src/utils/llmClient.js +355 -206
- package/src/watcher/fileWatcher.js +137 -0
- package/src/worktrees/worktreeManager.js +176 -0
package/src/core/queryEngine.js
CHANGED
|
@@ -1,236 +1,286 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return QUERY_MODES.
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (/
|
|
28
|
-
return QUERY_MODES.
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
Format:
|
|
55
|
-
|
|
56
|
-
[QUERY_MODES.
|
|
57
|
-
Your job:
|
|
58
|
-
- List
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
${
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
1
|
+
const llm = require('../utils/llmClient');
|
|
2
|
+
const indexer = require('./indexer');
|
|
3
|
+
const logger = require('../utils/logger');
|
|
4
|
+
const costTracker = require('../utils/costTracker');
|
|
5
|
+
const { MemoryManager } = require('../memory/memoryManager');
|
|
6
|
+
|
|
7
|
+
// The 3 core query modes from the article
|
|
8
|
+
const QUERY_MODES = {
|
|
9
|
+
DEBUG: 'debug', // "Why is this failing?"
|
|
10
|
+
USAGE: 'usage', // "Where is this used?"
|
|
11
|
+
IMPACT: 'impact', // "If I change this, what breaks?"
|
|
12
|
+
GENERAL: 'general', // Open-ended question
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detect the most appropriate query mode from the question text
|
|
17
|
+
*/
|
|
18
|
+
function detectMode(question) {
|
|
19
|
+
const q = question.toLowerCase();
|
|
20
|
+
|
|
21
|
+
if (/why.*(fail|error|crash|break|throw|exception|not work)/i.test(q) ||
|
|
22
|
+
/what.*(error|exception|wrong|cause|happen)/i.test(q) ||
|
|
23
|
+
/debug|trace|stack|exception|ClassCastException|NullPointer|TypeError/i.test(q)) {
|
|
24
|
+
return QUERY_MODES.DEBUG;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (/where.*(use|call|reference|import|depend)|who.*(use|call)|find.*(usage|reference)/i.test(q)) {
|
|
28
|
+
return QUERY_MODES.USAGE;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (/if.*change|what.*break|impact|affect|depend|side effect|ripple|downstream/i.test(q)) {
|
|
32
|
+
return QUERY_MODES.IMPACT;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return QUERY_MODES.GENERAL;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build a mode-specific system prompt
|
|
40
|
+
*/
|
|
41
|
+
function buildSystemPrompt(mode) {
|
|
42
|
+
const base = `You are an expert developer assistant with deep knowledge of the codebase.
|
|
43
|
+
You answer questions ONLY based on the code context provided — never guess or hallucinate.
|
|
44
|
+
If the context doesn't contain enough information, say so clearly.
|
|
45
|
+
Be concise, direct, and developer-friendly. Use code examples from the context when relevant.`;
|
|
46
|
+
|
|
47
|
+
const modeInstructions = {
|
|
48
|
+
[QUERY_MODES.DEBUG]: `
|
|
49
|
+
Your job: DIAGNOSE the root cause of bugs and errors.
|
|
50
|
+
- Identify the exact type mismatch, null reference, or logic flaw
|
|
51
|
+
- Trace the execution flow that leads to the error
|
|
52
|
+
- Point to the specific file, function, and line range where the issue originates
|
|
53
|
+
- Provide a concrete fix with code
|
|
54
|
+
Format: Root Cause → Affected Flow → Fix`,
|
|
55
|
+
|
|
56
|
+
[QUERY_MODES.USAGE]: `
|
|
57
|
+
Your job: FIND all usages of a function, class, module, or variable.
|
|
58
|
+
- List every file where it is imported or referenced
|
|
59
|
+
- Explain HOW it is used in each context (called directly, passed as callback, extended, etc.)
|
|
60
|
+
- Note any patterns or inconsistencies in usage
|
|
61
|
+
Format: Summary → File-by-file breakdown`,
|
|
62
|
+
|
|
63
|
+
[QUERY_MODES.IMPACT]: `
|
|
64
|
+
Your job: ANALYSE what would break or change if the target is modified.
|
|
65
|
+
- List all files/modules that directly depend on it
|
|
66
|
+
- Identify indirect dependencies (things that use things that use it)
|
|
67
|
+
- Flag any risky or tightly-coupled areas
|
|
68
|
+
- Suggest safe modification strategies
|
|
69
|
+
Format: Direct Impact → Indirect Impact → Risk Level → Safe change strategy`,
|
|
70
|
+
|
|
71
|
+
[QUERY_MODES.GENERAL]: `
|
|
72
|
+
Your job: Answer the developer's question using the codebase context.
|
|
73
|
+
- Be specific and cite the relevant files/functions
|
|
74
|
+
- If the answer spans multiple files, connect the dots
|
|
75
|
+
- If something is unclear in the code, flag it`,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return base + (modeInstructions[mode] || modeInstructions[QUERY_MODES.GENERAL]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Format retrieved context chunks into a readable prompt section
|
|
83
|
+
*/
|
|
84
|
+
function formatContext(docs) {
|
|
85
|
+
if (!docs || docs.length === 0) {
|
|
86
|
+
return 'No relevant context found in the codebase index.';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return docs
|
|
90
|
+
.map((doc, i) => {
|
|
91
|
+
const meta = doc.metadata || {};
|
|
92
|
+
const metaSummary = [
|
|
93
|
+
meta.functions?.length ? `Functions: ${meta.functions.slice(0, 5).join(', ')}` : null,
|
|
94
|
+
meta.classes?.length ? `Classes: ${meta.classes.join(', ')}` : null,
|
|
95
|
+
meta.imports?.length ? `Imports: ${meta.imports.slice(0, 3).join(', ')}` : null,
|
|
96
|
+
meta.errors?.length ? `Errors: ${meta.errors.slice(0, 3).join(', ')}` : null,
|
|
97
|
+
meta.patterns?.length ? `Patterns: ${meta.patterns.join(', ')}` : null,
|
|
98
|
+
]
|
|
99
|
+
.filter(Boolean)
|
|
100
|
+
.join(' | ');
|
|
101
|
+
|
|
102
|
+
return `--- [${i + 1}] ${doc.filename} (${doc.kind}) | Score: ${doc.relevanceScore} ---
|
|
103
|
+
Path: ${doc.filePath}
|
|
104
|
+
${metaSummary ? `Meta: ${metaSummary}` : ''}
|
|
105
|
+
\`\`\`
|
|
106
|
+
${doc.content}
|
|
107
|
+
\`\`\``;
|
|
108
|
+
})
|
|
109
|
+
.join('\n\n');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class QueryEngine {
|
|
113
|
+
/**
|
|
114
|
+
* Main query method
|
|
115
|
+
*/
|
|
116
|
+
async query(question, options = {}) {
|
|
117
|
+
const {
|
|
118
|
+
mode: forcedMode,
|
|
119
|
+
topK = 8,
|
|
120
|
+
stream = false,
|
|
121
|
+
filter = {},
|
|
122
|
+
} = options;
|
|
123
|
+
|
|
124
|
+
if (!question || question.trim().length === 0) {
|
|
125
|
+
throw new Error('Question cannot be empty');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const mode = forcedMode || detectMode(question);
|
|
129
|
+
logger.info(`Query mode: ${mode} | Q: "${question.slice(0, 80)}..."`);
|
|
130
|
+
|
|
131
|
+
// Retrieve relevant context
|
|
132
|
+
let docs;
|
|
133
|
+
switch (mode) {
|
|
134
|
+
case QUERY_MODES.DEBUG:
|
|
135
|
+
docs = indexer.searchForErrors(question, topK);
|
|
136
|
+
break;
|
|
137
|
+
case QUERY_MODES.USAGE:
|
|
138
|
+
// Extract the symbol being searched
|
|
139
|
+
const usageMatch = question.match(/(?:where|how|who).*?(?:is|are|does)?\s+[`"']?(\w+)[`"']?\s+(?:used|called|import|reference)/i);
|
|
140
|
+
const symbol = usageMatch?.[1] || question;
|
|
141
|
+
docs = indexer.searchForUsages(symbol, topK);
|
|
142
|
+
break;
|
|
143
|
+
case QUERY_MODES.IMPACT:
|
|
144
|
+
const impactMatch = question.match(/(?:change|modify|update|refactor)\s+[`"']?(\w+)[`"']?/i);
|
|
145
|
+
const target = impactMatch?.[1] || question;
|
|
146
|
+
docs = indexer.searchForImpact(target, topK);
|
|
147
|
+
break;
|
|
148
|
+
default:
|
|
149
|
+
docs = indexer.search(question, topK, filter);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const contextText = formatContext(docs);
|
|
153
|
+
const systemPrompt = buildSystemPrompt(mode);
|
|
154
|
+
|
|
155
|
+
const userMessage = `## Developer Question
|
|
156
|
+
${question}
|
|
157
|
+
|
|
158
|
+
## Codebase Context (retrieved from your system)
|
|
159
|
+
${contextText}
|
|
160
|
+
|
|
161
|
+
## Answer`;
|
|
162
|
+
|
|
163
|
+
const { provider, model } = llm.getInfo();
|
|
164
|
+
logger.info(`Sending to ${provider} (${model}): ${docs.length} context chunks`);
|
|
165
|
+
|
|
166
|
+
// Inject relevant memories into context
|
|
167
|
+
const memories = MemoryManager.getRelevant(question, 5);
|
|
168
|
+
const memoryContext = MemoryManager.formatAsContext(memories);
|
|
169
|
+
const fullSystem = memoryContext ? `${systemPrompt}\n\n${memoryContext}` : systemPrompt;
|
|
170
|
+
|
|
171
|
+
if (stream) {
|
|
172
|
+
return this._streamQuery(fullSystem, userMessage, docs, mode);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const response = await llm.chat({
|
|
176
|
+
model: llm.model('smart'),
|
|
177
|
+
max_tokens: 2000,
|
|
178
|
+
system: fullSystem,
|
|
179
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const answer = response.content[0].text;
|
|
183
|
+
|
|
184
|
+
// Track cost
|
|
185
|
+
const sessionId = options.sessionId || 'default';
|
|
186
|
+
const cost = costTracker.record({
|
|
187
|
+
model: llm.model('smart'),
|
|
188
|
+
inputTokens: response.usage.input_tokens,
|
|
189
|
+
outputTokens: response.usage.output_tokens,
|
|
190
|
+
sessionId,
|
|
191
|
+
queryType: mode,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Auto-extract memories from this exchange (async, fire-and-forget)
|
|
195
|
+
if (options.extractMemories !== false) {
|
|
196
|
+
MemoryManager.extractFromExchange(question, answer, sessionId).catch(() => { });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
answer,
|
|
201
|
+
mode,
|
|
202
|
+
memoriesUsed: memories.length,
|
|
203
|
+
sources: docs.map(d => ({
|
|
204
|
+
file: d.filename,
|
|
205
|
+
path: d.filePath,
|
|
206
|
+
kind: d.kind,
|
|
207
|
+
relevanceScore: d.relevanceScore,
|
|
208
|
+
functions: d.metadata?.functions?.slice(0, 5) || [],
|
|
209
|
+
})),
|
|
210
|
+
usage: {
|
|
211
|
+
inputTokens: response.usage.input_tokens,
|
|
212
|
+
outputTokens: response.usage.output_tokens,
|
|
213
|
+
costUsd: cost.costUsd,
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Streaming version of query
|
|
220
|
+
*/
|
|
221
|
+
async *_streamQuery(systemPrompt, userMessage, docs, mode) {
|
|
222
|
+
const stream = await llm.chat({
|
|
223
|
+
model: llm.model('smart'),
|
|
224
|
+
max_tokens: 2000,
|
|
225
|
+
system: systemPrompt,
|
|
226
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
227
|
+
stream: true,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// First yield metadata
|
|
231
|
+
yield {
|
|
232
|
+
type: 'metadata',
|
|
233
|
+
mode,
|
|
234
|
+
sources: docs.map(d => ({
|
|
235
|
+
file: d.filename,
|
|
236
|
+
path: d.filePath,
|
|
237
|
+
kind: d.kind,
|
|
238
|
+
relevanceScore: d.relevanceScore,
|
|
239
|
+
})),
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Then stream the answer
|
|
243
|
+
for await (const event of stream) {
|
|
244
|
+
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
|
|
245
|
+
yield { type: 'text', text: event.delta.text };
|
|
246
|
+
}
|
|
247
|
+
if (event.type === 'message_stop') {
|
|
248
|
+
yield { type: 'done' };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Quick debug helper - answer a specific error without full context retrieval
|
|
255
|
+
*/
|
|
256
|
+
async debugError(errorMessage, stackTrace = '', options = {}) {
|
|
257
|
+
const question = `Why is this error happening and how do I fix it?
|
|
258
|
+
Error: ${errorMessage}
|
|
259
|
+
${stackTrace ? `Stack trace:\n${stackTrace}` : ''}`;
|
|
260
|
+
|
|
261
|
+
return this.query(question, { ...options, mode: QUERY_MODES.DEBUG });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Find all usages of a symbol across the codebase
|
|
266
|
+
*/
|
|
267
|
+
async findUsages(symbol, options = {}) {
|
|
268
|
+
return this.query(`Where is ${symbol} used across the codebase?`, {
|
|
269
|
+
...options,
|
|
270
|
+
mode: QUERY_MODES.USAGE,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Impact analysis for a proposed change
|
|
276
|
+
*/
|
|
277
|
+
async analyzeImpact(target, changeDescription = '', options = {}) {
|
|
278
|
+
const question = changeDescription
|
|
279
|
+
? `If I change ${target} to ${changeDescription}, what would break or be affected?`
|
|
280
|
+
: `If I change or remove ${target}, what would break?`;
|
|
281
|
+
|
|
282
|
+
return this.query(question, { ...options, mode: QUERY_MODES.IMPACT });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
module.exports = { QueryEngine: new QueryEngine(), QUERY_MODES, detectMode };
|