fivocell 2.0.0 → 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.
Files changed (51) hide show
  1. package/README.md +104 -697
  2. package/dist/behavioral-tracker.d.ts +90 -0
  3. package/dist/behavioral-tracker.d.ts.map +1 -0
  4. package/dist/behavioral-tracker.js +185 -0
  5. package/dist/behavioral-tracker.js.map +1 -0
  6. package/dist/cli.d.ts +1 -6
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +430 -3862
  9. package/dist/cli.js.map +1 -1
  10. package/dist/code-scanner.d.ts +51 -0
  11. package/dist/code-scanner.d.ts.map +1 -0
  12. package/dist/code-scanner.js +966 -0
  13. package/dist/code-scanner.js.map +1 -0
  14. package/dist/community-intel.d.ts +34 -0
  15. package/dist/community-intel.d.ts.map +1 -0
  16. package/dist/community-intel.js +148 -0
  17. package/dist/community-intel.js.map +1 -0
  18. package/dist/daemon/lifecycle.d.ts +0 -15
  19. package/dist/daemon/lifecycle.d.ts.map +1 -1
  20. package/dist/daemon/lifecycle.js +88 -231
  21. package/dist/daemon/lifecycle.js.map +1 -1
  22. package/dist/daemon/server.d.ts.map +1 -1
  23. package/dist/daemon/server.js +368 -19
  24. package/dist/daemon/server.js.map +1 -1
  25. package/dist/developer-intelligence.d.ts +18 -0
  26. package/dist/developer-intelligence.d.ts.map +1 -0
  27. package/dist/developer-intelligence.js +180 -0
  28. package/dist/developer-intelligence.js.map +1 -0
  29. package/dist/layers.d.ts +92 -0
  30. package/dist/layers.d.ts.map +1 -0
  31. package/dist/layers.js +226 -0
  32. package/dist/layers.js.map +1 -0
  33. package/dist/mcp-server.d.ts +194 -1842
  34. package/dist/mcp-server.d.ts.map +1 -1
  35. package/dist/mcp-server.js +169 -875
  36. package/dist/mcp-server.js.map +1 -1
  37. package/dist/pc-scanner.d.ts +46 -0
  38. package/dist/pc-scanner.d.ts.map +1 -0
  39. package/dist/pc-scanner.js +488 -0
  40. package/dist/pc-scanner.js.map +1 -0
  41. package/dist/predictive-engine.d.ts +19 -0
  42. package/dist/predictive-engine.d.ts.map +1 -0
  43. package/dist/predictive-engine.js +107 -0
  44. package/dist/predictive-engine.js.map +1 -0
  45. package/dist/style-pull.d.ts +1 -1
  46. package/dist/style-pull.js +2 -2
  47. package/dist/team-git.d.ts +47 -0
  48. package/dist/team-git.d.ts.map +1 -0
  49. package/dist/team-git.js +305 -0
  50. package/dist/team-git.js.map +1 -0
  51. package/package.json +1 -1
@@ -204,16 +204,104 @@ app.get('/health', (_req, res) => {
204
204
  });
205
205
  });
