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
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const GitTool = require('../../tools/GitTool');
|
|
4
|
+
const logger = require('../../utils/logger');
|
|
5
|
+
|
|
6
|
+
/** GET /api/git/status */
|
|
7
|
+
router.get('/status', async (req, res) => {
|
|
8
|
+
const { cwd } = req.query;
|
|
9
|
+
try {
|
|
10
|
+
const status = await GitTool.status(cwd);
|
|
11
|
+
res.json(status);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
res.status(500).json({ error: err.message });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/** GET /api/git/diff */
|
|
18
|
+
router.get('/diff', async (req, res) => {
|
|
19
|
+
const { cwd, staged, file, stat } = req.query;
|
|
20
|
+
try {
|
|
21
|
+
const result = await GitTool.diff({ cwd, staged: staged === 'true', file, stat: stat === 'true' });
|
|
22
|
+
res.json(result);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
res.status(500).json({ error: err.message });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** POST /api/git/commit */
|
|
29
|
+
router.post('/commit', async (req, res) => {
|
|
30
|
+
const { cwd, files, message, autoMessage } = req.body;
|
|
31
|
+
try {
|
|
32
|
+
const result = await GitTool.commit({ cwd, files, message, autoMessage });
|
|
33
|
+
res.json(result);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
res.status(500).json({ error: err.message });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/** POST /api/git/review */
|
|
40
|
+
router.post('/review', async (req, res) => {
|
|
41
|
+
const { cwd, staged, file, focus } = req.body;
|
|
42
|
+
try {
|
|
43
|
+
const result = await GitTool.review({ cwd, staged, file, focus });
|
|
44
|
+
res.json(result);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
res.status(500).json({ error: err.message });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/** GET /api/git/log */
|
|
51
|
+
router.get('/log', async (req, res) => {
|
|
52
|
+
const { cwd, limit, file, oneline } = req.query;
|
|
53
|
+
try {
|
|
54
|
+
const result = await GitTool.log({ cwd, limit: parseInt(limit) || 10, file, oneline: oneline === 'true' });
|
|
55
|
+
res.json({ commits: result });
|
|
56
|
+
} catch (err) {
|
|
57
|
+
res.status(500).json({ error: err.message });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/** GET /api/git/branches */
|
|
62
|
+
router.get('/branches', async (req, res) => {
|
|
63
|
+
const { cwd } = req.query;
|
|
64
|
+
try {
|
|
65
|
+
const result = await GitTool.branches(cwd);
|
|
66
|
+
res.json({ branches: result });
|
|
67
|
+
} catch (err) {
|
|
68
|
+
res.status(500).json({ error: err.message });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
module.exports = router;
|
package/src/api/routes/ingest.js
CHANGED
|
@@ -5,65 +5,85 @@ const indexer = require('../../core/indexer');
|
|
|
5
5
|
const store = require('../../storage/store');
|
|
6
6
|
const logger = require('../../utils/logger');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* POST /api/ingest/file
|
|
10
|
+
* Ingest a single file by path
|
|
11
|
+
*/
|
|
8
12
|
router.post('/file', async (req, res) => {
|
|
9
|
-
|
|
13
|
+
const { filePath } = req.body;
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
if (!filePath) {
|
|
16
|
+
return res.status(400).json({ error: 'filePath is required' });
|
|
17
|
+
}
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
try {
|
|
20
|
+
const result = await ingester.ingestFile(filePath);
|
|
21
|
+
indexer.build();
|
|
22
|
+
res.json({ success: true, result });
|
|
23
|
+
} catch (err) {
|
|
24
|
+
logger.error(`Ingest file error: ${err.message}`);
|
|
25
|
+
res.status(500).json({ error: err.message });
|
|
26
|
+
}
|
|
23
27
|
});
|
|
24
28
|
|
|
29
|
+
/**
|
|
30
|
+
* POST /api/ingest/directory
|
|
31
|
+
* Ingest all supported files in a directory
|
|
32
|
+
*/
|
|
25
33
|
router.post('/directory', async (req, res) => {
|
|
26
|
-
|
|
34
|
+
const { dirPath } = req.body;
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
if (!dirPath) {
|
|
37
|
+
return res.status(400).json({ error: 'dirPath is required' });
|
|
38
|
+
}
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
try {
|
|
41
|
+
const result = await ingester.ingestDirectory(dirPath);
|
|
42
|
+
res.json({ success: true, result });
|
|
43
|
+
} catch (err) {
|
|
44
|
+
logger.error(`Ingest directory error: ${err.message}`);
|
|
45
|
+
res.status(500).json({ error: err.message });
|
|
46
|
+
}
|
|
39
47
|
});
|
|
40
48
|
|
|
49
|
+
/**
|
|
50
|
+
* POST /api/ingest/raw
|
|
51
|
+
* Ingest raw text (error logs, bug descriptions, API responses)
|
|
52
|
+
*/
|
|
41
53
|
router.post('/raw', async (req, res) => {
|
|
42
|
-
|
|
54
|
+
const { content, kind, label, tags } = req.body;
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
if (!content) {
|
|
57
|
+
return res.status(400).json({ error: 'content is required' });
|
|
58
|
+
}
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
try {
|
|
61
|
+
const result = await ingester.ingestRawText(content, { kind, label, tags });
|
|
62
|
+
indexer.build();
|
|
63
|
+
res.json({ success: true, result });
|
|
64
|
+
} catch (err) {
|
|
65
|
+
logger.error(`Ingest raw error: ${err.message}`);
|
|
66
|
+
res.status(500).json({ error: err.message });
|
|
67
|
+
}
|
|
56
68
|
});
|
|
57
69
|
|
|
70
|
+
/**
|
|
71
|
+
* DELETE /api/ingest/clear
|
|
72
|
+
* Clear the entire knowledge base
|
|
73
|
+
*/
|
|
58
74
|
router.delete('/clear', (req, res) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
store.clear();
|
|
76
|
+
indexer.invalidate();
|
|
77
|
+
res.json({ success: true, message: 'Knowledge base cleared' });
|
|
62
78
|
});
|
|
63
79
|
|
|
80
|
+
/**
|
|
81
|
+
* GET /api/ingest/files
|
|
82
|
+
* List all ingested files
|
|
83
|
+
*/
|
|
64
84
|
router.get('/files', (req, res) => {
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
const files = store.getIngestedFiles();
|
|
86
|
+
res.json({ files, count: files.length });
|
|
67
87
|
});
|
|
68
88
|
|
|
69
89
|
module.exports = router;
|
|
@@ -3,63 +3,81 @@ const router = express.Router();
|
|
|
3
3
|
const store = require('../../storage/store');
|
|
4
4
|
const indexer = require('../../core/indexer');
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* GET /api/knowledge/stats
|
|
8
|
+
* Get knowledge base statistics
|
|
9
|
+
*/
|
|
6
10
|
router.get('/stats', (req, res) => {
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
const stats = store.getStats();
|
|
12
|
+
res.json(stats);
|
|
9
13
|
});
|
|
10
14
|
|
|
15
|
+
/**
|
|
16
|
+
* GET /api/knowledge/search
|
|
17
|
+
* Raw TF-IDF search (no AI, just retrieval)
|
|
18
|
+
* Query params: q, topK, kind
|
|
19
|
+
*/
|
|
11
20
|
router.get('/search', (req, res) => {
|
|
12
|
-
|
|
21
|
+
const { q, topK = '8', kind } = req.query;
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
if (!q) {
|
|
24
|
+
return res.status(400).json({ error: 'q query parameter is required' });
|
|
25
|
+
}
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
const filter = kind ? { kind } : {};
|
|
28
|
+
const results = indexer.search(q, parseInt(topK), filter);
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
res.json({
|
|
31
|
+
query: q,
|
|
32
|
+
count: results.length,
|
|
33
|
+
results: results.map(r => ({
|
|
34
|
+
file: r.filename,
|
|
35
|
+
path: r.filePath,
|
|
36
|
+
kind: r.kind,
|
|
37
|
+
relevanceScore: r.relevanceScore,
|
|
38
|
+
snippet: r.content.slice(0, 300) + (r.content.length > 300 ? '...' : ''),
|
|
39
|
+
metadata: r.metadata,
|
|
40
|
+
})),
|
|
41
|
+
});
|
|
33
42
|
});
|
|
34
43
|
|
|
44
|
+
/**
|
|
45
|
+
* GET /api/knowledge/files
|
|
46
|
+
* List all ingested files with metadata
|
|
47
|
+
*/
|
|
35
48
|
router.get('/files', (req, res) => {
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
const { kind } = req.query;
|
|
50
|
+
const docs = kind ? store.getByKind(kind) : store.getAll();
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
grouped[doc.filePath].chunks++;
|
|
52
|
+
// Group by file path
|
|
53
|
+
const grouped = {};
|
|
54
|
+
for (const doc of docs) {
|
|
55
|
+
if (!grouped[doc.filePath]) {
|
|
56
|
+
grouped[doc.filePath] = {
|
|
57
|
+
filePath: doc.filePath,
|
|
58
|
+
filename: doc.filename,
|
|
59
|
+
kind: doc.kind,
|
|
60
|
+
chunks: 0,
|
|
61
|
+
ingestedAt: doc.ingestedAt,
|
|
62
|
+
metadata: doc.metadata,
|
|
63
|
+
};
|
|
52
64
|
}
|
|
65
|
+
grouped[doc.filePath].chunks++;
|
|
66
|
+
}
|
|
53
67
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
res.json({
|
|
69
|
+
count: Object.keys(grouped).length,
|
|
70
|
+
files: Object.values(grouped),
|
|
71
|
+
});
|
|
58
72
|
});
|
|
59
73
|
|
|
74
|
+
/**
|
|
75
|
+
* POST /api/knowledge/rebuild
|
|
76
|
+
* Force rebuild of the search index
|
|
77
|
+
*/
|
|
60
78
|
router.post('/rebuild', (req, res) => {
|
|
61
|
-
|
|
62
|
-
|
|
79
|
+
const count = indexer.build();
|
|
80
|
+
res.json({ success: true, documentsIndexed: count });
|
|
63
81
|
});
|
|
64
82
|
|
|
65
83
|
module.exports = router;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { MemoryManager, MEMORY_TYPES } = require('../../memory/memoryManager');
|
|
4
|
+
|
|
5
|
+
/** GET /api/memory — list all memories */
|
|
6
|
+
router.get('/', (req, res) => {
|
|
7
|
+
const { type } = req.query;
|
|
8
|
+
const memories = MemoryManager.list(type || null);
|
|
9
|
+
res.json({ memories, stats: MemoryManager.getStats() });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/** POST /api/memory — add a memory manually */
|
|
13
|
+
router.post('/', (req, res) => {
|
|
14
|
+
const { content, type, tags } = req.body;
|
|
15
|
+
if (!content) return res.status(400).json({ error: 'content is required' });
|
|
16
|
+
try {
|
|
17
|
+
const entry = MemoryManager.add(content, type || MEMORY_TYPES.FACT, tags || []);
|
|
18
|
+
res.json({ success: true, entry });
|
|
19
|
+
} catch (err) {
|
|
20
|
+
res.status(500).json({ error: err.message });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/** DELETE /api/memory/:id — delete a memory */
|
|
25
|
+
router.delete('/:id', (req, res) => {
|
|
26
|
+
const deleted = MemoryManager.delete(req.params.id);
|
|
27
|
+
res.json({ success: deleted });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/** DELETE /api/memory — clear all memories */
|
|
31
|
+
router.delete('/', (req, res) => {
|
|
32
|
+
MemoryManager.clear();
|
|
33
|
+
res.json({ success: true, message: 'All memories cleared' });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** GET /api/memory/types — list valid memory types */
|
|
37
|
+
router.get('/types', (req, res) => {
|
|
38
|
+
res.json({ types: Object.values(MEMORY_TYPES) });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
module.exports = router;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// ── skills.js ──────────────────────────────────────────────────────────────────
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const skillsRouter = express.Router();
|
|
4
|
+
const skillsManager = require('../../skills/skillsManager');
|
|
5
|
+
|
|
6
|
+
skillsRouter.get('/', (req, res) => res.json({ skills: skillsManager.list(req.query) }));
|
|
7
|
+
skillsRouter.post('/', (req, res) => {
|
|
8
|
+
try { res.json({ skill: skillsManager.create(req.body.name, req.body.description, req.body.prompt, req.body.tags) }); }
|
|
9
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
10
|
+
});
|
|
11
|
+
skillsRouter.post('/:name', async (req, res) => {
|
|
12
|
+
const { target, sessionId, extraContext } = req.body;
|
|
13
|
+
if (!target) return res.status(400).json({ error: 'target is required' });
|
|
14
|
+
try { res.json(await skillsManager.run(req.params.name, target, { sessionId, extraContext })); }
|
|
15
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
16
|
+
});
|
|
17
|
+
skillsRouter.delete('/:name', (req, res) => {
|
|
18
|
+
try { res.json({ success: skillsManager.delete(req.params.name) }); }
|
|
19
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
20
|
+
});
|
|
21
|
+
module.exports.skillsRouter = skillsRouter;
|
|
22
|
+
|
|
23
|
+
// ── lsp.js ─────────────────────────────────────────────────────────────────────
|
|
24
|
+
const lspRouter = express.Router();
|
|
25
|
+
const symbolNavigator = require('../../lsp/symbolNavigator');
|
|
26
|
+
|
|
27
|
+
lspRouter.get('/definition/:symbol', async (req, res) => {
|
|
28
|
+
try { res.json(await symbolNavigator.goToDefinition(req.params.symbol, req.query.cwd)); }
|
|
29
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
30
|
+
});
|
|
31
|
+
lspRouter.get('/references/:symbol', async (req, res) => {
|
|
32
|
+
try { res.json(await symbolNavigator.findReferences(req.params.symbol, req.query.cwd)); }
|
|
33
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
34
|
+
});
|
|
35
|
+
lspRouter.post('/hover', async (req, res) => {
|
|
36
|
+
try { res.json(await symbolNavigator.hover(req.body.symbol, req.body.filePath, req.body.sessionId)); }
|
|
37
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
38
|
+
});
|
|
39
|
+
lspRouter.get('/outline', async (req, res) => {
|
|
40
|
+
if (!req.query.path) return res.status(400).json({ error: 'path is required' });
|
|
41
|
+
try { res.json(await symbolNavigator.outline(req.query.path)); }
|
|
42
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
43
|
+
});
|
|
44
|
+
lspRouter.get('/symbols', async (req, res) => {
|
|
45
|
+
if (!req.query.q) return res.status(400).json({ error: 'q is required' });
|
|
46
|
+
try { res.json(await symbolNavigator.workspaceSymbols(req.query.q, req.query.cwd)); }
|
|
47
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
48
|
+
});
|
|
49
|
+
lspRouter.post('/rename', async (req, res) => {
|
|
50
|
+
const { oldName, newName, cwd } = req.body;
|
|
51
|
+
if (!oldName || !newName) return res.status(400).json({ error: 'oldName and newName required' });
|
|
52
|
+
try { res.json(await symbolNavigator.renameSymbol(oldName, newName, cwd)); }
|
|
53
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
54
|
+
});
|
|
55
|
+
module.exports.lspRouter = lspRouter;
|
|
56
|
+
|
|
57
|
+
// ── files.js ───────────────────────────────────────────────────────────────────
|
|
58
|
+
const filesRouter = express.Router();
|
|
59
|
+
const FileEditTool = require('../../tools/FileEditTool');
|
|
60
|
+
|
|
61
|
+
filesRouter.get('/read', (req, res) => {
|
|
62
|
+
if (!req.query.path) return res.status(400).json({ error: 'path is required' });
|
|
63
|
+
try { res.json(FileEditTool.read(req.query.path, { startLine: req.query.startLine, endLine: req.query.endLine })); }
|
|
64
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
65
|
+
});
|
|
66
|
+
filesRouter.post('/str-replace', async (req, res) => {
|
|
67
|
+
const { filePath, oldStr, newStr, dryRun, backup } = req.body;
|
|
68
|
+
if (!filePath || !oldStr) return res.status(400).json({ error: 'filePath and oldStr required' });
|
|
69
|
+
try { res.json(await FileEditTool.strReplace(filePath, oldStr, newStr || '', { dryRun, backup })); }
|
|
70
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
71
|
+
});
|
|
72
|
+
filesRouter.post('/insert-after', async (req, res) => {
|
|
73
|
+
const { filePath, afterStr, insertText, dryRun } = req.body;
|
|
74
|
+
if (!filePath || !afterStr || !insertText) return res.status(400).json({ error: 'filePath, afterStr, insertText required' });
|
|
75
|
+
try { res.json(await FileEditTool.insertAfter(filePath, afterStr, insertText, { dryRun })); }
|
|
76
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
77
|
+
});
|
|
78
|
+
filesRouter.post('/rewrite', async (req, res) => {
|
|
79
|
+
const { filePath, content, dryRun, backup } = req.body;
|
|
80
|
+
if (!filePath || !content) return res.status(400).json({ error: 'filePath and content required' });
|
|
81
|
+
try { res.json(await FileEditTool.rewrite(filePath, content, { dryRun, backup })); }
|
|
82
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
83
|
+
});
|
|
84
|
+
filesRouter.post('/ai-edit', async (req, res) => {
|
|
85
|
+
const { filePath, instruction, dryRun, sessionId } = req.body;
|
|
86
|
+
if (!filePath || !instruction) return res.status(400).json({ error: 'filePath and instruction required' });
|
|
87
|
+
try { res.json(await FileEditTool.aiEdit(filePath, instruction, { dryRun, sessionId })); }
|
|
88
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
89
|
+
});
|
|
90
|
+
filesRouter.post('/undo', (req, res) => {
|
|
91
|
+
if (!req.body.filePath) return res.status(400).json({ error: 'filePath required' });
|
|
92
|
+
try { res.json(FileEditTool.undo(req.body.filePath)); }
|
|
93
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
94
|
+
});
|
|
95
|
+
module.exports.filesRouter = filesRouter;
|
|
96
|
+
|
|
97
|
+
// ── monitor.js ─────────────────────────────────────────────────────────────────
|
|
98
|
+
const monitorRouter = express.Router();
|
|
99
|
+
const { ProactiveMonitor } = require('../../monitor/proactiveMonitor');
|
|
100
|
+
|
|
101
|
+
monitorRouter.get('/status', (req, res) => res.json(ProactiveMonitor.getStatus()));
|
|
102
|
+
monitorRouter.get('/alerts', (req, res) => {
|
|
103
|
+
const { severity, unacknowledged, limit } = req.query;
|
|
104
|
+
res.json({ alerts: ProactiveMonitor.getAlerts({ severity, unacknowledged: unacknowledged === 'true', limit: parseInt(limit) || 50 }) });
|
|
105
|
+
});
|
|
106
|
+
monitorRouter.post('/run-all', async (req, res) => {
|
|
107
|
+
try { res.json({ results: await ProactiveMonitor.runAll() }); }
|
|
108
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
109
|
+
});
|
|
110
|
+
monitorRouter.post('/run/:checkId', async (req, res) => {
|
|
111
|
+
try { res.json(await ProactiveMonitor.runCheck(req.params.checkId)); }
|
|
112
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
113
|
+
});
|
|
114
|
+
monitorRouter.post('/alerts/:id/acknowledge', (req, res) => {
|
|
115
|
+
try { res.json(ProactiveMonitor.acknowledge(req.params.id)); }
|
|
116
|
+
catch (e) { res.status(404).json({ error: e.message }); }
|
|
117
|
+
});
|
|
118
|
+
monitorRouter.post('/acknowledge-all', (req, res) => {
|
|
119
|
+
const count = ProactiveMonitor.acknowledgeAll();
|
|
120
|
+
res.json({ success: true, acknowledged: count });
|
|
121
|
+
});
|
|
122
|
+
monitorRouter.post('/start', (req, res) => {
|
|
123
|
+
ProactiveMonitor.start(req.body.cwd);
|
|
124
|
+
res.json({ success: true, message: 'Monitor started' });
|
|
125
|
+
});
|
|
126
|
+
monitorRouter.post('/stop', (req, res) => {
|
|
127
|
+
ProactiveMonitor.stop();
|
|
128
|
+
res.json({ success: true });
|
|
129
|
+
});
|
|
130
|
+
module.exports.monitorRouter = monitorRouter;
|
|
131
|
+
|
|
132
|
+
// ── conversation.js ────────────────────────────────────────────────────────────
|
|
133
|
+
const convRouter = express.Router();
|
|
134
|
+
const conversationEngine = require('../../core/conversationEngine');
|
|
135
|
+
|
|
136
|
+
convRouter.post('/', async (req, res) => {
|
|
137
|
+
const { message, convId, sessionId, topK } = req.body;
|
|
138
|
+
if (!message) return res.status(400).json({ error: 'message is required' });
|
|
139
|
+
try { res.json(await conversationEngine.chat(message, convId || 'default', { sessionId, topK })); }
|
|
140
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
141
|
+
});
|
|
142
|
+
convRouter.post('/follow-up', async (req, res) => {
|
|
143
|
+
const { message, convId, sessionId } = req.body;
|
|
144
|
+
if (!message) return res.status(400).json({ error: 'message is required' });
|
|
145
|
+
try { res.json(await conversationEngine.followUp(message, convId || 'default', { sessionId })); }
|
|
146
|
+
catch (e) { res.status(500).json({ error: e.message }); }
|
|
147
|
+
});
|
|
148
|
+
convRouter.get('/', (req, res) => res.json({ conversations: conversationEngine.list() }));
|
|
149
|
+
convRouter.get('/:id/history', (req, res) => res.json({ history: conversationEngine.getHistory(req.params.id) }));
|
|
150
|
+
convRouter.delete('/:id', (req, res) => { conversationEngine.reset(req.params.id); res.json({ success: true }); });
|
|
151
|
+
module.exports.convRouter = convRouter;
|
|
152
|
+
|
|
153
|
+
// ── watcher.js ─────────────────────────────────────────────────────────────────
|
|
154
|
+
const watcherRouter = express.Router();
|
|
155
|
+
const fileWatcher = require('../../watcher/fileWatcher');
|
|
156
|
+
|
|
157
|
+
watcherRouter.get('/status', (req, res) => res.json(fileWatcher.getStatus()));
|
|
158
|
+
watcherRouter.post('/watch', (req, res) => {
|
|
159
|
+
if (!req.body.path) return res.status(400).json({ error: 'path is required' });
|
|
160
|
+
try { res.json(fileWatcher.watch(req.body.path)); }
|
|
161
|
+
catch (e) { res.status(400).json({ error: e.message }); }
|
|
162
|
+
});
|
|
163
|
+
watcherRouter.post('/unwatch', (req, res) => {
|
|
164
|
+
if (!req.body.path) return res.status(400).json({ error: 'path is required' });
|
|
165
|
+
res.json({ success: fileWatcher.unwatch(req.body.path) });
|
|
166
|
+
});
|
|
167
|
+
watcherRouter.post('/stop', (req, res) => { fileWatcher.stopAll(); res.json({ success: true }); });
|
|
168
|
+
module.exports.watcherRouter = watcherRouter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const pipelineEngine = require('../../pipelines/pipelineEngine');
|
|
4
|
+
const logger = require('../../utils/logger');
|
|
5
|
+
|
|
6
|
+
/** GET /api/pipelines — list all available pipelines and steps */
|
|
7
|
+
router.get('/', (req, res) => {
|
|
8
|
+
res.json({
|
|
9
|
+
pipelines: pipelineEngine.getAvailablePipelines(),
|
|
10
|
+
availableSteps: pipelineEngine.getAvailableSteps(),
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/** POST /api/pipelines/:name — run a named pipeline */
|
|
15
|
+
router.post('/:name', async (req, res) => {
|
|
16
|
+
const { task, sessionId, topK, ...rest } = req.body;
|
|
17
|
+
if (!task) return res.status(400).json({ error: 'task is required' });
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const result = await pipelineEngine.run(req.params.name, { task, ...rest }, { sessionId, topK });
|
|
21
|
+
res.json(result);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
logger.error(`Pipeline error: ${err.message}`);
|
|
24
|
+
res.status(500).json({ error: err.message });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** POST /api/pipelines/custom/run — run a custom steps pipeline */
|
|
29
|
+
router.post('/custom/run', async (req, res) => {
|
|
30
|
+
const { steps, task, sessionId, topK, ...rest } = req.body;
|
|
31
|
+
if (!task || !steps?.length) return res.status(400).json({ error: 'task and steps[] are required' });
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const result = await pipelineEngine.runCustom(steps, { task, ...rest }, { sessionId, topK });
|
|
35
|
+
res.json(result);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
res.status(500).json({ error: err.message });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
module.exports = router;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const plannerEngine = require('../../planner/plannerEngine');
|
|
4
|
+
const costTracker = require('../../utils/costTracker');
|
|
5
|
+
const indexer = require('../../core/indexer');
|
|
6
|
+
const logger = require('../../utils/logger');
|
|
7
|
+
|
|
8
|
+
/** POST /api/plan — generate an execution plan for a task */
|
|
9
|
+
router.post('/', async (req, res) => {
|
|
10
|
+
const { task, sessionId } = req.body;
|
|
11
|
+
if (!task) return res.status(400).json({ error: 'task is required' });
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Retrieve relevant context chunks for the plan
|
|
15
|
+
const context = indexer.search(task, 6);
|
|
16
|
+
const plan = await plannerEngine.generatePlan(task, context, sessionId);
|
|
17
|
+
res.json(plan);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
logger.error(`Plan error: ${err.message}`);
|
|
20
|
+
res.status(500).json({ error: err.message });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/** POST /api/plan/compact — compact a conversation history */
|
|
25
|
+
router.post('/compact', async (req, res) => {
|
|
26
|
+
const { messages, sessionId } = req.body;
|
|
27
|
+
if (!messages || !Array.isArray(messages)) {
|
|
28
|
+
return res.status(400).json({ error: 'messages array is required' });
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const result = await plannerEngine.compact(messages, sessionId);
|
|
32
|
+
res.json(result);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
res.status(500).json({ error: err.message });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/** GET /api/plan/doctor — environment health check */
|
|
39
|
+
router.get('/doctor', async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
const result = await plannerEngine.doctor();
|
|
42
|
+
res.json(result);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
res.status(500).json({ error: err.message });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/** GET /api/cost — get cost summary */
|
|
49
|
+
router.get('/cost', (req, res) => {
|
|
50
|
+
const { sessionId } = req.query;
|
|
51
|
+
res.json(costTracker.getSummary(sessionId || 'default'));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
module.exports = router;
|