fivocell 2.0.0 → 3.1.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 (55) 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/cross-model-engine.d.ts +38 -0
  19. package/dist/cross-model-engine.d.ts.map +1 -0
  20. package/dist/cross-model-engine.js +98 -0
  21. package/dist/cross-model-engine.js.map +1 -0
  22. package/dist/daemon/lifecycle.d.ts +0 -15
  23. package/dist/daemon/lifecycle.d.ts.map +1 -1
  24. package/dist/daemon/lifecycle.js +88 -231
  25. package/dist/daemon/lifecycle.js.map +1 -1
  26. package/dist/daemon/server.d.ts.map +1 -1
  27. package/dist/daemon/server.js +403 -19
  28. package/dist/daemon/server.js.map +1 -1
  29. package/dist/developer-intelligence.d.ts +18 -0
  30. package/dist/developer-intelligence.d.ts.map +1 -0
  31. package/dist/developer-intelligence.js +180 -0
  32. package/dist/developer-intelligence.js.map +1 -0
  33. package/dist/layers.d.ts +92 -0
  34. package/dist/layers.d.ts.map +1 -0
  35. package/dist/layers.js +226 -0
  36. package/dist/layers.js.map +1 -0
  37. package/dist/mcp-server.d.ts +194 -1842
  38. package/dist/mcp-server.d.ts.map +1 -1
  39. package/dist/mcp-server.js +169 -875
  40. package/dist/mcp-server.js.map +1 -1
  41. package/dist/pc-scanner.d.ts +46 -0
  42. package/dist/pc-scanner.d.ts.map +1 -0
  43. package/dist/pc-scanner.js +488 -0
  44. package/dist/pc-scanner.js.map +1 -0
  45. package/dist/predictive-engine.d.ts +19 -0
  46. package/dist/predictive-engine.d.ts.map +1 -0
  47. package/dist/predictive-engine.js +107 -0
  48. package/dist/predictive-engine.js.map +1 -0
  49. package/dist/style-pull.d.ts +1 -1
  50. package/dist/style-pull.js +2 -2
  51. package/dist/team-git.d.ts +47 -0
  52. package/dist/team-git.d.ts.map +1 -0
  53. package/dist/team-git.js +305 -0
  54. package/dist/team-git.js.map +1 -0
  55. package/package.json +1 -1
@@ -204,16 +204,121 @@ 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 === 'model_use') {
289
+ // Track AI model interaction
290
+ try {
291
+ const { recordModelInteraction } = require('../cross-model-engine');
292
+ recordModelInteraction({
293
+ modelName: req.body.modelName || 'unknown',
294
+ taskType: req.body.taskType || req.body.type || 'general',
295
+ accepted: req.body.accepted !== false,
296
+ responseTimeMs: req.body.responseTimeMs,
297
+ project,
298
+ suggestion: req.body.suggestion,
299
+ });
300
+ }
301
+ catch { }
302
+ res.json({ recorded: true });
303
+ return;
304
+ }
305
+ if (type === 'tab_switch' || type === 'focus_block' || type === 'file_save') {
306
+ try {
307
+ const db = (0, database_1.getDb)();
308
+ db.prepare('INSERT INTO behavior_events (event_type, project, file_path, description, severity, evidence) VALUES (?, ?, ?, ?, ?, ?)')
309
+ .run(type, '', file || '', JSON.stringify({ fromFile, costMs, durationMs, switchCount, language }), 'low', JSON.stringify(req.body));
310
+ }
311
+ catch { }
312
+ res.json({ recorded: true, type });
313
+ return;
314
+ }
315
+ // ─── Legacy Signals ──────────────────────────────────────────
316
+ const ctx = context || {};
212
317
  let signal;
213
318
  let patternsExtracted = 0;
