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.
Files changed (58) hide show
  1. package/.env.example +23 -55
  2. package/README.md +609 -219
  3. package/cli.js +486 -160
  4. package/package.json +2 -2
  5. package/src/agents/BaseAgent.js +113 -0
  6. package/src/agents/dreamer.js +165 -0
  7. package/src/agents/improver.js +175 -0
  8. package/src/agents/specialists.js +202 -0
  9. package/src/agents/taskDecomposer.js +176 -0
  10. package/src/agents/teamCoordinator.js +153 -0
  11. package/src/api/routes/agents.js +172 -0
  12. package/src/api/routes/extras.js +115 -0
  13. package/src/api/routes/git.js +72 -0
  14. package/src/api/routes/ingest.js +60 -40
  15. package/src/api/routes/knowledge.js +59 -41
  16. package/src/api/routes/memory.js +41 -0
  17. package/src/api/routes/newRoutes.js +168 -0
  18. package/src/api/routes/pipelines.js +41 -0
  19. package/src/api/routes/planner.js +54 -0
  20. package/src/api/routes/query.js +24 -0
  21. package/src/api/routes/sessions.js +54 -0
  22. package/src/api/routes/tasks.js +67 -0
  23. package/src/api/routes/tools.js +85 -0
  24. package/src/api/routes/v5routes.js +196 -0
  25. package/src/api/server.js +133 -5
  26. package/src/context/compactor.js +151 -0
  27. package/src/context/contextEngineer.js +181 -0
  28. package/src/context/contextVisualizer.js +140 -0
  29. package/src/core/conversationEngine.js +231 -0
  30. package/src/core/indexer.js +169 -143
  31. package/src/core/ingester.js +141 -126
  32. package/src/core/queryEngine.js +286 -236
  33. package/src/cron/cronScheduler.js +260 -0
  34. package/src/dashboard/index.html +1181 -0
  35. package/src/lsp/symbolNavigator.js +220 -0
  36. package/src/memory/memoryManager.js +186 -0
  37. package/src/memory/teamMemory.js +111 -0
  38. package/src/messaging/messageBus.js +177 -0
  39. package/src/monitor/proactiveMonitor.js +337 -0
  40. package/src/pipelines/pipelineEngine.js +230 -0
  41. package/src/planner/plannerEngine.js +202 -0
  42. package/src/plugins/builtin/stats-plugin.js +29 -0
  43. package/src/plugins/pluginManager.js +144 -0
  44. package/src/prompts/promptEngineer.js +289 -0
  45. package/src/sessions/sessionManager.js +166 -0
  46. package/src/skills/skillsManager.js +263 -0
  47. package/src/storage/store.js +127 -105
  48. package/src/tasks/taskManager.js +151 -0
  49. package/src/tools/BashTool.js +154 -0
  50. package/src/tools/FileEditTool.js +280 -0
  51. package/src/tools/GitTool.js +212 -0
  52. package/src/tools/GrepTool.js +199 -0
  53. package/src/tools/registry.js +1380 -0
  54. package/src/utils/costTracker.js +69 -0
  55. package/src/utils/fileParser.js +176 -153
  56. package/src/utils/llmClient.js +355 -206
  57. package/src/watcher/fileWatcher.js +137 -0
  58. package/src/worktrees/worktreeManager.js +176 -0
