dev-mcp-server 0.0.2 → 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 +133 -5
- 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/api/routes/query.js
CHANGED
|
@@ -3,6 +3,11 @@ const router = express.Router();
|
|
|
3
3
|
const { QueryEngine, QUERY_MODES } = require('../../core/queryEngine');
|
|
4
4
|
const logger = require('../../utils/logger');
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* POST /api/query
|
|
8
|
+
* General question — auto-detects mode
|
|
9
|
+
* Body: { question, topK?, filter? }
|
|
10
|
+
*/
|
|
6
11
|
router.post('/', async (req, res) => {
|
|
7
12
|
const { question, topK, filter, mode } = req.body;
|
|
8
13
|
|
|
@@ -19,6 +24,11 @@ router.post('/', async (req, res) => {
|
|
|
19
24
|
}
|
|
20
25
|
});
|
|
21
26
|
|
|
27
|
+
/**
|
|
28
|
+
* POST /api/query/debug
|
|
29
|
+
* "Why is this failing?"
|
|
30
|
+
* Body: { error, stackTrace?, topK? }
|
|
31
|
+
*/
|
|
22
32
|
router.post('/debug', async (req, res) => {
|
|
23
33
|
const { error, stackTrace, question, topK } = req.body;
|
|
24
34
|
|
|
@@ -40,6 +50,11 @@ router.post('/debug', async (req, res) => {
|
|
|
40
50
|
}
|
|
41
51
|
});
|
|
42
52
|
|
|
53
|
+
/**
|
|
54
|
+
* POST /api/query/usage
|
|
55
|
+
* "Where is this used?"
|
|
56
|
+
* Body: { symbol, topK? }
|
|
57
|
+
*/
|
|
43
58
|
router.post('/usage', async (req, res) => {
|
|
44
59
|
const { symbol, topK } = req.body;
|
|
45
60
|
|
|
@@ -56,6 +71,11 @@ router.post('/usage', async (req, res) => {
|
|
|
56
71
|
}
|
|
57
72
|
});
|
|
58
73
|
|
|
74
|
+
/**
|
|
75
|
+
* POST /api/query/impact
|
|
76
|
+
* "If I change this, what breaks?"
|
|
77
|
+
* Body: { target, changeDescription?, topK? }
|
|
78
|
+
*/
|
|
59
79
|
router.post('/impact', async (req, res) => {
|
|
60
80
|
const { target, changeDescription, topK } = req.body;
|
|
61
81
|
|
|
@@ -72,6 +92,10 @@ router.post('/impact', async (req, res) => {
|
|
|
72
92
|
}
|
|
73
93
|
});
|
|
74
94
|
|
|
95
|
+
/**
|
|
96
|
+
* POST /api/query/stream
|
|
97
|
+
* Streaming version of general query (Server-Sent Events)
|
|
98
|
+
*/
|
|
75
99
|
router.post('/stream', async (req, res) => {
|
|
76
100
|
const { question, topK, mode } = req.body;
|
|
77
101
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const sessionManager = require('../../sessions/sessionManager');
|
|
4
|
+
|
|
5
|
+
/** GET /api/sessions — list all sessions */
|
|
6
|
+
router.get('/', (req, res) => {
|
|
7
|
+
res.json({ sessions: sessionManager.list() });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
/** POST /api/sessions — create a new session */
|
|
11
|
+
router.post('/', (req, res) => {
|
|
12
|
+
const session = sessionManager.create(req.body);
|
|
13
|
+
res.status(201).json({ success: true, session });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/** GET /api/sessions/:id — get / resume a session */
|
|
17
|
+
router.get('/:id', (req, res) => {
|
|
18
|
+
try {
|
|
19
|
+
const session = sessionManager.resume(req.params.id);
|
|
20
|
+
res.json(session);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
res.status(404).json({ error: err.message });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/** POST /api/sessions/:id/messages — append a message */
|
|
27
|
+
router.post('/:id/messages', (req, res) => {
|
|
28
|
+
try {
|
|
29
|
+
const session = sessionManager.addMessage(req.params.id, req.body);
|
|
30
|
+
res.json({ success: true, messageCount: session.messages.length });
|
|
31
|
+
} catch (err) {
|
|
32
|
+
res.status(404).json({ error: err.message });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** GET /api/sessions/:id/export — export as markdown */
|
|
37
|
+
router.get('/:id/export', (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const md = sessionManager.exportMarkdown(req.params.id);
|
|
40
|
+
res.setHeader('Content-Type', 'text/markdown');
|
|
41
|
+
res.setHeader('Content-Disposition', `attachment; filename="session-${req.params.id}.md"`);
|
|
42
|
+
res.send(md);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
res.status(404).json({ error: err.message });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/** DELETE /api/sessions/:id */
|
|
49
|
+
router.delete('/:id', (req, res) => {
|
|
50
|
+
const deleted = sessionManager.delete(req.params.id);
|
|
51
|
+
res.json({ success: deleted });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
module.exports = router;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { TaskManager, STATUS, PRIORITY } = require('../../tasks/taskManager');
|
|
4
|
+
|
|
5
|
+
/** GET /api/tasks */
|
|
6
|
+
router.get('/', (req, res) => {
|
|
7
|
+
const { status, priority, tags, assignee, includeDone } = req.query;
|
|
8
|
+
const tasks = TaskManager.list({
|
|
9
|
+
status, priority,
|
|
10
|
+
tags: tags ? tags.split(',') : undefined,
|
|
11
|
+
assignee,
|
|
12
|
+
includeDone: includeDone === 'true',
|
|
13
|
+
});
|
|
14
|
+
res.json({ tasks, stats: TaskManager.getStats() });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/** POST /api/tasks */
|
|
18
|
+
router.post('/', (req, res) => {
|
|
19
|
+
try {
|
|
20
|
+
const task = TaskManager.create(req.body);
|
|
21
|
+
res.status(201).json({ success: true, task });
|
|
22
|
+
} catch (err) {
|
|
23
|
+
res.status(400).json({ error: err.message });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/** GET /api/tasks/:id */
|
|
28
|
+
router.get('/:id', (req, res) => {
|
|
29
|
+
const task = TaskManager.get(parseInt(req.params.id));
|
|
30
|
+
if (!task) return res.status(404).json({ error: 'Task not found' });
|
|
31
|
+
res.json(task);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** PATCH /api/tasks/:id */
|
|
35
|
+
router.patch('/:id', (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
const task = TaskManager.update(parseInt(req.params.id), req.body);
|
|
38
|
+
res.json({ success: true, task });
|
|
39
|
+
} catch (err) {
|
|
40
|
+
res.status(400).json({ error: err.message });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/** POST /api/tasks/:id/notes */
|
|
45
|
+
router.post('/:id/notes', (req, res) => {
|
|
46
|
+
const { note } = req.body;
|
|
47
|
+
if (!note) return res.status(400).json({ error: 'note is required' });
|
|
48
|
+
try {
|
|
49
|
+
const task = TaskManager.addNote(parseInt(req.params.id), note);
|
|
50
|
+
res.json({ success: true, task });
|
|
51
|
+
} catch (err) {
|
|
52
|
+
res.status(400).json({ error: err.message });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/** DELETE /api/tasks/:id */
|
|
57
|
+
router.delete('/:id', (req, res) => {
|
|
58
|
+
const deleted = TaskManager.delete(parseInt(req.params.id));
|
|
59
|
+
res.json({ success: deleted });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/** GET /api/tasks/meta/constants */
|
|
63
|
+
router.get('/meta/constants', (req, res) => {
|
|
64
|
+
res.json({ STATUS, PRIORITY });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
module.exports = router;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const BashTool = require('../../tools/BashTool');
|
|
4
|
+
const GrepTool = require('../../tools/GrepTool');
|
|
5
|
+
const logger = require('../../utils/logger');
|
|
6
|
+
|
|
7
|
+
/** POST /api/tools/bash — execute a shell command */
|
|
8
|
+
router.post('/bash', async (req, res) => {
|
|
9
|
+
const { command, cwd, timeout, approved } = req.body;
|
|
10
|
+
if (!command) return res.status(400).json({ error: 'command is required' });
|
|
11
|
+
|
|
12
|
+
const permission = BashTool.checkPermission(command);
|
|
13
|
+
if (permission === 'dangerous') {
|
|
14
|
+
return res.status(403).json({ error: 'Command blocked: dangerous pattern detected', permission });
|
|
15
|
+
}
|
|
16
|
+
if (permission === 'needs-approval' && !approved) {
|
|
17
|
+
return res.status(202).json({ needsApproval: true, command, permission,
|
|
18
|
+
message: 'Set approved:true in body to execute, or grant permission via /api/tools/bash/permit' });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const result = await BashTool.execute(command, { cwd, timeout, approved: true });
|
|
23
|
+
res.json(result);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
res.status(500).json({ error: err.message });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/** POST /api/tools/bash/permit — grant permission for a command */
|
|
30
|
+
router.post('/bash/permit', (req, res) => {
|
|
31
|
+
const { command, level } = req.body;
|
|
32
|
+
if (!command) return res.status(400).json({ error: 'command is required' });
|
|
33
|
+
BashTool.grantPermission(command, level || 'session');
|
|
34
|
+
res.json({ success: true, command, level: level || 'session' });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/** GET /api/tools/grep — search codebase */
|
|
38
|
+
router.get('/grep', async (req, res) => {
|
|
39
|
+
const { pattern, cwd, glob, ignoreCase, maxResults, contextLines, literal } = req.query;
|
|
40
|
+
if (!pattern) return res.status(400).json({ error: 'pattern is required' });
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const result = await GrepTool.search(pattern, {
|
|
44
|
+
cwd, glob, ignoreCase: ignoreCase === 'true',
|
|
45
|
+
maxResults: parseInt(maxResults) || 50,
|
|
46
|
+
contextLines: parseInt(contextLines) || 2,
|
|
47
|
+
literal: literal === 'true',
|
|
48
|
+
});
|
|
49
|
+
res.json(result);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
res.status(500).json({ error: err.message });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/** GET /api/tools/grep/definitions/:symbol — find symbol definitions */
|
|
56
|
+
router.get('/grep/definitions/:symbol', async (req, res) => {
|
|
57
|
+
try {
|
|
58
|
+
const matches = await GrepTool.findDefinitions(req.params.symbol, req.query.cwd);
|
|
59
|
+
res.json({ symbol: req.params.symbol, matches, count: matches.length });
|
|
60
|
+
} catch (err) {
|
|
61
|
+
res.status(500).json({ error: err.message });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
/** GET /api/tools/grep/imports/:module — find all imports of a module */
|
|
66
|
+
router.get('/grep/imports/:module', async (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const result = await GrepTool.findImports(req.params.module, req.query.cwd);
|
|
69
|
+
res.json(result);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
res.status(500).json({ error: err.message });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/** GET /api/tools/grep/todos — find all TODO/FIXME comments */
|
|
76
|
+
router.get('/grep/todos', async (req, res) => {
|
|
77
|
+
try {
|
|
78
|
+
const result = await GrepTool.findTodos(req.query.cwd);
|
|
79
|
+
res.json(result);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
res.status(500).json({ error: err.message });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
module.exports = router;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const logger = require('../../utils/logger');
|
|
4
|
+
|
|
5
|
+
// ── PLUGINS ───────────────────────────────────────────────────────────────────
|
|
6
|
+
const pluginsRouter = express.Router();
|
|
7
|
+
const pluginManager = require('../../plugins/pluginManager');
|
|
8
|
+
|
|
9
|
+
pluginsRouter.get('/', (req, res) => res.json({ plugins: pluginManager.list(), stats: pluginManager.getStats() }));
|
|
10
|
+
pluginsRouter.get('/:name', (req, res) => {
|
|
11
|
+
const p = pluginManager.get(req.params.name);
|
|
12
|
+
if (!p) return res.status(404).json({ error: 'Plugin not found' });
|
|
13
|
+
res.json(p);
|
|
14
|
+
});
|
|
15
|
+
pluginsRouter.post('/:name/enable', (req, res) => {
|
|
16
|
+
try { res.json(pluginManager.enable(req.params.name)); }
|
|
17
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
18
|
+
});
|
|
19
|
+
pluginsRouter.post('/:name/disable', (req, res) => {
|
|
20
|
+
try { res.json(pluginManager.disable(req.params.name)); }
|
|
21
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
module.exports.pluginsRouter = pluginsRouter;
|
|
25
|
+
|
|
26
|
+
// ── WORKTREES ─────────────────────────────────────────────────────────────────
|
|
27
|
+
const worktreesRouter = express.Router();
|
|
28
|
+
const wm = require('../../worktrees/worktreeManager');
|
|
29
|
+
|
|
30
|
+
worktreesRouter.get('/', async (req, res) => {
|
|
31
|
+
try { res.json({ worktrees: await wm.list(req.query.cwd), stats: wm.getStats() }); }
|
|
32
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
33
|
+
});
|
|
34
|
+
worktreesRouter.post('/', async (req, res) => {
|
|
35
|
+
const { name, branch, cwd, createBranch } = req.body;
|
|
36
|
+
if (!name || !branch) return res.status(400).json({ error: 'name and branch required' });
|
|
37
|
+
try { res.status(201).json(await wm.create(name, branch, { cwd, createBranch })); }
|
|
38
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
39
|
+
});
|
|
40
|
+
worktreesRouter.get('/:name', (req, res) => {
|
|
41
|
+
const wt = wm.get(req.params.name);
|
|
42
|
+
if (!wt) return res.status(404).json({ error: 'Worktree not found' });
|
|
43
|
+
res.json(wt);
|
|
44
|
+
});
|
|
45
|
+
worktreesRouter.get('/:name/diff', async (req, res) => {
|
|
46
|
+
try { res.json(await wm.diff(req.params.name)); }
|
|
47
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
48
|
+
});
|
|
49
|
+
worktreesRouter.get('/:name/log', async (req, res) => {
|
|
50
|
+
try { res.json({ log: await wm.log(req.params.name, req.query.limit) }); }
|
|
51
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
52
|
+
});
|
|
53
|
+
worktreesRouter.post('/:name/commit', async (req, res) => {
|
|
54
|
+
try { res.json(await wm.commit(req.params.name, req.body.message)); }
|
|
55
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
56
|
+
});
|
|
57
|
+
worktreesRouter.delete('/:name', async (req, res) => {
|
|
58
|
+
try { res.json(await wm.remove(req.params.name, { force: req.query.force === 'true' })); }
|
|
59
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
module.exports.worktreesRouter = worktreesRouter;
|
|
63
|
+
|
|
64
|
+
// ── CRON ──────────────────────────────────────────────────────────────────────
|
|
65
|
+
const cronRouter = express.Router();
|
|
66
|
+
const cron = require('../../cron/cronScheduler');
|
|
67
|
+
|
|
68
|
+
cronRouter.get('/', (req, res) => res.json({ jobs: cron.list(), stats: cron.getStats() }));
|
|
69
|
+
cronRouter.post('/', (req, res) => {
|
|
70
|
+
try { res.status(201).json(cron.create(req.body)); }
|
|
71
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
72
|
+
});
|
|
73
|
+
cronRouter.get('/:name', (req, res) => {
|
|
74
|
+
const job = cron.list().find(j => j.name === req.params.name);
|
|
75
|
+
if (!job) return res.status(404).json({ error: 'Job not found' });
|
|
76
|
+
res.json(job);
|
|
77
|
+
});
|
|
78
|
+
cronRouter.patch('/:name', (req, res) => {
|
|
79
|
+
try { res.json(cron.update(req.params.name, req.body)); }
|
|
80
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
81
|
+
});
|
|
82
|
+
cronRouter.post('/:name/run', async (req, res) => {
|
|
83
|
+
try { res.json(await cron.runNow(req.params.name)); }
|
|
84
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
85
|
+
});
|
|
86
|
+
cronRouter.get('/:name/history', (req, res) => {
|
|
87
|
+
res.json({ runs: cron.getRunHistory(req.params.name, req.query.limit) });
|
|
88
|
+
});
|
|
89
|
+
cronRouter.delete('/:name', (req, res) => {
|
|
90
|
+
try { res.json({ success: cron.delete(req.params.name) }); }
|
|
91
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
module.exports.cronRouter = cronRouter;
|
|
95
|
+
|
|
96
|
+
// ── MESSAGING ─────────────────────────────────────────────────────────────────
|
|
97
|
+
const messagesRouter = express.Router();
|
|
98
|
+
const { MessageBus, PRIORITY } = require('../../messaging/messageBus');
|
|
99
|
+
|
|
100
|
+
messagesRouter.post('/', (req, res) => {
|
|
101
|
+
try { res.status(201).json(MessageBus.send(req.body)); }
|
|
102
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
103
|
+
});
|
|
104
|
+
messagesRouter.post('/broadcast', (req, res) => {
|
|
105
|
+
try { res.json({ messages: MessageBus.broadcast(req.body) }); }
|
|
106
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
107
|
+
});
|
|
108
|
+
messagesRouter.get('/inbox/:agent', (req, res) => {
|
|
109
|
+
res.json({
|
|
110
|
+
messages: MessageBus.inbox(req.params.agent, {
|
|
111
|
+
unreadOnly: req.query.unread === 'true',
|
|
112
|
+
priority: req.query.priority,
|
|
113
|
+
limit: parseInt(req.query.limit) || 50,
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
messagesRouter.get('/sent/:agent', (req, res) => {
|
|
118
|
+
res.json({ messages: MessageBus.sentBy(req.params.agent) });
|
|
119
|
+
});
|
|
120
|
+
messagesRouter.post('/:id/read', (req, res) => {
|
|
121
|
+
try { res.json(MessageBus.markRead(parseInt(req.params.id))); }
|
|
122
|
+
catch (e) { res.status(404).json({ error: e.message }); }
|
|
123
|
+
});
|
|
124
|
+
messagesRouter.post('/inbox/:agent/read-all', (req, res) => {
|
|
125
|
+
res.json({ read: MessageBus.markAllRead(req.params.agent) });
|
|
126
|
+
});
|
|
127
|
+
messagesRouter.post('/:id/reply', (req, res) => {
|
|
128
|
+
try { res.json(MessageBus.reply(parseInt(req.params.id), req.body)); }
|
|
129
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
130
|
+
});
|
|
131
|
+
messagesRouter.delete('/:id', (req, res) => {
|
|
132
|
+
res.json({ success: MessageBus.delete(parseInt(req.params.id)) });
|
|
133
|
+
});
|
|
134
|
+
messagesRouter.get('/stats/overview', (req, res) => {
|
|
135
|
+
res.json({ stats: MessageBus.getStats(), priorities: PRIORITY });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
module.exports.messagesRouter = messagesRouter;
|
|
139
|
+
|
|
140
|
+
// ── TEAM MEMORY ───────────────────────────────────────────────────────────────
|
|
141
|
+
const teamMemRouter = express.Router();
|
|
142
|
+
const teamMemory = require('../../memory/teamMemory');
|
|
143
|
+
|
|
144
|
+
teamMemRouter.get('/', (req, res) => {
|
|
145
|
+
const entries = teamMemory.get(req.query.team || 'global', {
|
|
146
|
+
type: req.query.type,
|
|
147
|
+
limit: parseInt(req.query.limit) || 50,
|
|
148
|
+
});
|
|
149
|
+
res.json({ entries, stats: teamMemory.getStats() });
|
|
150
|
+
});
|
|
151
|
+
teamMemRouter.post('/', (req, res) => {
|
|
152
|
+
try { res.status(201).json(teamMemory.add(req.body)); }
|
|
153
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
154
|
+
});
|
|
155
|
+
teamMemRouter.get('/search', (req, res) => {
|
|
156
|
+
if (!req.query.q) return res.status(400).json({ error: 'q required' });
|
|
157
|
+
res.json({ results: teamMemory.search(req.query.q, req.query.team) });
|
|
158
|
+
});
|
|
159
|
+
teamMemRouter.get('/teams', (req, res) => {
|
|
160
|
+
res.json({ teams: teamMemory.listTeams() });
|
|
161
|
+
});
|
|
162
|
+
teamMemRouter.delete('/:id', (req, res) => {
|
|
163
|
+
res.json({ success: teamMemory.delete(req.params.id) });
|
|
164
|
+
});
|
|
165
|
+
teamMemRouter.delete('/', (req, res) => {
|
|
166
|
+
if (!req.query.team) return res.status(400).json({ error: 'team query param required' });
|
|
167
|
+
teamMemory.clearTeam(req.query.team);
|
|
168
|
+
res.json({ success: true });
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
module.exports.teamMemRouter = teamMemRouter;
|
|
172
|
+
|
|
173
|
+
// ── CONTEXT VISUALIZER ────────────────────────────────────────────────────────
|
|
174
|
+
const contextVizRouter = express.Router();
|
|
175
|
+
const contextVisualizer = require('../../context/contextVisualizer');
|
|
176
|
+
|
|
177
|
+
contextVizRouter.post('/visualize', (req, res) => {
|
|
178
|
+
const { query, mode, topK, team } = req.body;
|
|
179
|
+
if (!query) return res.status(400).json({ error: 'query required' });
|
|
180
|
+
try {
|
|
181
|
+
const viz = contextVisualizer.visualize(query, { mode, topK, team });
|
|
182
|
+
res.json(viz);
|
|
183
|
+
} catch (e) { res.status(500).json({ error: e.message }); }
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
contextVizRouter.post('/visualize/text', (req, res) => {
|
|
187
|
+
const { query, mode, topK } = req.body;
|
|
188
|
+
if (!query) return res.status(400).json({ error: 'query required' });
|
|
189
|
+
try {
|
|
190
|
+
const viz = contextVisualizer.visualize(query, { mode, topK });
|
|
191
|
+
const text = contextVisualizer.format(viz);
|
|
192
|
+
res.type('text/plain').send(text);
|
|
193
|
+
} catch (e) { res.status(500).json({ error: e.message }); }
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
module.exports.contextVizRouter = contextVizRouter;
|
package/src/api/server.js
CHANGED
|
@@ -1,29 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
require('dotenv').config({ path: path.resolve(process.cwd(), '.env') });
|
|
1
|
+
require('dotenv').config();
|
|
3
2
|
const express = require('express');
|
|
4
3
|
const cors = require('cors');
|
|
5
4
|
const morgan = require('morgan');
|
|
6
5
|
const logger = require('../utils/logger');
|
|
7
|
-
const
|
|
6
|
+
const dreamer = require('../agents/dreamer');
|
|
7
|
+
const improver = require('../agents/improver');
|
|
8
|
+
const cronScheduler = require('../cron/cronScheduler');
|
|
9
|
+
const pluginManager = require('../plugins/pluginManager');
|
|
10
|
+
const llm = require('../utils/llmClient');
|
|
8
11
|
const store = require('../storage/store');
|
|
9
12
|
|
|
13
|
+
// Routes
|
|
10
14
|
const ingestRoutes = require('./routes/ingest');
|
|
11
15
|
const queryRoutes = require('./routes/query');
|
|
12
16
|
const knowledgeRoutes = require('./routes/knowledge');
|
|
17
|
+
const gitRoutes = require('./routes/git');
|
|
18
|
+
const toolsRoutes = require('./routes/tools');
|
|
19
|
+
const memoryRoutes = require('./routes/memory');
|
|
20
|
+
const tasksRoutes = require('./routes/tasks');
|
|
21
|
+
const sessionsRoutes = require('./routes/sessions');
|
|
22
|
+
const plannerRoutes = require('./routes/planner');
|
|
23
|
+
const agentsRoutes = require('./routes/agents');
|
|
24
|
+
const pipelinesRoutes = require('./routes/pipelines');
|
|
25
|
+
const { skillsRouter, lspRouter, filesRouter, monitorRouter, convRouter, watcherRouter } = require('./routes/newRoutes');
|
|
26
|
+
const { toolsRegistryRouter, promptsRouter, compactorRouter } = require('./routes/extras');
|
|
27
|
+
const { pluginsRouter, worktreesRouter, cronRouter, messagesRouter, teamMemRouter, contextVizRouter } = require('./routes/v5routes');
|
|
13
28
|
|
|
14
29
|
const app = express();
|
|
15
30
|
const PORT = process.env.PORT || 3000;
|
|
16
31
|
|
|
32
|
+
// ── Middleware ────────────────────────────────────────────────
|
|
17
33
|
app.use(cors());
|
|
18
34
|
app.use(express.json({ limit: '10mb' }));
|
|
19
35
|
app.use(morgan('dev', {
|
|
20
36
|
stream: { write: (msg) => logger.info(msg.trim()) },
|
|
21
37
|
}));
|
|
22
38
|
|
|
39
|
+
// ── Routes ────────────────────────────────────────────────────
|
|
23
40
|
app.use('/api/ingest', ingestRoutes);
|
|
24
41
|
app.use('/api/query', queryRoutes);
|
|
25
42
|
app.use('/api/knowledge', knowledgeRoutes);
|
|
43
|
+
app.use('/api/git', gitRoutes);
|
|
44
|
+
app.use('/api/tools', toolsRoutes);
|
|
45
|
+
app.use('/api/memory', memoryRoutes);
|
|
46
|
+
app.use('/api/tasks', tasksRoutes);
|
|
47
|
+
app.use('/api/sessions', sessionsRoutes);
|
|
48
|
+
app.use('/api', plannerRoutes); // /api/plan, /api/cost
|
|
49
|
+
app.use('/api/agents', agentsRoutes);
|
|
50
|
+
app.use('/api/pipelines', pipelinesRoutes);
|
|
51
|
+
app.use('/api/skills', skillsRouter);
|
|
52
|
+
app.use('/api/lsp', lspRouter);
|
|
53
|
+
app.use('/api/files', filesRouter);
|
|
54
|
+
app.use('/api/monitor', monitorRouter);
|
|
55
|
+
app.use('/api/chat', convRouter);
|
|
56
|
+
app.use('/api/watcher', watcherRouter);
|
|
57
|
+
app.use('/api/registry', toolsRegistryRouter);
|
|
58
|
+
app.use('/api/prompts', promptsRouter);
|
|
59
|
+
app.use('/api/compact', compactorRouter);
|
|
60
|
+
app.use('/api/plugins', pluginsRouter);
|
|
61
|
+
app.use('/api/worktrees', worktreesRouter);
|
|
62
|
+
app.use('/api/cron', cronRouter);
|
|
63
|
+
app.use('/api/messages', messagesRouter);
|
|
64
|
+
app.use('/api/team-memory', teamMemRouter);
|
|
65
|
+
app.use('/api/context', contextVizRouter);
|
|
66
|
+
|
|
67
|
+
// ── Dashboard (single-file HTML UI) ──────────────────────────────────────────
|
|
68
|
+
const path = require('path');
|
|
69
|
+
app.get('/dashboard', (req, res) => {
|
|
70
|
+
res.sendFile(path.join(__dirname, '../dashboard/index.html'));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ── LLM provider info ────────────────────────────────────────────────────────
|
|
74
|
+
app.get('/api/llm/info', (req, res) => res.json(llm.getInfo()));
|
|
26
75
|
|
|
76
|
+
// ── Health check ──────────────────────────────────────────────
|
|
27
77
|
app.get('/health', (req, res) => {
|
|
28
78
|
const stats = store.getStats();
|
|
29
79
|
res.json({
|
|
@@ -34,11 +84,12 @@ app.get('/health', (req, res) => {
|
|
|
34
84
|
});
|
|
35
85
|
});
|
|
36
86
|
|
|
87
|
+
// ── Root info ─────────────────────────────────────────────────
|
|
37
88
|
app.get('/', (req, res) => {
|
|
38
89
|
res.json({
|
|
39
90
|
name: 'Dev MCP Server — Model Context Platform',
|
|
40
91
|
version: '1.0.0',
|
|
41
|
-
description: '
|
|
92
|
+
description: '45 tools · 10 agents · 10 teams · Dreamer · Compactor · Prompt Engineering · Plugins · Cron · Worktrees · Messaging',
|
|
42
93
|
endpoints: {
|
|
43
94
|
health: 'GET /health',
|
|
44
95
|
ingest: {
|
|
@@ -61,31 +112,108 @@ app.get('/', (req, res) => {
|
|
|
61
112
|
files: 'GET /api/knowledge/files',
|
|
62
113
|
rebuild: 'POST /api/knowledge/rebuild',
|
|
63
114
|
},
|
|
115
|
+
git: {
|
|
116
|
+
status: 'GET /api/git/status',
|
|
117
|
+
diff: 'GET /api/git/diff',
|
|
118
|
+
commit: 'POST /api/git/commit',
|
|
119
|
+
review: 'POST /api/git/review',
|
|
120
|
+
log: 'GET /api/git/log',
|
|
121
|
+
branches: 'GET /api/git/branches',
|
|
122
|
+
},
|
|
123
|
+
tools: {
|
|
124
|
+
bash: 'POST /api/tools/bash',
|
|
125
|
+
bashPermit: 'POST /api/tools/bash/permit',
|
|
126
|
+
grep: 'GET /api/tools/grep?pattern=<pattern>',
|
|
127
|
+
definitions: 'GET /api/tools/grep/definitions/:symbol',
|
|
128
|
+
imports: 'GET /api/tools/grep/imports/:module',
|
|
129
|
+
todos: 'GET /api/tools/grep/todos',
|
|
130
|
+
},
|
|
131
|
+
memory: {
|
|
132
|
+
list: 'GET /api/memory',
|
|
133
|
+
add: 'POST /api/memory',
|
|
134
|
+
delete: 'DELETE /api/memory/:id',
|
|
135
|
+
clear: 'DELETE /api/memory',
|
|
136
|
+
},
|
|
137
|
+
tasks: {
|
|
138
|
+
list: 'GET /api/tasks',
|
|
139
|
+
create: 'POST /api/tasks',
|
|
140
|
+
get: 'GET /api/tasks/:id',
|
|
141
|
+
update: 'PATCH /api/tasks/:id',
|
|
142
|
+
addNote: 'POST /api/tasks/:id/notes',
|
|
143
|
+
delete: 'DELETE /api/tasks/:id',
|
|
144
|
+
},
|
|
145
|
+
sessions: {
|
|
146
|
+
list: 'GET /api/sessions',
|
|
147
|
+
create: 'POST /api/sessions',
|
|
148
|
+
resume: 'GET /api/sessions/:id',
|
|
149
|
+
addMessage: 'POST /api/sessions/:id/messages',
|
|
150
|
+
export: 'GET /api/sessions/:id/export',
|
|
151
|
+
delete: 'DELETE /api/sessions/:id',
|
|
152
|
+
},
|
|
153
|
+
planner: {
|
|
154
|
+
plan: 'POST /api/plan',
|
|
155
|
+
compact: 'POST /api/plan/compact',
|
|
156
|
+
doctor: 'GET /api/plan/doctor',
|
|
157
|
+
cost: 'GET /api/cost',
|
|
158
|
+
},
|
|
64
159
|
},
|
|
65
160
|
});
|
|
66
161
|
});
|
|
67
162
|
|
|
163
|
+
// ── 404 ───────────────────────────────────────────────────────
|
|
68
164
|
app.use((req, res) => {
|
|
69
165
|
res.status(404).json({ error: `Route not found: ${req.method} ${req.path}` });
|
|
70
166
|
});
|
|
71
167
|
|
|
168
|
+
// ── Error handler ─────────────────────────────────────────────
|
|
72
169
|
app.use((err, req, res, next) => {
|
|
73
170
|
logger.error(`Unhandled error: ${err.message}`);
|
|
74
171
|
res.status(500).json({ error: 'Internal server error', detail: err.message });
|
|
75
172
|
});
|
|
76
173
|
|
|
77
|
-
|
|
174
|
+
// ── Boot ──────────────────────────────────────────────────────
|
|
175
|
+
app.listen(PORT, async () => {
|
|
78
176
|
logger.info(`🚀 Dev MCP Server running on http://localhost:${PORT}`);
|
|
79
177
|
logger.info(`📚 Knowledge base: ${store.getStats().totalDocs} documents`);
|
|
80
178
|
|
|
179
|
+
// Auto-build index if data exists
|
|
81
180
|
const stats = store.getStats();
|
|
82
181
|
if (stats.totalDocs > 0) {
|
|
83
182
|
logger.info('🔍 Rebuilding search index...');
|
|
183
|
+
const indexer = require('../core/indexer');
|
|
84
184
|
const count = indexer.build();
|
|
85
185
|
logger.info(`✅ Index ready: ${count} documents`);
|
|
86
186
|
} else {
|
|
87
187
|
logger.info('📭 Knowledge base is empty — run: node cli.js ingest <path>');
|
|
88
188
|
}
|
|
189
|
+
|
|
190
|
+
// Start the Dreamer for background cognitive work
|
|
191
|
+
if (process.env.ENABLE_DREAMER !== 'false') {
|
|
192
|
+
const intervalMin = parseInt(process.env.DREAM_INTERVAL_MINUTES) || 30;
|
|
193
|
+
dreamer.start(intervalMin);
|
|
194
|
+
logger.info(`💤 Dreamer started (every ${intervalMin} min)`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Start Cron Scheduler
|
|
198
|
+
if (process.env.ENABLE_CRON !== 'false') {
|
|
199
|
+
cronScheduler.start();
|
|
200
|
+
logger.info(`⏰ Cron scheduler started`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Load Plugins
|
|
204
|
+
const toolRegistry = require('../tools/registry');
|
|
205
|
+
await pluginManager.loadAll(app, {
|
|
206
|
+
toolRegistry,
|
|
207
|
+
store,
|
|
208
|
+
indexer: require('../core/indexer'),
|
|
209
|
+
memoryManager: require('../memory/memoryManager').MemoryManager,
|
|
210
|
+
});
|
|
211
|
+
logger.info(`🔌 Plugins: ${pluginManager.getStats().enabled} enabled`);
|
|
212
|
+
if (process.env.ENABLE_MONITOR !== 'false') {
|
|
213
|
+
const { ProactiveMonitor } = require('../monitor/proactiveMonitor');
|
|
214
|
+
ProactiveMonitor.start(process.cwd());
|
|
215
|
+
logger.info(`👁️ Proactive monitor started`);
|
|
216
|
+
}
|
|
89
217
|
});
|
|
90
218
|
|
|
91
219
|
module.exports = app;
|