214
319
  switch (type) {
215
320
  case 'edit_diff': {
216
- signal = engines.signalCapture.captureEditDiff(originalCode || '', editedCode || '', context);
321
+ signal = engines.signalCapture.captureEditDiff(originalCode || '', editedCode || '', ctx);
217
322
  try {
218
323
  const result = engines.extractionCascade.extract(originalCode || '', editedCode || '');
219
324
  patternsExtracted = engines.learningLayer.process(result, true);
@@ -226,35 +331,28 @@ app.post('/signal', (req, res) => {
226
331
  break;
227
332
  }
228
333
  case 'accept': {
229
- signal = engines.signalCapture.captureAccept(originalCode || '', context);
334
+ signal = engines.signalCapture.captureAccept(originalCode || '', ctx);
230
335
  const dummy = engines.patternStore.getAllPatterns().slice(0, 1);
231
336
  for (const p of dummy) {
232
337
  const step = (0, workflow_tracker_1.recordStep)(p, 'accept');
233
338
  (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
- });
339
+ (0, failure_memory_1.recordFailure)(p, false, ctx.language || 'unknown');
340
+ (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
341
  }
244
342
  break;
245
343
  }
246
344
  case 'reject': {
247
- signal = engines.signalCapture.captureReject(originalCode || '', context);
345
+ signal = engines.signalCapture.captureReject(originalCode || '', ctx);
248
346
  const dummyR = engines.patternStore.getAllPatterns().slice(0, 1);
249
347
  for (const p of dummyR) {
250
348
  const step = (0, workflow_tracker_1.recordStep)(p, 'reject');
251
349
  (0, outcome_recorder_1.recordOutcome)(p, 'reject', step.sessionId, false, 0);
252
- (0, failure_memory_1.recordFailure)(p, true, context?.language || 'unknown');
350
+ (0, failure_memory_1.recordFailure)(p, true, ctx.language || 'unknown');
253
351
  }
254
352
  break;
255
353
  }
256
354
  case 'retry': {
257
- signal = engines.signalCapture.captureRetry(originalCode || '', context);
355
+ signal = engines.signalCapture.captureRetry(originalCode || '', ctx);
258
356
  const dummyRt = engines.patternStore.getAllPatterns().slice(0, 1);
259
357
  for (const p of dummyRt) {
260
358
  const step = (0, workflow_tracker_1.recordStep)(p, 'retry');
@@ -1562,20 +1660,306 @@ app.post('/cloud/push', (req, res) => {
1562
1660
  pushToCloud(req.body?.data || {}, req.body?.type || 'pattern').then(r => res.json(r));
1563
1661
  }).catch(e => res.status(500).json({ error: String(e) }));
1564
1662
  });
1663
+ // ─── CODE SCANNER (Deep Learning) ──────────────────────
1664
+ app.post('/scan/codebase', (req, res) => {
1665
+ const { dir, project } = req.body;
1666
+ if (!dir) {
1667
+ res.status(400).json({ error: 'dir required' });
1668
+ return;
1669
+ }
1670
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ scanCodebase }) => {
1671
+ const result = scanCodebase(dir, project || require('path').basename(dir));
1672
+ res.json(result);
1673
+ }).catch(e => res.status(500).json({ error: String(e) }));
1674
+ });
1675
+ app.get('/scan/profile/:project', (req, res) => {
1676
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ getDeveloperProfile }) => {
1677
+ const profile = getDeveloperProfile(req.params.project);
1678
+ if (!profile) {
1679
+ res.status(404).json({ error: 'no profile — run scan first' });
1680
+ return;
1681
+ }
1682
+ res.json(profile);
1683
+ }).catch(e => res.status(500).json({ error: String(e) }));
1684
+ });
1685
+ app.get('/scan/patterns/:project', (req, res) => {
1686
+ const category = req.query.category;
1687
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ getCodePatterns }) => {
1688
+ res.json({ patterns: getCodePatterns(req.params.project, category) });
1689
+ }).catch(e => res.status(500).json({ error: String(e) }));
1690
+ });
1691
+ app.get('/scan/report/:project', (req, res) => {
1692
+ Promise.resolve().then(() => __importStar(require('../code-scanner'))).then(({ getDeveloperProfile, getCodePatterns }) => {
1693
+ const profile = getDeveloperProfile(req.params.project);
1694
+ const patterns = getCodePatterns(req.params.project);
1695
+ res.json({ project: req.params.project, profile, patterns, patternCount: patterns.length });
1696
+ }).catch(e => res.status(500).json({ error: String(e) }));
1697
+ });
1698
+ // ─── BEHAVIORAL TRACKING ──────────────────────────────────────
1699
+ app.get('/behavior/summary', (req, res) => {
1700
+ const { getBehaviorSummary } = require('../behavioral-tracker');
1701
+ res.json(getBehaviorSummary(String(req.query.project || undefined)));
1702
+ });
1703
+ app.get('/behavior/errors', (req, res) => {
1704
+ const { getErrorPatterns, getRecentErrors } = require('../behavioral-tracker');
1705
+ res.json({ patterns: getErrorPatterns(String(req.query.project || undefined)), recent: getRecentErrors(10, String(req.query.project || undefined)) });
1706
+ });
1707
+ app.get('/behavior/decisions', (req, res) => {
1708
+ const { getDecisions } = require('../behavioral-tracker');
1709
+ res.json({ decisions: getDecisions(String(req.query.project || undefined), 20) });
1710
+ });
1711
+ app.get('/behavior/context', (req, res) => {
1712
+ const { getCurrentContext } = require('../behavioral-tracker');
1713
+ res.json(getCurrentContext(String(req.query.project || undefined)) || { task: 'none', files: [] });
1714
+ });
1715
+ app.get('/behavior/stuck', (req, res) => {
1716
+ const { getStuckPatterns } = require('../behavioral-tracker');
1717
+ res.json({ stuck: getStuckPatterns(String(req.query.project || undefined)) });
1718
+ });
1719
+ // ─── Session Bridge Helper ────────────────────────────────────────────────
1720
+ function formatSessionBridge(fromTool, toTool, context, decisions) {
1721
+ const lines = [];
1722
+ lines.push(`═══ CROSS-SESSION BRIDGE ═══`);
1723
+ lines.push(`From: ${fromTool} → To: ${toTool}`);
1724
+ if (context)
1725
+ lines.push(`Last context: ${context}`);
1726
+ if (decisions && decisions !== 'none' && decisions !== '[]') {
1727
+ lines.push(`Decisions made: ${decisions}`);
1728
+ }
1729
+ lines.push(`Cell remembers. No need to re-explain.`);
1730
+ return lines.join('\n');
1731
+ }
1732
+ // ─── MCP ENDPOINTS (merged from mcp-server.ts) ─────────────────────────────
1733
+ const MCP_TOOLS = [
1734
+ { name: 'cell_get_dev_profile', description: 'Developer profile — naming, style, errors, architecture', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
1735
+ { name: 'cell_get_code_patterns', description: 'Code patterns by category', inputSchema: { type: 'object', properties: { project: { type: 'string' }, category: { type: 'string' } }, required: ['project'] } },
1736
+ { name: 'cell_deep_scan', description: 'Deep scan codebase', inputSchema: { type: 'object', properties: { dir: { type: 'string' }, project: { type: 'string' } }, required: ['dir'] } },
1737
+ { name: 'cell_scan_report', description: 'Full scan report', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
1738
+ { 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'] } },
1739
+ { name: 'cell_get_context', description: 'Full context injection', inputSchema: { type: 'object', properties: { project: { type: 'string' }, tool: { type: 'string' } }, required: ['project'] } },
1740
+ { name: 'cell_scan_full_pc', description: 'Scan entire PC for all code projects', inputSchema: { type: 'object', properties: {} } },
1741
+ { name: 'cell_behavior_summary', description: 'Behavioral summary', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
1742
+ { 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'] } },
1743
+ { 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'] } },
1744
+ { 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'] } },
1745
+ { 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'] } },
1746
+ { name: 'cell_log_stuck', description: 'Log when stuck', inputSchema: { type: 'object', properties: { project: { type: 'string' }, file: { type: 'string' }, description: { type: 'string' } }, required: ['description'] } },
1747
+ { 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'] } },
1748
+ { name: 'cell_predict', description: 'Predictive intelligence — repeat mistakes, decision regret, energy warnings, complexity traps', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: [] } },
1749
+ { name: 'cell_model_track', description: 'Track AI model interaction — which tool was used, what task, accepted/rejected', inputSchema: { type: 'object', properties: { modelName: { type: 'string' }, taskType: { type: 'string' }, accepted: { type: 'boolean' }, responseTimeMs: { type: 'number' }, project: { type: 'string' }, suggestion: { type: 'string' } }, required: ['modelName', 'taskType', 'accepted'] } },
1750
+ { name: 'cell_model_recommend', description: 'Get AI model recommendations — which tool works best for which task', inputSchema: { type: 'object', properties: {} } },
1751
+ ];
1752
+ async function handleMCPToolCall(name, args) {
1753
+ switch (name) {
1754
+ case 'cell_get_dev_profile': {
1755
+ const { getDeveloperProfile } = require('../code-scanner');
1756
+ const profile = getDeveloperProfile(args.project);
1757
+ if (!profile)
1758
+ return { error: 'no profile — run cell scan first' };
1759
+ return { project: args.project, profile };
1760
+ }
1761
+ case 'cell_get_code_patterns': {
1762
+ const { getCodePatterns } = require('../code-scanner');
1763
+ return { project: args.project, patterns: getCodePatterns(args.project, args.category) };
1764
+ }
1765
+ case 'cell_deep_scan': {
1766
+ const { scanCodebase } = require('../code-scanner');
1767
+ const result = scanCodebase(args.dir, args.project || path.basename(args.dir));
1768
+ return { project: result.project, filesScanned: result.filesScanned, totalLines: result.totalLines, profile: result.profile, strengths: result.profile.strengths, improvements: result.profile.improvements };
1769
+ }
1770
+ case 'cell_scan_report': {
1771
+ const { getDeveloperProfile, getCodePatterns } = require('../code-scanner');
1772
+ const profile = getDeveloperProfile(args.project);
1773
+ const patterns = getCodePatterns(args.project);
1774
+ return { project: args.project, profile, patterns, patternCount: patterns.length };
1775
+ }
1776
+ case 'cell_send_signal': {
1777
+ const { logError, logDecision, logContext, logStuck } = require('../behavioral-tracker');
1778
+ const type = args.type;
1779
+ if (type === 'error')
1780
+ return logError({ project: args.project, file: args.file, errorType: 'manual', errorMessage: String(args.editedCode || ''), line: 0 });
1781
+ if (type === 'decision')
1782
+ return logDecision({ project: args.project, file: args.file, decision: String(args.originalCode || ''), approach: String(args.editedCode || ''), worked: true });
1783
+ if (type === 'context')
1784
+ return logContext({ project: args.project, task: String(args.originalCode || ''), files: [] });
1785
+ if (type === 'stuck')
1786
+ return logStuck({ project: args.project, file: args.file, description: String(args.originalCode || '') });
1787
+ return { recorded: true };
1788
+ }
1789
+ case 'cell_get_context': {
1790
+ const { buildContext, formatContextForInjection } = require('../core/prompt-builder');
1791
+ const ctx = buildContext(args.project, args.tool);
1792
+ return { content: [{ type: 'text', text: formatContextForInjection(ctx, false) }], context: ctx };
1793
+ }
1794
+ case 'cell_scan_full_pc': {
1795
+ const { scanFullPC } = require('../pc-scanner');
1796
+ const result = scanFullPC();
1797
+ return { totalProjects: result.totalProjects, totalFiles: result.totalFiles, totalLines: result.totalLines, profile: result.aggregatedProfile };
1798
+ }
1799
+ case 'cell_behavior_summary': {
1800
+ const { getBehaviorSummary } = require('../behavioral-tracker');
1801
+ return getBehaviorSummary(args.project);
1802
+ }
1803
+ case 'cell_log_error': {
1804
+ const { logError } = require('../behavioral-tracker');
1805
+ return logError({ project: args.project, file: args.file, errorType: args.errorType, errorMessage: args.errorMessage, line: args.line });
1806
+ }
1807
+ case 'cell_log_fix': {
1808
+ const { logErrorFix } = require('../behavioral-tracker');
1809
+ logErrorFix({ errorId: args.errorId, fixApplied: args.fixApplied, worked: args.worked, timeToFixMs: args.timeToFixMs });
1810
+ return { recorded: true };
1811
+ }
1812
+ case 'cell_log_decision': {
1813
+ const { logDecision } = require('../behavioral-tracker');
1814
+ return logDecision({ project: args.project, file: args.file, decision: args.decision, approach: args.approach, worked: args.worked });
1815
+ }
1816
+ case 'cell_log_context': {
1817
+ const { logContext } = require('../behavioral-tracker');
1818
+ return logContext({ project: args.project, task: args.task, files: args.files });
1819
+ }
1820
+ case 'cell_log_stuck': {
1821
+ const { logStuck } = require('../behavioral-tracker');
1822
+ return logStuck({ project: args.project, file: args.file, description: args.description });
1823
+ }
1824
+ case 'cell_session_bridge': {
1825
+ const { SessionMemory } = require('../core/session-memory');
1826
+ const memory = new SessionMemory();
1827
+ const project = args.project;
1828
+ const tool = args.tool || 'unknown';
1829
+ try {
1830
+ // Get last session for this project
1831
+ const lastSession = memory.getLastSessionForProject(project);
1832
+ // Start new session for current tool
1833
+ const newSessionId = memory.startSession(tool, project);
1834
+ // If there was a previous session with a different tool, bridge it
1835
+ let bridgeContext = '';
1836
+ if (lastSession && lastSession.toolName !== tool) {
1837
+ const decisions = lastSession.keyDecisions ? JSON.stringify(lastSession.keyDecisions) : 'none';
1838
+ bridgeContext = formatSessionBridge(lastSession.toolName, tool, lastSession.contextSnapshot, decisions);
1839
+ }
1840
+ // Get recent sessions for context chain
1841
+ const recentSessions = memory.getRecentSessions(3).filter((s) => s.project === project);
1842
+ const sessionHistory = recentSessions.map((s) => ({
1843
+ tool: s.toolName,
1844
+ when: s.startTime,
1845
+ files: JSON.parse(String(s.filesTouched || '[]')),
1846
+ decisions: JSON.parse(String(s.keyDecisions || '[]')),
1847
+ }));
1848
+ return {
1849
+ newSessionId,
1850
+ lastSession: lastSession ? {
1851
+ tool: lastSession.toolName,
1852
+ startedAt: lastSession.startTime,
1853
+ context: lastSession.contextSnapshot,
1854
+ } : null,
1855
+ bridgeContext,
1856
+ sessionHistory,
1857
+ message: bridgeContext || 'No previous session to bridge. Start a new session!',
1858
+ };
1859
+ }
1860
+ catch (err) {
1861
+ const msg = err instanceof Error ? err.message : String(err);
1862
+ return { error: `Session bridge failed: ${msg}`, crossedSession: false };
1863
+ }
1864
+ }
1865
+ case 'cell_predict': {
1866
+ const { generatePredictions } = require('../predictive-engine');
1867
+ return generatePredictions(args.project);
1868
+ }
1869
+ case 'cell_model_track': {
1870
+ const { recordModelInteraction } = require('../cross-model-engine');
1871
+ recordModelInteraction({
1872
+ modelName: args.modelName,
1873
+ taskType: args.taskType,
1874
+ accepted: args.accepted,
1875
+ responseTimeMs: args.responseTimeMs,
1876
+ project: args.project,
1877
+ suggestion: args.suggestion,
1878
+ });
1879
+ return { recorded: true };
1880
+ }
1881
+ case 'cell_model_recommend': {
1882
+ const { getModelRecommendations } = require('../cross-model-engine');
1883
+ return { recommendations: getModelRecommendations() };
1884
+ }
1885
+ default:
1886
+ return { error: `unknown tool: ${name}` };
1887
+ }
1888
+ }
1889
+ app.post('/mcp', async (req, res) => {
1890
+ const rpc = req.body;
1891
+ const { method, params, id } = rpc;
1892
+ try {
1893
+ let result;
1894
+ switch (method) {
1895
+ case 'initialize':
1896
+ result = { protocolVersion: params?.protocolVersion || '2024-11-05', capabilities: { tools: { listChanged: false } }, serverInfo: { name: 'fivo-cell', version: '3.0.0' } };
1897
+ break;
1898
+ case 'notifications/initialized':
1899
+ res.status(204).end();
1900
+ return;
1901
+ case 'ping':
1902
+ result = {};
1903
+ break;
1904
+ case 'tools/list':
1905
+ result = { tools: MCP_TOOLS };
1906
+ break;
1907
+ case 'tools/call':
1908
+ result = await handleMCPToolCall(params?.name || '', params?.arguments || {});
1909
+ break;
1910
+ default:
1911
+ res.status(200).json({ jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${method}` } });
1912
+ return;
1913
+ }
1914
+ res.status(200).json({ jsonrpc: '2.0', id, result });
1915
+ }
1916
+ catch (err) {
1917
+ const message = err instanceof Error ? err.message : String(err);
1918
+ res.status(200).json({ jsonrpc: '2.0', id, error: { code: -1, message } });
1919
+ }
1920
+ });
1565
1921
  app.use((_req, res) => { res.status(404).json({ error: 'not found' }); });
1566
1922
  app.use((err, _req, res, _next) => {
1567
1923
  console.error('Daemon error:', err.message);
1568
1924
  res.status(500).json({ error: 'internal server error' });
1569
1925
  });
1926
+ // ─── Self-Healing ──────────────────────────────────────────────────────
1927
+ process.on('uncaughtException', (err) => {
1928
+ console.error('[cell] Uncaught:', err.message);
1929
+ // Don't exit — keep serving
1930
+ });
1931
+ process.on('unhandledRejection', (reason) => {
1932
+ console.error('[cell] Unhandled rejection:', String(reason));
1933
+ });
1570
1934
  try {
1571
1935
  (0, ai_memory_1.initializeMemory)();
1572
1936
  }
1573
1937
  catch (e) {
1574
1938
  console.error('AI memory init failed:', e);
1575
1939
  }
1576
- app.listen(PORT, () => {
1940
+ // ─── Start with port conflict handling ─────────────────────────────────
1941
+ const server = app.listen(PORT, () => {
1577
1942
  console.log(`Cell Daemon running on http://localhost:${PORT}`);
1578
- console.log(`Modules loaded: ${Object.keys(engines).length}`);
1943
+ console.log(`MCP endpoint: POST http://localhost:${PORT}/mcp`);
1579
1944
  console.log(`Health: http://localhost:${PORT}/health`);
1580
1945
  });
1946
+ server.on('error', (err) => {
1947
+ if (err.code === 'EADDRINUSE') {
1948
+ console.error(`[cell] Port ${PORT} in use. Kill existing daemon first: cell stop`);
1949
+ }
1950
+ else {
1951
+ console.error(`[cell] Server error: ${err.message}`);
1952
+ }
1953
+ });
1954
+ server.on('close', () => {
1955
+ console.log('[cell] Daemon stopped.');
1956
+ });
1957
+ // Graceful shutdown
1958
+ const gracefulShutdown = () => {
1959
+ console.log('[cell] Daemon shutting down...');
1960
+ server.close();
1961
+ process.exit(0);
1962
+ };
1963
+ process.on('SIGTERM', gracefulShutdown);
1964
+ process.on('SIGINT', gracefulShutdown);
1581
1965
  //# sourceMappingURL=server.js.map