@@ -0,0 +1,260 @@
1
+ 'use strict';
2
+ /**
3
+ * Schedule any agent task to run automatically on a cron schedule.
4
+ * Results are saved to memory + create tasks for any action items found.
5
+ *
6
+ * Schedule format: standard cron (minute hour day month weekday)
7
+ * Examples:
8
+ * "0 9 * * 1-5" — 9am weekdays
9
+ * "0 0 * * *" — midnight daily
10
+ * "0/30 * * * *" — every 30 minutes
11
+ * "@daily" — once a day
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const logger = require('../utils/logger');
17
+
18
+ const CRON_FILE = path.join(process.cwd(), 'data', 'cron-jobs.json');
19
+ const RUN_LOG = path.join(process.cwd(), 'data', 'cron-runs.json');
20
+
21
+ // Minimal cron parser — converts cron expr to ms interval
22
+ function parseCronInterval(schedule) {
23
+ const presets = {
24
+ '@hourly': 3600000,
25
+ '@daily': 86400000,
26
+ '@weekly': 604800000,
27
+ '@monthly': 2592000000,
28
+ };
29
+ if (presets[schedule]) return presets[schedule];
30
+
31
+ // For simplicity, support "every N minutes" patterns
32
+ const parts = schedule.split(' ');
33
+ if (parts.length === 5) {
34
+ const minute = parts[0];
35
+ if (minute.startsWith('*/')) return parseInt(minute.slice(2)) * 60000;
36
+ if (minute.startsWith('0/')) return parseInt(minute.slice(2)) * 60000;
37
+ // Default: run once per day
38
+ return 86400000;
39
+ }
40
+ return 3600000; // fallback: hourly
41
+ }
42
+
43
+ class CronScheduler {
44
+ constructor() {
45
+ this._jobs = this._loadJobs();
46
+ this._handles = new Map(); // jobName → setInterval handle
47
+ this._running = false;
48
+ this._runLog = this._loadRunLog();
49
+ }
50
+
51
+ _loadJobs() {
52
+ try { if (fs.existsSync(CRON_FILE)) return JSON.parse(fs.readFileSync(CRON_FILE, 'utf-8')); } catch { }
53
+ return {};
54
+ }
55
+ _saveJobs() { fs.writeFileSync(CRON_FILE, JSON.stringify(this._jobs, null, 2)); }
56
+
57
+ _loadRunLog() {
58
+ try { if (fs.existsSync(RUN_LOG)) return JSON.parse(fs.readFileSync(RUN_LOG, 'utf-8')); } catch { }
59
+ return { runs: [] };
60
+ }
61
+ _saveRunLog() {
62
+ if (this._runLog.runs.length > 200) this._runLog.runs = this._runLog.runs.slice(-200);
63
+ fs.writeFileSync(RUN_LOG, JSON.stringify(this._runLog, null, 2));
64
+ }
65
+
66
+ /**
67
+ * Create and register a new cron job
68
+ */
69
+ create(opts = {}) {
70
+ const {
71
+ name,
72
+ schedule,
73
+ task,
74
+ agent = 'DebugAgent',
75
+ enabled = true,
76
+ description = '',
77
+ team, // use a team instead of single agent
78
+ pipeline, // use a pipeline instead
79
+ } = opts;
80
+
81
+ if (!name || !schedule || !task) throw new Error('name, schedule, task are required');
82
+ if (this._jobs[name]) throw new Error(`Job "${name}" already exists`);
83
+
84
+ const job = {
85
+ name,
86
+ schedule,
87
+ task,
88
+ agent: team ? null : (pipeline ? null : agent),
89
+ team: team || null,
90
+ pipeline: pipeline || null,
91
+ description,
92
+ enabled,
93
+ createdAt: new Date().toISOString(),
94
+ lastRun: null,
95
+ lastStatus: null,
96
+ runCount: 0,
97
+ };
98
+
99
+ this._jobs[name] = job;
100
+ this._saveJobs();
101
+
102
+ if (enabled && this._running) {
103
+ this._schedule(job);
104
+ }
105
+
106
+ logger.info(`[Cron] Created: ${name} (${schedule}) → ${agent || team || pipeline}`);
107
+ return job;
108
+ }
109
+
110
+ /**
111
+ * Start the scheduler — register all enabled jobs
112
+ */
113
+ start() {
114
+ if (this._running) return;
115
+ this._running = true;
116
+ let scheduled = 0;
117
+ for (const job of Object.values(this._jobs)) {
118
+ if (job.enabled) { this._schedule(job); scheduled++; }
119
+ }
120
+ logger.info(`[Cron] ⏰ Started: ${scheduled} job(s) scheduled`);
121
+ }
122
+
123
+ stop() {
124
+ for (const handle of this._handles.values()) clearInterval(handle);
125
+ this._handles.clear();
126
+ this._running = false;
127
+ logger.info('[Cron] Stopped');
128
+ }
129
+
130
+ _schedule(job) {
131
+ if (this._handles.has(job.name)) return;
132
+ const intervalMs = parseCronInterval(job.schedule);
133
+ const handle = setInterval(() => this._run(job), intervalMs);
134
+ this._handles.set(job.name, handle);
135
+ logger.info(`[Cron] Scheduled: ${job.name} every ${intervalMs / 60000}min`);
136
+ }
137
+
138
+ /**
139
+ * Run a job immediately (manual trigger)
140
+ */
141
+ async runNow(name) {
142
+ const job = this._jobs[name];
143
+ if (!job) throw new Error(`Job not found: ${name}`);
144
+ return this._run(job);
145
+ }
146
+
147
+ async _run(job) {
148
+ const startTime = Date.now();
149
+ logger.info(`[Cron] Running: ${job.name}`);
150
+
151
+ const runRecord = {
152
+ jobName: job.name,
153
+ startedAt: new Date().toISOString(),
154
+ status: 'running',
155
+ durationMs: 0,
156
+ result: null,
157
+ error: null,
158
+ };
159
+
160
+ try {
161
+ let result;
162
+
163
+ if (job.pipeline) {
164
+ const pipelineEngine = require('../pipelines/pipelineEngine');
165
+ result = await pipelineEngine.run(job.pipeline, { task: job.task });
166
+ runRecord.result = result.finalOutput?.answer?.slice(0, 300) || 'Pipeline complete';
167
+ } else if (job.team) {
168
+ const teamCoordinator = require('../agents/teamCoordinator');
169
+ result = await teamCoordinator.runTeam(job.team, job.task, { sessionId: `cron_${job.name}` });
170
+ runRecord.result = result.report?.slice(0, 300) || 'Team run complete';
171
+ } else {
172
+ const agents = require('../agents/specialists');
173
+ const agent = agents[job.agent] || agents['DebugAgent'];
174
+ const indexer = require('../core/indexer');
175
+ const context = indexer.search(job.task, 6);
176
+ result = await agent.run(job.task, { context, sessionId: `cron_${job.name}` });
177
+ runRecord.result = result.answer?.slice(0, 300);
178
+ }
179
+
180
+ runRecord.status = 'success';
181
+ runRecord.durationMs = Date.now() - startTime;
182
+
183
+ // Update job metadata
184
+ this._jobs[job.name].lastRun = runRecord.startedAt;
185
+ this._jobs[job.name].lastStatus = 'success';
186
+ this._jobs[job.name].runCount = (this._jobs[job.name].runCount || 0) + 1;
187
+ this._saveJobs();
188
+
189
+ // Save outcome to memory for the dreamer
190
+ const { MemoryManager } = require('../memory/memoryManager');
191
+ MemoryManager.add(
192
+ `Cron job "${job.name}" result: ${runRecord.result}`,
193
+ 'fact',
194
+ ['cron', job.name]
195
+ );
196
+
197
+ } catch (err) {
198
+ runRecord.status = 'error';
199
+ runRecord.error = err.message;
200
+ runRecord.durationMs = Date.now() - startTime;
201
+ this._jobs[job.name].lastStatus = 'error';
202
+ this._saveJobs();
203
+ logger.error(`[Cron] ${job.name} failed: ${err.message}`);
204
+ }
205
+
206
+ this._runLog.runs.push(runRecord);
207
+ this._saveRunLog();
208
+ return runRecord;
209
+ }
210
+
211
+ update(name, updates = {}) {
212
+ const job = this._jobs[name];
213
+ if (!job) throw new Error(`Job not found: ${name}`);
214
+ const allowed = ['schedule', 'task', 'agent', 'enabled', 'description'];
215
+ for (const k of allowed) { if (updates[k] !== undefined) job[k] = updates[k]; }
216
+ this._saveJobs();
217
+
218
+ // Re-schedule if running state changed
219
+ if (updates.enabled === true && this._running && !this._handles.has(name)) {
220
+ this._schedule(job);
221
+ } else if (updates.enabled === false) {
222
+ clearInterval(this._handles.get(name));
223
+ this._handles.delete(name);
224
+ }
225
+
226
+ return job;
227
+ }
228
+
229
+ delete(name) {
230
+ if (!this._jobs[name]) throw new Error(`Job not found: ${name}`);
231
+ clearInterval(this._handles.get(name));
232
+ this._handles.delete(name);
233
+ delete this._jobs[name];
234
+ this._saveJobs();
235
+ return true;
236
+ }
237
+
238
+ list() {
239
+ return Object.values(this._jobs);
240
+ }
241
+
242
+ getRunHistory(name, limit = 20) {
243
+ return this._runLog.runs
244
+ .filter(r => !name || r.jobName === name)
245
+ .slice(-limit)
246
+ .reverse();
247
+ }
248
+
249
+ getStats() {
250
+ const jobs = Object.values(this._jobs);
251
+ return {
252
+ total: jobs.length,
253
+ enabled: jobs.filter(j => j.enabled).length,
254
+ running: this._running,
255
+ totalRuns: this._runLog.runs.length,
256
+ };
257
+ }
258
+ }
259
+
260
+ module.exports = new CronScheduler();