206
206
  app.post('/signal', (req, res) => {
207
- const { type, originalCode, editedCode, context } = req.body;
208
- if (!type || !context) {
209
- res.status(400).json({ error: 'type and context required' });
207
+ const { type, originalCode, editedCode, context, file, fromFile, costMs, durationMs, switchCount, language, errorMessage, errorType, line, fixApplied, fixWorked, timeToFixMs, decision, approach, result, worked, task, files, description, stuckId, resolution, project } = req.body;
208
+ if (!type) {
209
+ res.status(400).json({ error: 'type required' });
210
210
  return;
211
211
  }
212
+ // ─── Behavioral Signals ───────────────────────────────────────
213
+ if (type === 'error') {
214
+ const { logError, getErrorPatterns } = require('../behavioral-tracker');
215
+ const r = logError({ project, file, errorType, errorMessage, line });
216
+ // Check if repeat mistake — send warning
217
+ let warning = null;
218
+ try {
219
+ const patterns = getErrorPatterns(project);
220
+ const match = patterns.find((p) => p.error_type === errorType);
221
+ if (match && match.count >= 2) {
222
+ warning = `⚠ Repeat mistake! "${errorType}" has occurred ${match.count}x before. Fix the root cause.`;
223
+ }
224
+ }
225
+ catch { }
226
+ res.json({ recorded: true, id: r.id, warning });
227
+ return;
228
+ }
229
+ if (type === 'error_fix') {
230
+ const { logErrorFix } = require('../behavioral-tracker');
231
+ logErrorFix({ errorId: stuckId || 0, fixApplied: fixApplied || '', worked: fixWorked || false, timeToFixMs: timeToFixMs || 0 });
232
+ res.json({ recorded: true });
233
+ return;
234
+ }
235
+ if (type === 'decision') {
236
+ const { logDecision } = require('../behavioral-tracker');
237
+ const r = logDecision({ project, file, decision, approach, result, worked, context: req.body.context || '' });
238
+ // Auto-track in session memory
239
+ try {
240
+ const { SessionMemory } = require('../core/session-memory');
241
+ const mem = new SessionMemory();
242
+ const lastSess = mem.getLastSessionForProject(project || '');
243
+ if (lastSess && !lastSess.endTime) {
244
+ const decs = JSON.parse(String(lastSess.keyDecisions || '[]'));
245
+ decs.push(`${decision}: ${approach || result || ''}`);
246
+ mem.endSession(lastSess.id, { keyDecisions: decs, contextSnapshot: req.body.context || decision });
247
+ }
248
+ }
249
+ catch { }
250
+ res.json({ recorded: true, id: r.id });
251
+ return;
252
+ }
253
+ if (type === 'context') {
254
+ const { logContext } = require('../behavioral-tracker');
255
+ const r = logContext({ project, task, files });
256
+ // Auto-start session if none exists
257
+ try {
258
+ const { SessionMemory } = require('../core/session-memory');
259
+ const mem = new SessionMemory();
260
+ const lastSess = mem.getLastSessionForProject(project || '');
261
+ if (!lastSess || lastSess.endTime) {
262
+ const tool = req.body.tool || 'cli';
263
+ mem.startSession(tool, project || '');
264
+ if (files && files.length > 0) {
265
+ const newSess = mem.getLastSessionForProject(project || '');
266
+ if (newSess)
267
+ mem.endSession(newSess.id, { filesTouched: files, contextSnapshot: task });
268
+ }
269
+ }
270
+ }
271
+ catch { }
272
+ res.json({ recorded: true, id: r.id });
273
+ return;
274
+ }
275
+ if (type === 'stuck') {
276
+ const { logStuck } = require('../behavioral-tracker');
277
+ const r = logStuck({ project, file, description });
278
+ res.json({ recorded: true, id: r.id });
279
+ return;
280
+ }
281
+ if (type === 'stuck_resolved') {
282
+ const { logStuckResolved } = require('../behavioral-tracker');
283
+ logStuckResolved(stuckId || 0, resolution || '');
284
+ res.json({ recorded: true });
285
+ return;
286
+ }
287
+ // ─── IDE Signals ──────────────────────────────────────────────
288
+ if (type === 'tab_switch' || type === 'focus_block' || type === 'file_save') {
289
+ try {
290
+ const db = (0, database_1.getDb)();
291
+ db.prepare('INSERT INTO behavior_events (event_type, project, file_path, description, severity, evidence) VALUES (?, ?, ?, ?, ?, ?)')
292
+ .run(type, '', file || '', JSON.stringify({ fromFile, costMs, durationMs, switchCount, language }), 'low', JSON.stringify(req.body));
293
+ }
294
+ catch { }
295
+ res.json({ recorded: true, type });
296
+ return;
297
+ }
298
+ // ─── Legacy Signals ──────────────────────────────────────────
299
+ const ctx = context || {};
212
300
  let signal;
213
301
  let patternsExtracted = 0;
214
302
  switch (type) {
215
303
  case 'edit_diff': {
216
- signal = engines.signalCapture.captureEditDiff(originalCode || '', editedCode || '', context);
304
+ signal = engines.signalCapture.captureEditDiff(originalCode || '', editedCode || '', ctx);
217
305
  try {
218
306
  const result = engines.extractionCascade.extract(originalCode || '', editedCode || '');
219
307
  patternsExtracted = engines.learningLayer.process(result, true);
@@ -226,35 +314,28 @@ app.post('/signal', (req, res) => {
226
314
  break;
227
315
  }
228
316
  case 'accept': {
229
- signal = engines.signalCapture.captureAccept(originalCode || '', context);
317
+ signal = engines.signalCapture.captureAccept(originalCode || '', ctx);
230
318
  const dummy = engines.patternStore.getAllPatterns().slice(0, 1);
231
319
  for (const p of dummy) {
232
320
  const step = (0, workflow_tracker_1.recordStep)(p, 'accept');
233
321
  (0, outcome_recorder_1.recordOutcome)(p, 'accept', step.sessionId, true, 0);
234
- (0, failure_memory_1.recordFailure)(p, false, context?.language || 'unknown');
235
- (0, time_saved_1.recordTimeSaved)({
236
- patternId: p.patternId,
237
- category: p.category,
238
- description: p.description,
239
- from: p.from,
240
- to: p.to,
241
- language: context?.language || 'unknown',
242
- });
322
+ (0, failure_memory_1.recordFailure)(p, false, ctx.language || 'unknown');
323
+ (0, time_saved_1.recordTimeSaved)({ patternId: p.patternId, category: p.category, description: p.description, from: p.from, to: p.to, language: ctx.language || 'unknown' });
243
324
  }
244
325
  break;
245
326
  }
246
327
  case 'reject': {
247
- signal = engines.signalCapture.captureReject(originalCode || '', context);
328
+ signal = engines.signalCapture.captureReject(originalCode || '', ctx);
248
329
  const dummyR = engines.patternStore.getAllPatterns().slice(0, 1);
249
330
  for (const p of dummyR) {
250
331
  const step = (0, workflow_tracker_1.recordStep)(p, 'reject');
251
332
  (0, outcome_recorder_1.recordOutcome)(p, 'reject', step.sessionId, false, 0);
252
- (0, failure_memory_1.recordFailure)(p, true, context?.language || 'unknown');
333
+ (0, failure_memory_1.recordFailure)(p, true, ctx.language || 'unknown');
253
334
  }
254
335
  break;
255
336
  }
256
337
  case 'retry': {
257
- signal = engines.signalCapture.captureRetry(originalCode || '', context);
338
+ signal = engines.signalCapture.captureRetry(originalCode || '', ctx);
258
339
  const dummyRt = engines.patternStore.getAllPatterns().slice(0, 1);
259
340
  for (const p of dummyRt) {
260
341
  const step = (0, workflow_tracker_1.recordStep)(p, 'retry');
@@ -1562,20 +1643,288 @@ app.post('/cloud/push', (req, res) => {
1562
1643
  pushToCloud(req.body?.data || {}, req.body?.type || 'pattern').then(r => res.json(r));
1563
1644
  }).catch(e => res.status(500).json({ error: String(e) }));
1564
1645
  });
1646
+ // ─── CODE SCANNER (Deep Learning) ──────────────────────
1647
+ app.post('/scan/codebase', (req, res) => {
1648
+ const { dir, project } = req.body;
1649
+ if (!dir) {
1650
+ res.status(400).json({ error: 'dir required' });
1651
+ return;
1652
+ }
1653
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ scanCodebase }) => {
1654
+ const result = scanCodebase(dir, project || require('path').basename(dir));
1655
+ res.json(result);
1656
+ }).catch(e => res.status(500).json({ error: String(e) }));
1657
+ });
1658
+ app.get('/scan/profile/:project', (req, res) => {
1659
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ getDeveloperProfile }) => {
1660
+ const profile = getDeveloperProfile(req.params.project);
1661
+ if (!profile) {
1662
+ res.status(404).json({ error: 'no profile — run scan first' });
1663
+ return;
1664
+ }
1665
+ res.json(profile);
1666
+ }).catch(e => res.status(500).json({ error: String(e) }));
1667
+ });
1668
+ app.get('/scan/patterns/:project', (req, res) => {
1669
+ const category = req.query.category;
1670
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ getCodePatterns }) => {
1671
+ res.json({ patterns: getCodePatterns(req.params.project, category) });
1672
+ }).catch(e => res.status(500).json({ error: String(e) }));
1673
+ });
1674
+ app.get('/scan/report/:project', (req, res) => {
1675
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ getDeveloperProfile, getCodePatterns }) => {
1676
+ const profile = getDeveloperProfile(req.params.project);
1677
+ const patterns = getCodePatterns(req.params.project);
1678
+ res.json({ project: req.params.project, profile, patterns, patternCount: patterns.length });
1679
+ }).catch(e => res.status(500).json({ error: String(e) }));
1680
+ });
1681
+ // ─── BEHAVIORAL TRACKING ──────────────────────────────────────
1682
+ app.get('/behavior/summary', (req, res) => {
1683
+ const { getBehaviorSummary } = require('../behavioral-tracker');
1684
+ res.json(getBehaviorSummary(String(req.query.project || undefined)));
1685
+ });
1686
+ app.get('/behavior/errors', (req, res) => {
1687
+ const { getErrorPatterns, getRecentErrors } = require('../behavioral-tracker');
1688
+ res.json({ patterns: getErrorPatterns(String(req.query.project || undefined)), recent: getRecentErrors(10, String(req.query.project || undefined)) });
1689
+ });
1690
+ app.get('/behavior/decisions', (req, res) => {
1691
+ const { getDecisions } = require('../behavioral-tracker');
1692
+ res.json({ decisions: getDecisions(String(req.query.project || undefined), 20) });
1693
+ });
1694
+ app.get('/behavior/context', (req, res) => {
1695
+ const { getCurrentContext } = require('../behavioral-tracker');
1696
+ res.json(getCurrentContext(String(req.query.project || undefined)) || { task: 'none', files: [] });
1697
+ });
1698
+ app.get('/behavior/stuck', (req, res) => {
1699
+ const { getStuckPatterns } = require('../behavioral-tracker');
1700
+ res.json({ stuck: getStuckPatterns(String(req.query.project || undefined)) });
1701
+ });
1702
+ // ─── Session Bridge Helper ────────────────────────────────────────────────
1703
+ function formatSessionBridge(fromTool, toTool, context, decisions) {
1704
+ const lines = [];
1705
+ lines.push(`═══ CROSS-SESSION BRIDGE ═══`);
1706
+ lines.push(`From: ${fromTool} → To: ${toTool}`);
1707
+ if (context)
1708
+ lines.push(`Last context: ${context}`);
1709
+ if (decisions && decisions !== 'none' && decisions !== '[]') {
1710
+ lines.push(`Decisions made: ${decisions}`);
1711
+ }
1712
+ lines.push(`Cell remembers. No need to re-explain.`);
1713
+ return lines.join('\n');
1714
+ }
1715
+ // ─── MCP ENDPOINTS (merged from mcp-server.ts) ─────────────────────────────
1716
+ const MCP_TOOLS = [
1717
+ { name: 'cell_get_dev_profile', description: 'Developer profile — naming, style, errors, architecture', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
1718
+ { name: 'cell_get_code_patterns', description: 'Code patterns by category', inputSchema: { type: 'object', properties: { project: { type: 'string' }, category: { type: 'string' } }, required: ['project'] } },
1719
+ { name: 'cell_deep_scan', description: 'Deep scan codebase', inputSchema: { type: 'object', properties: { dir: { type: 'string' }, project: { type: 'string' } }, required: ['dir'] } },
1720
+ { name: 'cell_scan_report', description: 'Full scan report', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
1721
+ { name: 'cell_send_signal', description: 'Send learning signal', inputSchema: { type: 'object', properties: { type: { type: 'string' }, originalCode: { type: 'string' }, editedCode: { type: 'string' }, file: { type: 'string' }, language: { type: 'string' } }, required: ['type'] } },
1722
+ { name: 'cell_get_context', description: 'Full context injection', inputSchema: { type: 'object', properties: { project: { type: 'string' }, tool: { type: 'string' } }, required: ['project'] } },
1723
+ { name: 'cell_scan_full_pc', description: 'Scan entire PC for all code projects', inputSchema: { type: 'object', properties: {} } },
1724
+ { name: 'cell_behavior_summary', description: 'Behavioral summary', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
1725
+ { name: 'cell_log_error', description: 'Log an error encounter', inputSchema: { type: 'object', properties: { project: { type: 'string' }, file: { type: 'string' }, errorType: { type: 'string' }, errorMessage: { type: 'string' }, line: { type: 'number' } }, required: ['errorType', 'errorMessage'] } },
1726
+ { name: 'cell_log_fix', description: 'Log how an error was fixed', inputSchema: { type: 'object', properties: { errorId: { type: 'number' }, fixApplied: { type: 'string' }, worked: { type: 'boolean' }, timeToFixMs: { type: 'number' } }, required: ['errorId', 'fixApplied', 'worked'] } },
1727
+ { name: 'cell_log_decision', description: 'Log a coding decision', inputSchema: { type: 'object', properties: { project: { type: 'string' }, file: { type: 'string' }, decision: { type: 'string' }, approach: { type: 'string' }, worked: { type: 'boolean' } }, required: ['decision'] } },
1728
+ { name: 'cell_log_context', description: 'Log current context', inputSchema: { type: 'object', properties: { project: { type: 'string' }, task: { type: 'string' }, files: { type: 'array', items: { type: 'string' } } }, required: ['task'] } },
1729
+ { name: 'cell_log_stuck', description: 'Log when stuck', inputSchema: { type: 'object', properties: { project: { type: 'string' }, file: { type: 'string' }, description: { type: 'string' } }, required: ['description'] } },
1730
+ { name: 'cell_session_bridge', description: 'Cross-session memory bridge — get last session context when switching tools', inputSchema: { type: 'object', properties: { project: { type: 'string' }, tool: { type: 'string' } }, required: ['project'] } },
1731
+ { name: 'cell_predict', description: 'Predictive intelligence — repeat mistakes, decision regret, energy warnings, complexity traps', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: [] } },
1732
+ ];
1733
+ async function handleMCPToolCall(name, args) {
1734
+ switch (name) {
1735
+ case 'cell_get_dev_profile': {
1736
+ const { getDeveloperProfile } = require('../code-scanner');
1737
+ const profile = getDeveloperProfile(args.project);
1738
+ if (!profile)
1739
+ return { error: 'no profile — run cell scan first' };
1740
+ return { project: args.project, profile };
1741
+ }
1742
+ case 'cell_get_code_patterns': {
1743
+ const { getCodePatterns } = require('../code-scanner');
1744
+ return { project: args.project, patterns: getCodePatterns(args.project, args.category) };
1745
+ }
1746
+ case 'cell_deep_scan': {
1747
+ const { scanCodebase } = require('../code-scanner');
1748
+ const result = scanCodebase(args.dir, args.project || path.basename(args.dir));
1749
+ return { project: result.project, filesScanned: result.filesScanned, totalLines: result.totalLines, profile: result.profile, strengths: result.profile.strengths, improvements: result.profile.improvements };
1750
+ }
1751
+ case 'cell_scan_report': {
1752
+ const { getDeveloperProfile, getCodePatterns } = require('../code-scanner');
1753
+ const profile = getDeveloperProfile(args.project);
1754
+ const patterns = getCodePatterns(args.project);
1755
+ return { project: args.project, profile, patterns, patternCount: patterns.length };
1756
+ }
1757
+ case 'cell_send_signal': {
1758
+ const { logError, logDecision, logContext, logStuck } = require('../behavioral-tracker');
1759
+ const type = args.type;
1760
+ if (type === 'error')
1761
+ return logError({ project: args.project, file: args.file, errorType: 'manual', errorMessage: String(args.editedCode || ''), line: 0 });
1762
+ if (type === 'decision')
1763
+ return logDecision({ project: args.project, file: args.file, decision: String(args.originalCode || ''), approach: String(args.editedCode || ''), worked: true });
1764
+ if (type === 'context')
1765
+ return logContext({ project: args.project, task: String(args.originalCode || ''), files: [] });
1766
+ if (type === 'stuck')
1767
+ return logStuck({ project: args.project, file: args.file, description: String(args.originalCode || '') });
1768
+ return { recorded: true };
1769
+ }
1770
+ case 'cell_get_context': {
1771
+ const { buildContext, formatContextForInjection } = require('../core/prompt-builder');
1772
+ const ctx = buildContext(args.project, args.tool);
1773
+ return { content: [{ type: 'text', text: formatContextForInjection(ctx, false) }], context: ctx };
1774
+ }
1775
+ case 'cell_scan_full_pc': {
1776
+ const { scanFullPC } = require('../pc-scanner');
1777
+ const result = scanFullPC();
1778
+ return { totalProjects: result.totalProjects, totalFiles: result.totalFiles, totalLines: result.totalLines, profile: result.aggregatedProfile };
1779
+ }
1780
+ case 'cell_behavior_summary': {
1781
+ const { getBehaviorSummary } = require('../behavioral-tracker');
1782
+ return getBehaviorSummary(args.project);
1783
+ }
1784
+ case 'cell_log_error': {
1785
+ const { logError } = require('../behavioral-tracker');
1786
+ return logError({ project: args.project, file: args.file, errorType: args.errorType, errorMessage: args.errorMessage, line: args.line });
1787
+ }
1788
+ case 'cell_log_fix': {
1789
+ const { logErrorFix } = require('../behavioral-tracker');
1790
+ logErrorFix({ errorId: args.errorId, fixApplied: args.fixApplied, worked: args.worked, timeToFixMs: args.timeToFixMs });
1791
+ return { recorded: true };
1792
+ }
1793
+ case 'cell_log_decision': {
1794
+ const { logDecision } = require('../behavioral-tracker');
1795
+ return logDecision({ project: args.project, file: args.file, decision: args.decision, approach: args.approach, worked: args.worked });
1796
+ }
1797
+ case 'cell_log_context': {
1798
+ const { logContext } = require('../behavioral-tracker');
1799
+ return logContext({ project: args.project, task: args.task, files: args.files });
1800
+ }
1801
+ case 'cell_log_stuck': {
1802
+ const { logStuck } = require('../behavioral-tracker');
1803
+ return logStuck({ project: args.project, file: args.file, description: args.description });
1804
+ }
1805
+ case 'cell_session_bridge': {
1806
+ const { SessionMemory } = require('../core/session-memory');
1807
+ const memory = new SessionMemory();
1808
+ const project = args.project;
1809
+ const tool = args.tool || 'unknown';
1810
+ try {
1811
+ // Get last session for this project
1812
+ const lastSession = memory.getLastSessionForProject(project);
1813
+ // Start new session for current tool
1814
+ const newSessionId = memory.startSession(tool, project);
1815
+ // If there was a previous session with a different tool, bridge it
1816
+ let bridgeContext = '';
1817
+ if (lastSession && lastSession.toolName !== tool) {
1818
+ const decisions = lastSession.keyDecisions ? JSON.stringify(lastSession.keyDecisions) : 'none';
1819
+ bridgeContext = formatSessionBridge(lastSession.toolName, tool, lastSession.contextSnapshot, decisions);
1820
+ }
1821
+ // Get recent sessions for context chain
1822
+ const recentSessions = memory.getRecentSessions(3).filter((s) => s.project === project);
1823
+ const sessionHistory = recentSessions.map((s) => ({
1824
+ tool: s.toolName,
1825
+ when: s.startTime,
1826
+ files: JSON.parse(String(s.filesTouched || '[]')),
1827
+ decisions: JSON.parse(String(s.keyDecisions || '[]')),
1828
+ }));
1829
+ return {
1830
+ newSessionId,
1831
+ lastSession: lastSession ? {
1832
+ tool: lastSession.toolName,
1833
+ startedAt: lastSession.startTime,
1834
+ context: lastSession.contextSnapshot,
1835
+ } : null,
1836
+ bridgeContext,
1837
+ sessionHistory,
1838
+ message: bridgeContext || 'No previous session to bridge. Start a new session!',
1839
+ };
1840
+ }
1841
+ catch (err) {
1842
+ const msg = err instanceof Error ? err.message : String(err);
1843
+ return { error: `Session bridge failed: ${msg}`, crossedSession: false };
1844
+ }
1845
+ }
1846
+ case 'cell_predict': {
1847
+ const { generatePredictions } = require('../predictive-engine');
1848
+ return generatePredictions(args.project);
1849
+ }
1850
+ default:
1851
+ return { error: `unknown tool: ${name}` };
1852
+ }
1853
+ }
1854
+ app.post('/mcp', async (req, res) => {
1855
+ const rpc = req.body;
1856
+ const { method, params, id } = rpc;
1857
+ try {
1858
+ let result;
1859
+ switch (method) {
1860
+ case 'initialize':
1861
+ result = { protocolVersion: params?.protocolVersion || '2024-11-05', capabilities: { tools: { listChanged: false } }, serverInfo: { name: 'fivo-cell', version: '3.0.0' } };
1862
+ break;
1863
+ case 'notifications/initialized':
1864
+ res.status(204).end();
1865
+ return;
1866
+ case 'ping':
1867
+ result = {};
1868
+ break;
1869
+ case 'tools/list':
1870
+ result = { tools: MCP_TOOLS };
1871
+ break;
1872
+ case 'tools/call':
1873
+ result = await handleMCPToolCall(params?.name || '', params?.arguments || {});
1874
+ break;
1875
+ default:
1876
+ res.status(200).json({ jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${method}` } });
1877
+ return;
1878
+ }
1879
+ res.status(200).json({ jsonrpc: '2.0', id, result });
1880
+ }
1881
+ catch (err) {
1882
+ const message = err instanceof Error ? err.message : String(err);
1883
+ res.status(200).json({ jsonrpc: '2.0', id, error: { code: -1, message } });
1884
+ }
1885
+ });
1565
1886
  app.use((_req, res) => { res.status(404).json({ error: 'not found' }); });
1566
1887
  app.use((err, _req, res, _next) => {
1567
1888
  console.error('Daemon error:', err.message);
1568
1889
  res.status(500).json({ error: 'internal server error' });
1569
1890
  });
1891
+ // ─── Self-Healing ──────────────────────────────────────────────────────
1892
+ process.on('uncaughtException', (err) => {
1893
+ console.error('[cell] Uncaught:', err.message);
1894
+ // Don't exit — keep serving
1895
+ });
1896
+ process.on('unhandledRejection', (reason) => {
1897
+ console.error('[cell] Unhandled rejection:', String(reason));
1898
+ });
1570
1899
  try {
1571
1900
  (0, ai_memory_1.initializeMemory)();
1572
1901
  }
1573
1902
  catch (e) {
1574
1903
  console.error('AI memory init failed:', e);
1575
1904
  }
1576
- app.listen(PORT, () => {
1905
+ // ─── Start with port conflict handling ─────────────────────────────────
1906
+ const server = app.listen(PORT, () => {
1577
1907
  console.log(`Cell Daemon running on http://localhost:${PORT}`);
1578
- console.log(`Modules loaded: ${Object.keys(engines).length}`);
1908
+ console.log(`MCP endpoint: POST http://localhost:${PORT}/mcp`);
1579
1909
  console.log(`Health: http://localhost:${PORT}/health`);
1580
1910
  });
1911
+ server.on('error', (err) => {
1912
+ if (err.code === 'EADDRINUSE') {
1913
+ console.error(`[cell] Port ${PORT} in use. Kill existing daemon first: cell stop`);
1914
+ }
1915
+ else {
1916
+ console.error(`[cell] Server error: ${err.message}`);
1917
+ }
1918
+ });
1919
+ server.on('close', () => {
1920
+ console.log('[cell] Daemon stopped.');
1921
+ });
1922
+ // Graceful shutdown
1923
+ const gracefulShutdown = () => {
1924
+ console.log('[cell] Daemon shutting down...');
1925
+ server.close();
1926
+ process.exit(0);
1927
+ };
1928
+ process.on('SIGTERM', gracefulShutdown);
1929
+ process.on('SIGINT', gracefulShutdown);
1581
1930
  //# sourceMappingURL=server.js.map