fivocell 4.0.0 → 4.1.1
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/dist/__tests__/behavior-intelligence-bug.test.d.ts +2 -0
- package/dist/__tests__/behavior-intelligence-bug.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-intelligence-bug.test.js +21 -0
- package/dist/__tests__/behavior-intelligence-bug.test.js.map +1 -0
- package/dist/__tests__/code-scanner-arrow-return-type.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-arrow-return-type.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-arrow-return-type.test.js +76 -0
- package/dist/__tests__/code-scanner-arrow-return-type.test.js.map +1 -0
- package/dist/__tests__/code-scanner-blindspot.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-blindspot.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-blindspot.test.js +18 -0
- package/dist/__tests__/code-scanner-blindspot.test.js.map +1 -0
- package/dist/__tests__/code-scanner-error-recovery.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-error-recovery.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-error-recovery.test.js +21 -0
- package/dist/__tests__/code-scanner-error-recovery.test.js.map +1 -0
- package/dist/__tests__/code-scanner-n1-detection.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-n1-detection.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-n1-detection.test.js +113 -0
- package/dist/__tests__/code-scanner-n1-detection.test.js.map +1 -0
- package/dist/__tests__/code-scanner-nesting.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-nesting.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-nesting.test.js +113 -0
- package/dist/__tests__/code-scanner-nesting.test.js.map +1 -0
- package/dist/__tests__/code-scanner-null-check.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-null-check.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-null-check.test.js +126 -0
- package/dist/__tests__/code-scanner-null-check.test.js.map +1 -0
- package/dist/__tests__/code-scanner-sql-fix.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-sql-fix.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-sql-fix.test.js +21 -0
- package/dist/__tests__/code-scanner-sql-fix.test.js.map +1 -0
- package/dist/__tests__/code-scanner-trust-score.test.d.ts +1 -0
- package/dist/__tests__/code-scanner-trust-score.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-trust-score.test.js +39 -0
- package/dist/__tests__/code-scanner-trust-score.test.js.map +1 -0
- package/dist/__tests__/code-scanner-validation.test.d.ts +2 -0
- package/dist/__tests__/code-scanner-validation.test.d.ts.map +1 -0
- package/dist/__tests__/code-scanner-validation.test.js +131 -0
- package/dist/__tests__/code-scanner-validation.test.js.map +1 -0
- package/dist/__tests__/community-store.test.d.ts +2 -0
- package/dist/__tests__/community-store.test.d.ts.map +1 -0
- package/dist/__tests__/community-store.test.js +231 -0
- package/dist/__tests__/community-store.test.js.map +1 -0
- package/dist/__tests__/enhanced-blind-spots.test.d.ts +2 -0
- package/dist/__tests__/enhanced-blind-spots.test.d.ts.map +1 -0
- package/dist/__tests__/enhanced-blind-spots.test.js +347 -0
- package/dist/__tests__/enhanced-blind-spots.test.js.map +1 -0
- package/dist/__tests__/knowledge-graph-store.test.d.ts +2 -0
- package/dist/__tests__/knowledge-graph-store.test.d.ts.map +1 -0
- package/dist/__tests__/knowledge-graph-store.test.js +252 -0
- package/dist/__tests__/knowledge-graph-store.test.js.map +1 -0
- package/dist/__tests__/live-watcher.test.d.ts +2 -0
- package/dist/__tests__/live-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/live-watcher.test.js +312 -0
- package/dist/__tests__/live-watcher.test.js.map +1 -0
- package/dist/__tests__/mcp-cell-tools.test.d.ts +2 -0
- package/dist/__tests__/mcp-cell-tools.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-cell-tools.test.js +176 -0
- package/dist/__tests__/mcp-cell-tools.test.js.map +1 -0
- package/dist/__tests__/multi-project.test.d.ts +2 -0
- package/dist/__tests__/multi-project.test.d.ts.map +1 -0
- package/dist/__tests__/multi-project.test.js +145 -0
- package/dist/__tests__/multi-project.test.js.map +1 -0
- package/dist/__tests__/pc-scanner-paths.test.d.ts +2 -0
- package/dist/__tests__/pc-scanner-paths.test.d.ts.map +1 -0
- package/dist/__tests__/pc-scanner-paths.test.js +16 -0
- package/dist/__tests__/pc-scanner-paths.test.js.map +1 -0
- package/dist/__tests__/prompt-builder-realdata.test.d.ts +2 -0
- package/dist/__tests__/prompt-builder-realdata.test.d.ts.map +1 -0
- package/dist/__tests__/prompt-builder-realdata.test.js +94 -0
- package/dist/__tests__/prompt-builder-realdata.test.js.map +1 -0
- package/dist/__tests__/prompt-builder-sessions.test.d.ts +2 -0
- package/dist/__tests__/prompt-builder-sessions.test.d.ts.map +1 -0
- package/dist/__tests__/prompt-builder-sessions.test.js +124 -0
- package/dist/__tests__/prompt-builder-sessions.test.js.map +1 -0
- package/dist/__tests__/security.test.d.ts +1 -0
- package/dist/__tests__/security.test.d.ts.map +1 -0
- package/dist/__tests__/security.test.js +161 -0
- package/dist/__tests__/security.test.js.map +1 -0
- package/dist/__tests__/session-bridge.test.d.ts +2 -0
- package/dist/__tests__/session-bridge.test.d.ts.map +1 -0
- package/dist/__tests__/session-bridge.test.js +158 -0
- package/dist/__tests__/session-bridge.test.js.map +1 -0
- package/dist/__tests__/session-memory-tables.test.d.ts +2 -0
- package/dist/__tests__/session-memory-tables.test.d.ts.map +1 -0
- package/dist/__tests__/session-memory-tables.test.js +169 -0
- package/dist/__tests__/session-memory-tables.test.js.map +1 -0
- package/dist/__tests__/staleness-detection.test.d.ts +2 -0
- package/dist/__tests__/staleness-detection.test.d.ts.map +1 -0
- package/dist/__tests__/staleness-detection.test.js +105 -0
- package/dist/__tests__/staleness-detection.test.js.map +1 -0
- package/dist/__tests__/team-collaboration.test.d.ts +2 -0
- package/dist/__tests__/team-collaboration.test.d.ts.map +1 -0
- package/dist/__tests__/team-collaboration.test.js +224 -0
- package/dist/__tests__/team-collaboration.test.js.map +1 -0
- package/dist/__tests__/tool-specific-format.test.d.ts +2 -0
- package/dist/__tests__/tool-specific-format.test.d.ts.map +1 -0
- package/dist/__tests__/tool-specific-format.test.js +130 -0
- package/dist/__tests__/tool-specific-format.test.js.map +1 -0
- package/dist/__tests__/usage-intelligence-store.test.d.ts +2 -0
- package/dist/__tests__/usage-intelligence-store.test.d.ts.map +1 -0
- package/dist/__tests__/usage-intelligence-store.test.js +266 -0
- package/dist/__tests__/usage-intelligence-store.test.js.map +1 -0
- package/dist/behavior-intelligence.d.ts.map +1 -1
- package/dist/behavior-intelligence.js +12 -1
- package/dist/behavior-intelligence.js.map +1 -1
- package/dist/cli.js +403 -4
- package/dist/cli.js.map +1 -1
- package/dist/code-scanner.d.ts.map +1 -1
- package/dist/code-scanner.js +344 -102
- package/dist/code-scanner.js.map +1 -1
- package/dist/core/community-store.d.ts +128 -0
- package/dist/core/community-store.d.ts.map +1 -0
- package/dist/core/community-store.js +329 -0
- package/dist/core/community-store.js.map +1 -0
- package/dist/core/database.d.ts +0 -3
- package/dist/core/database.d.ts.map +1 -1
- package/dist/core/database.js +287 -15
- package/dist/core/database.js.map +1 -1
- package/dist/core/enhanced-blind-spots.d.ts +27 -0
- package/dist/core/enhanced-blind-spots.d.ts.map +1 -0
- package/dist/core/enhanced-blind-spots.js +710 -0
- package/dist/core/enhanced-blind-spots.js.map +1 -0
- package/dist/core/knowledge-graph-store.d.ts +69 -0
- package/dist/core/knowledge-graph-store.d.ts.map +1 -0
- package/dist/core/knowledge-graph-store.js +269 -0
- package/dist/core/knowledge-graph-store.js.map +1 -0
- package/dist/core/live-watcher.d.ts +52 -0
- package/dist/core/live-watcher.d.ts.map +1 -0
- package/dist/core/live-watcher.js +369 -0
- package/dist/core/live-watcher.js.map +1 -0
- package/dist/core/project-registry.d.ts +24 -0
- package/dist/core/project-registry.d.ts.map +1 -0
- package/dist/core/project-registry.js +70 -0
- package/dist/core/project-registry.js.map +1 -0
- package/dist/core/prompt-builder.d.ts +49 -0
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +540 -67
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/security.d.ts +23 -0
- package/dist/core/security.d.ts.map +1 -0
- package/dist/core/security.js +117 -0
- package/dist/core/security.js.map +1 -0
- package/dist/core/session-memory.d.ts +64 -0
- package/dist/core/session-memory.d.ts.map +1 -1
- package/dist/core/session-memory.js +111 -0
- package/dist/core/session-memory.js.map +1 -1
- package/dist/core/syntax-engine.d.ts.map +1 -1
- package/dist/core/syntax-engine.js +5 -2
- package/dist/core/syntax-engine.js.map +1 -1
- package/dist/core/usage-intelligence-store.d.ts +126 -0
- package/dist/core/usage-intelligence-store.d.ts.map +1 -0
- package/dist/core/usage-intelligence-store.js +405 -0
- package/dist/core/usage-intelligence-store.js.map +1 -0
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +936 -17
- package/dist/daemon/server.js.map +1 -1
- package/dist/knowledge-graph-builder.d.ts.map +1 -1
- package/dist/knowledge-graph-builder.js +18 -2
- package/dist/knowledge-graph-builder.js.map +1 -1
- package/dist/pc-scanner.d.ts +1 -0
- package/dist/pc-scanner.d.ts.map +1 -1
- package/dist/pc-scanner.js +64 -33
- package/dist/pc-scanner.js.map +1 -1
- package/package.json +1 -1
package/dist/daemon/server.js
CHANGED
|
@@ -117,6 +117,12 @@ const database_1 = require("../core/database");
|
|
|
117
117
|
const logger_1 = require("../core/logger");
|
|
118
118
|
const CELL_DIR = path.join(os.homedir(), '.fivo', 'cell');
|
|
119
119
|
(0, database_1.initializeDatabase)();
|
|
120
|
+
try {
|
|
121
|
+
require('../behavioral-tracker').initBehaviorTables();
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
console.error('Behavior tables init failed:', e);
|
|
125
|
+
}
|
|
120
126
|
const META_PATH = path.join(CELL_DIR, 'cell-meta.json');
|
|
121
127
|
const PORT = 9876;
|
|
122
128
|
function getCloudConfig() {
|
|
@@ -132,18 +138,19 @@ function getCloudConfig() {
|
|
|
132
138
|
const app = (0, express_1.default)();
|
|
133
139
|
exports.app = app;
|
|
134
140
|
// All 40 modules instantiated — verifies they work in daemon context
|
|
141
|
+
const sharedPatternStore = new pattern_store_1.PatternStore(CELL_DIR);
|
|
135
142
|
const engines = {
|
|
136
143
|
signalCapture: new signal_capture_1.SignalCapture(),
|
|
137
|
-
patternStore:
|
|
144
|
+
patternStore: sharedPatternStore,
|
|
138
145
|
learningLayer: null,
|
|
139
146
|
confidenceEngine: new confidence_engine_1.ConfidenceEngine(),
|
|
140
147
|
extractionCascade: new extraction_cascade_1.ExtractionCascade(),
|
|
141
148
|
decayEngine: new decay_engine_1.DecayEngine(),
|
|
142
|
-
configAttribution: new config_attribution_1.ConfigAttribution(
|
|
149
|
+
configAttribution: new config_attribution_1.ConfigAttribution(CELL_DIR),
|
|
143
150
|
communityPrior: new community_prior_1.CommunityPrior(CELL_DIR),
|
|
144
151
|
communityLive: new community_live_1.CommunityLive(CELL_DIR),
|
|
145
152
|
communityFull: new community_full_1.CommunityFull(CELL_DIR),
|
|
146
|
-
teamComposer: new team_composer_1.TeamComposer(
|
|
153
|
+
teamComposer: new team_composer_1.TeamComposer(sharedPatternStore, CELL_DIR),
|
|
147
154
|
conflictDetector: new conflict_detector_1.ConflictDetector(CELL_DIR),
|
|
148
155
|
promptConditioner: new prompt_conditioner_1.PromptConditioner(),
|
|
149
156
|
buildFailurePredictor: new build_failure_predictor_1.BuildFailurePredictor(),
|
|
@@ -154,7 +161,7 @@ const engines = {
|
|
|
154
161
|
mergeConflictPredictor: new merge_conflict_predictor_1.MergeConflictPredictor(),
|
|
155
162
|
testAnticipator: new test_anticipator_1.TestAnticipator(),
|
|
156
163
|
contextRestore: new context_restore_1.ContextRestore(CELL_DIR),
|
|
157
|
-
crossProjectSync: new cross_project_sync_1.CrossProjectSync(
|
|
164
|
+
crossProjectSync: new cross_project_sync_1.CrossProjectSync(sharedPatternStore, CELL_DIR),
|
|
158
165
|
solutionIndex: new solution_index_1.SolutionIndex(CELL_DIR),
|
|
159
166
|
languageSwitchAssistant: new language_switch_assistant_1.LanguageSwitchAssistant(),
|
|
160
167
|
interruptionRecovery: new interruption_recovery_1.InterruptionRecovery(CELL_DIR),
|
|
@@ -189,7 +196,7 @@ if (cloudConfig.cloudSyncEnabled) {
|
|
|
189
196
|
if (patterns.length > 0)
|
|
190
197
|
(0, ws_client_1.streamPatterns)(patterns);
|
|
191
198
|
}
|
|
192
|
-
app.use((0, cors_1.default)({ origin:
|
|
199
|
+
app.use((0, cors_1.default)({ origin: ['http://localhost', 'http://127.0.0.1'], credentials: false }));
|
|
193
200
|
app.use(express_1.default.json({ limit: '1mb' }));
|
|
194
201
|
app.use((0, morgan_1.default)('short'));
|
|
195
202
|
app.get('/health', (_req, res) => {
|
|
@@ -1717,15 +1724,38 @@ app.get('/behavior/stuck', (req, res) => {
|
|
|
1717
1724
|
res.json({ stuck: getStuckPatterns(String(req.query.project || undefined)) });
|
|
1718
1725
|
});
|
|
1719
1726
|
// ─── Session Bridge Helper ────────────────────────────────────────────────
|
|
1720
|
-
function formatSessionBridge(fromTool, toTool, context, decisions) {
|
|
1727
|
+
function formatSessionBridge(fromTool, toTool, context, decisions, options) {
|
|
1721
1728
|
const lines = [];
|
|
1722
1729
|
lines.push(`═══ CROSS-SESSION BRIDGE ═══`);
|
|
1723
1730
|
lines.push(`From: ${fromTool} → To: ${toTool}`);
|
|
1724
1731
|
if (context)
|
|
1725
1732
|
lines.push(`Last context: ${context}`);
|
|
1726
|
-
if (decisions && decisions !== 'none' && decisions !== '[]') {
|
|
1733
|
+
if (decisions && decisions !== 'none' && decisions !== '[]' && decisions !== 'undefined') {
|
|
1727
1734
|
lines.push(`Decisions made: ${decisions}`);
|
|
1728
1735
|
}
|
|
1736
|
+
if (options?.triedApproaches && options.triedApproaches.length > 0) {
|
|
1737
|
+
lines.push('');
|
|
1738
|
+
lines.push('🔄 TRIED ALREADY (don\'t suggest these):');
|
|
1739
|
+
for (const a of options.triedApproaches.slice(0, 5)) {
|
|
1740
|
+
const countSuffix = a.count > 1 ? ` (${a.count}x)` : '';
|
|
1741
|
+
lines.push(` → ${a.approach} — ${a.status}${countSuffix}`);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
if (options?.openQuestions && options.openQuestions.length > 0) {
|
|
1745
|
+
lines.push('');
|
|
1746
|
+
lines.push('💭 OPEN QUESTIONS (unresolved):');
|
|
1747
|
+
for (const q of options.openQuestions.slice(0, 5)) {
|
|
1748
|
+
lines.push(` → [${q.priority}] ${q.question}`);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
if (options?.hotFiles && options.hotFiles.length > 0) {
|
|
1752
|
+
lines.push('');
|
|
1753
|
+
lines.push('🔥 HOT FILES (recently active):');
|
|
1754
|
+
for (const f of options.hotFiles.slice(0, 5)) {
|
|
1755
|
+
lines.push(` → ${f.filePath} (${f.touchCount} touches)`);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
lines.push('');
|
|
1729
1759
|
lines.push(`Cell remembers. No need to re-explain.`);
|
|
1730
1760
|
return lines.join('\n');
|
|
1731
1761
|
}
|
|
@@ -1745,6 +1775,56 @@ const MCP_TOOLS = [
|
|
|
1745
1775
|
{ 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
1776
|
{ name: 'cell_log_stuck', description: 'Log when stuck', inputSchema: { type: 'object', properties: { project: { type: 'string' }, file: { type: 'string' }, description: { type: 'string' } }, required: ['description'] } },
|
|
1747
1777
|
{ 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'] } },
|
|
1778
|
+
{ name: 'cell_session_end', description: 'End a session — save final files, decisions, context snapshot', inputSchema: { type: 'object', properties: { sessionId: { type: 'number' }, filesTouched: { type: 'array', items: { type: 'string' } }, keyDecisions: { type: 'array', items: { type: 'string' } }, contextSnapshot: { type: 'string' } }, required: ['sessionId'] } },
|
|
1779
|
+
{ name: 'cell_session_log', description: 'Log session activity — approach (tried/failed), file (touched), question (open), resolve (answered)', inputSchema: { type: 'object', properties: { type: { type: 'string' }, sessionId: { type: 'number' }, project: { type: 'string' }, approach: { type: 'string' }, status: { type: 'string' }, reason: { type: 'string' }, filePath: { type: 'string' }, question: { type: 'string' }, priority: { type: 'string' }, questionId: { type: 'number' }, answer: { type: 'string' } }, required: ['type'] } },
|
|
1780
|
+
{ name: 'cell_session_start', description: 'Start a new session explicitly — returns session ID for tracking', inputSchema: { type: 'object', properties: { project: { type: 'string' }, tool: { type: 'string' } }, required: ['project', 'tool'] } },
|
|
1781
|
+
{ name: 'cell_session_status', description: 'Get current status of a session — files, approaches, active state', inputSchema: { type: 'object', properties: { sessionId: { type: 'number' } }, required: ['sessionId'] } },
|
|
1782
|
+
{ name: 'cell_session_recent', description: 'Get recent sessions for a project (or all projects)', inputSchema: { type: 'object', properties: { project: { type: 'string' }, limit: { type: 'number' } }, required: [] } },
|
|
1783
|
+
{ name: 'cell_session_recommendations', description: 'Get AI guidance — what to avoid, open questions, hot files for context', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1784
|
+
{ name: 'cell_list_projects', description: 'List all registered projects in the workspace', inputSchema: { type: 'object', properties: {} } },
|
|
1785
|
+
{ name: 'cell_get_project', description: 'Get full metadata for a project — stack, file count, last scan', inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] } },
|
|
1786
|
+
{ name: 'cell_detect_project', description: 'Auto-detect project from current working directory path', inputSchema: { type: 'object', properties: { cwd: { type: 'string' } }, required: ['cwd'] } },
|
|
1787
|
+
{ name: 'cell_register_project', description: 'Register a new project explicitly (auto-detected on scan too)', inputSchema: { type: 'object', properties: { name: { type: 'string' }, path: { type: 'string' }, stack: { type: 'string' }, fileCount: { type: 'number' } }, required: ['name', 'path'] } },
|
|
1788
|
+
{ name: 'cell_graph_files', description: 'Get file dependency graph (complexity, coupling, risk) for project', inputSchema: { type: 'object', properties: { project: { type: 'string' }, limit: { type: 'number', description: 'Max files to return (default 20)' } }, required: ['project'] } },
|
|
1789
|
+
{ name: 'cell_graph_blast_radius', description: 'Compute blast radius — which files break if I change this one?', inputSchema: { type: 'object', properties: { project: { type: 'string' }, file: { type: 'string' } }, required: ['project', 'file'] } },
|
|
1790
|
+
{ name: 'cell_graph_tech', description: 'Show technology proficiency (languages, frameworks, architecture)', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1791
|
+
{ name: 'cell_graph_stats', description: 'Get knowledge graph statistics (file + pattern + tech nodes/edges)', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1792
|
+
{ name: 'cell_team_silos', description: 'Detect knowledge silos (bus factor risks) in the team', inputSchema: { type: 'object', properties: {} } },
|
|
1793
|
+
{ name: 'cell_team_bus_factor', description: 'Calculate bus factor report — who knows what, single points of failure', inputSchema: { type: 'object', properties: {} } },
|
|
1794
|
+
{ name: 'cell_team_health', description: 'Team health score (bus factor, silos, skill gaps, strengths)', inputSchema: { type: 'object', properties: { project: { type: 'string' } } } },
|
|
1795
|
+
{ name: 'cell_team_onboard', description: 'Generate onboarding doc for new team member', inputSchema: { type: 'object', properties: { teamName: { type: 'string' }, project: { type: 'string' } }, required: ['teamName'] } },
|
|
1796
|
+
{ name: 'cell_team_retro', description: 'Sprint retrospective (commits, files, contributors, key decisions)', inputSchema: { type: 'object', properties: { days: { type: 'number', description: 'Days to look back (default 14)' }, project: { type: 'string' } } } },
|
|
1797
|
+
{ name: 'cell_team_handoff', description: 'Generate handoff context when transferring work between team members', inputSchema: { type: 'object', properties: { from: { type: 'string' }, to: { type: 'string' }, project: { type: 'string' } }, required: ['from', 'to', 'project'] } },
|
|
1798
|
+
{ name: 'cell_team_knowledge_transfer', description: 'Suggest knowledge transfer pairs to reduce bus factor risk', inputSchema: { type: 'object', properties: {} } },
|
|
1799
|
+
{ name: 'cell_team_style', description: 'Compose team coding style (languages, patterns, conventions)', inputSchema: { type: 'object', properties: { project: { type: 'string' } } } },
|
|
1800
|
+
{ name: 'cell_team_dashboard', description: 'Combined team intelligence — silos, bus factor, health, style, all in one call', inputSchema: { type: 'object', properties: { project: { type: 'string' } } } },
|
|
1801
|
+
{ name: 'cell_community_stats', description: 'Community-wide stats: total patterns, insights, benchmarks, top contributors', inputSchema: { type: 'object', properties: {} } },
|
|
1802
|
+
{ name: 'cell_community_share', description: 'Share an anonymized pattern with the community (privacy-validated)', inputSchema: { type: 'object', properties: { category: { type: 'string' }, rule: { type: 'string' }, language: { type: 'string' }, framework: { type: 'string' }, successRate: { type: 'number' } }, required: ['category', 'rule'] } },
|
|
1803
|
+
{ name: 'cell_community_patterns', description: 'Browse community patterns (top voted, by category, by language)', inputSchema: { type: 'object', properties: { category: { type: 'string' }, language: { type: 'string' }, minVotes: { type: 'number' }, limit: { type: 'number' } } } },
|
|
1804
|
+
{ name: 'cell_community_vote', description: 'Upvote or downvote a community pattern', inputSchema: { type: 'object', properties: { patternId: { type: 'string' }, upvote: { type: 'boolean' } }, required: ['patternId', 'upvote'] } },
|
|
1805
|
+
{ name: 'cell_community_insights', description: 'Get community insights (failure patterns, security warnings, best practices)', inputSchema: { type: 'object', properties: { severity: { type: 'string', enum: ['critical', 'warning', 'info'] }, category: { type: 'string' }, limit: { type: 'number' } } } },
|
|
1806
|
+
{ name: 'cell_community_benchmarks', description: 'Get benchmarks — your code vs community averages', inputSchema: { type: 'object', properties: { category: { type: 'string' }, limit: { type: 'number' } } } },
|
|
1807
|
+
{ name: 'cell_community_trends', description: 'Technology evolution signals — adoption phase, 30d/90d trends, migrations', inputSchema: { type: 'object', properties: {} } },
|
|
1808
|
+
{ name: 'cell_community_failures', description: 'Failure pattern library — most common errors by language with mitigations', inputSchema: { type: 'object', properties: { language: { type: 'string' }, limit: { type: 'number' } } } },
|
|
1809
|
+
{ name: 'cell_community_validate', description: 'Validate a rule string for privacy safety before sharing', inputSchema: { type: 'object', properties: { rule: { type: 'string' } }, required: ['rule'] } },
|
|
1810
|
+
{ name: 'cell_community_dashboard', description: 'Combined community dashboard — stats + trends + insights + benchmarks', inputSchema: { type: 'object', properties: {} } },
|
|
1811
|
+
{ name: 'cell_usage_decision_log', description: 'Log a decision with outcome (was it the right call?)', inputSchema: { type: 'object', properties: { project: { type: 'string' }, technology: { type: 'string' }, reason: { type: 'string' }, outcome: { type: 'string' }, wasRight: { type: 'boolean' }, confidence: { type: 'number' } }, required: ['project', 'technology', 'wasRight'] } },
|
|
1812
|
+
{ name: 'cell_usage_decision_patterns', description: 'Detect decision patterns — e.g. always picks MongoDB, always regrets it', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1813
|
+
{ name: 'cell_usage_repeat_mistakes', description: 'Find repeated error patterns — "you keep making this mistake" alerts', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1814
|
+
{ name: 'cell_usage_ai_stats', description: 'AI interaction stats — acceptance rate, iterations, recommendations per model', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1815
|
+
{ name: 'cell_usage_productivity', description: 'Productivity signals — flow state, friction, context switches', inputSchema: { type: 'object', properties: { project: { type: 'string' }, signalType: { type: 'string', enum: ['flow', 'friction', 'context_switch', 'long_session'] }, durationMinutes: { type: 'number' }, filesTouched: { type: 'number' }, contextSwitches: { type: 'number' } }, required: ['project'] } },
|
|
1816
|
+
{ name: 'cell_usage_skills', description: 'Skill progression map — improving/regressing/plateau per technology', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1817
|
+
{ name: 'cell_usage_burnout', description: 'Burnout risk detection from session patterns and friction signals', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1818
|
+
{ name: 'cell_usage_dashboard', description: 'Combined usage intelligence dashboard — decisions + mistakes + AI + productivity + skills + burnout', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1819
|
+
{ name: 'cell_watch_start', description: 'Start live file watcher for a project directory', inputSchema: { type: 'object', properties: { project: { type: 'string' }, dir: { type: 'string' } }, required: ['project', 'dir'] } },
|
|
1820
|
+
{ name: 'cell_watch_stop', description: 'Stop the live file watcher for a project', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1821
|
+
{ name: 'cell_watch_status', description: 'Get watcher status (active, event count, last event)', inputSchema: { type: 'object', properties: { project: { type: 'string' } } } },
|
|
1822
|
+
{ name: 'cell_watch_events', description: 'Get recent live events (file save/create/delete) for a project', inputSchema: { type: 'object', properties: { project: { type: 'string' }, limit: { type: 'number' }, eventType: { type: 'string', enum: ['file_save', 'file_create', 'file_delete', 'file_rename', 'git_commit', 'package_change'] } } } },
|
|
1823
|
+
{ name: 'cell_watch_refresh', description: 'Manually trigger auto-refresh; force=true to bypass staleness check', inputSchema: { type: 'object', properties: { project: { type: 'string' }, force: { type: 'boolean' } }, required: ['project'] } },
|
|
1824
|
+
{ name: 'cell_watch_check', description: 'Check if project needs refresh (staleness detection)', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: ['project'] } },
|
|
1825
|
+
{ name: 'cell_blindspots_scan', description: 'Scan a directory for 15+ types of blind spots (error handling, null safety, perf, quality, etc.)', inputSchema: { type: 'object', properties: { dir: { type: 'string' }, maxFiles: { type: 'number' } } } },
|
|
1826
|
+
{ name: 'cell_blindspots_report', description: 'Get a formatted blind spot report with severity groupings and recommendations', inputSchema: { type: 'object', properties: { dir: { type: 'string' }, maxFiles: { type: 'number' } } } },
|
|
1827
|
+
{ name: 'cell_blindspots_summary', description: 'Get blind spot counts by category only (quick check)', inputSchema: { type: 'object', properties: { dir: { type: 'string' } } } },
|
|
1748
1828
|
{ name: 'cell_predict', description: 'Predictive intelligence — repeat mistakes, decision regret, energy warnings, complexity traps', inputSchema: { type: 'object', properties: { project: { type: 'string' } }, required: [] } },
|
|
1749
1829
|
{ 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
1830
|
{ name: 'cell_model_recommend', description: 'Get AI model recommendations — which tool works best for which task', inputSchema: { type: 'object', properties: {} } },
|
|
@@ -1787,9 +1867,10 @@ async function handleMCPToolCall(name, args) {
|
|
|
1787
1867
|
return { recorded: true };
|
|
1788
1868
|
}
|
|
1789
1869
|
case 'cell_get_context': {
|
|
1790
|
-
const { buildContext,
|
|
1870
|
+
const { buildContext, formatContextForTool } = require('../core/prompt-builder');
|
|
1791
1871
|
const ctx = buildContext(args.project, args.tool);
|
|
1792
|
-
|
|
1872
|
+
const toolName = args.tool || (args.project ? 'claude-code' : undefined);
|
|
1873
|
+
return { content: [{ type: 'text', text: formatContextForTool(ctx, toolName) }], context: ctx };
|
|
1793
1874
|
}
|
|
1794
1875
|
case 'cell_scan_full_pc': {
|
|
1795
1876
|
const { scanFullPC } = require('../pc-scanner');
|
|
@@ -1827,23 +1908,26 @@ async function handleMCPToolCall(name, args) {
|
|
|
1827
1908
|
const project = args.project;
|
|
1828
1909
|
const tool = args.tool || 'unknown';
|
|
1829
1910
|
try {
|
|
1830
|
-
// Get last session for this project
|
|
1831
1911
|
const lastSession = memory.getLastSessionForProject(project);
|
|
1832
|
-
// Start new session for current tool
|
|
1833
1912
|
const newSessionId = memory.startSession(tool, project);
|
|
1834
|
-
// If there was a previous session with a different tool, bridge it
|
|
1835
1913
|
let bridgeContext = '';
|
|
1836
1914
|
if (lastSession && lastSession.toolName !== tool) {
|
|
1837
|
-
const
|
|
1838
|
-
|
|
1915
|
+
const triedApproaches = memory.getRecentApproaches(project, 5);
|
|
1916
|
+
const openQuestions = memory.getOpenQuestions(project);
|
|
1917
|
+
const hotFiles = memory.getMostTouchedFiles(project, 5);
|
|
1918
|
+
const decisions = Array.isArray(lastSession.keyDecisions) ? JSON.stringify(lastSession.keyDecisions) : 'none';
|
|
1919
|
+
bridgeContext = formatSessionBridge(lastSession.toolName, tool, lastSession.contextSnapshot, decisions, {
|
|
1920
|
+
triedApproaches,
|
|
1921
|
+
openQuestions,
|
|
1922
|
+
hotFiles,
|
|
1923
|
+
});
|
|
1839
1924
|
}
|
|
1840
|
-
// Get recent sessions for context chain
|
|
1841
1925
|
const recentSessions = memory.getRecentSessions(3).filter((s) => s.project === project);
|
|
1842
1926
|
const sessionHistory = recentSessions.map((s) => ({
|
|
1843
1927
|
tool: s.toolName,
|
|
1844
1928
|
when: s.startTime,
|
|
1845
|
-
files:
|
|
1846
|
-
decisions:
|
|
1929
|
+
files: s.filesTouched,
|
|
1930
|
+
decisions: s.keyDecisions,
|
|
1847
1931
|
}));
|
|
1848
1932
|
return {
|
|
1849
1933
|
newSessionId,
|
|
@@ -1854,6 +1938,9 @@ async function handleMCPToolCall(name, args) {
|
|
|
1854
1938
|
} : null,
|
|
1855
1939
|
bridgeContext,
|
|
1856
1940
|
sessionHistory,
|
|
1941
|
+
triedApproaches: memory.getRecentApproaches(project, 5),
|
|
1942
|
+
openQuestions: memory.getOpenQuestions(project),
|
|
1943
|
+
hotFiles: memory.getMostTouchedFiles(project, 5),
|
|
1857
1944
|
message: bridgeContext || 'No previous session to bridge. Start a new session!',
|
|
1858
1945
|
};
|
|
1859
1946
|
}
|
|
@@ -1862,6 +1949,819 @@ async function handleMCPToolCall(name, args) {
|
|
|
1862
1949
|
return { error: `Session bridge failed: ${msg}`, crossedSession: false };
|
|
1863
1950
|
}
|
|
1864
1951
|
}
|
|
1952
|
+
case 'cell_session_end': {
|
|
1953
|
+
const { SessionMemory } = require('../core/session-memory');
|
|
1954
|
+
const memory = new SessionMemory();
|
|
1955
|
+
const sessionId = args.sessionId;
|
|
1956
|
+
try {
|
|
1957
|
+
memory.endSession(sessionId, {
|
|
1958
|
+
filesTouched: Array.isArray(args.filesTouched) ? args.filesTouched : [],
|
|
1959
|
+
keyDecisions: Array.isArray(args.keyDecisions) ? args.keyDecisions : [],
|
|
1960
|
+
contextSnapshot: args.contextSnapshot || '',
|
|
1961
|
+
});
|
|
1962
|
+
return { ended: true, sessionId };
|
|
1963
|
+
}
|
|
1964
|
+
catch (err) {
|
|
1965
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1966
|
+
return { error: `Session end failed: ${msg}`, ended: false };
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
case 'cell_session_log': {
|
|
1970
|
+
const { SessionMemory } = require('../core/session-memory');
|
|
1971
|
+
const memory = new SessionMemory();
|
|
1972
|
+
try {
|
|
1973
|
+
const sessionId = args.sessionId || 0;
|
|
1974
|
+
const project = args.project || 'unknown';
|
|
1975
|
+
if (args.type === 'approach' && args.approach) {
|
|
1976
|
+
const id = memory.recordApproach({
|
|
1977
|
+
sessionId: sessionId || undefined,
|
|
1978
|
+
project,
|
|
1979
|
+
approach: args.approach,
|
|
1980
|
+
status: args.status || 'tried',
|
|
1981
|
+
reason: args.reason,
|
|
1982
|
+
});
|
|
1983
|
+
return { recorded: 'approach', id };
|
|
1984
|
+
}
|
|
1985
|
+
if (args.type === 'file' && args.filePath) {
|
|
1986
|
+
memory.recordFileTouch({ sessionId: sessionId || undefined, project, filePath: args.filePath });
|
|
1987
|
+
return { recorded: 'file' };
|
|
1988
|
+
}
|
|
1989
|
+
if (args.type === 'question' && args.question) {
|
|
1990
|
+
const id = memory.recordQuestion({
|
|
1991
|
+
sessionId: sessionId || undefined,
|
|
1992
|
+
project,
|
|
1993
|
+
question: args.question,
|
|
1994
|
+
priority: args.priority || 'medium',
|
|
1995
|
+
});
|
|
1996
|
+
return { recorded: 'question', id };
|
|
1997
|
+
}
|
|
1998
|
+
if (args.type === 'resolve' && args.questionId) {
|
|
1999
|
+
memory.resolveQuestion(args.questionId, args.answer || '');
|
|
2000
|
+
return { recorded: 'resolve' };
|
|
2001
|
+
}
|
|
2002
|
+
return { error: 'unknown log type or missing field' };
|
|
2003
|
+
}
|
|
2004
|
+
catch (err) {
|
|
2005
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2006
|
+
return { error: `Session log failed: ${msg}` };
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
case 'cell_session_start': {
|
|
2010
|
+
const { SessionMemory } = require('../core/session-memory');
|
|
2011
|
+
const memory = new SessionMemory();
|
|
2012
|
+
try {
|
|
2013
|
+
const project = args.project || 'unknown';
|
|
2014
|
+
const tool = args.tool || 'unknown';
|
|
2015
|
+
const sessionId = memory.startSession(tool, project);
|
|
2016
|
+
return {
|
|
2017
|
+
sessionId,
|
|
2018
|
+
project,
|
|
2019
|
+
tool,
|
|
2020
|
+
startedAt: new Date().toISOString(),
|
|
2021
|
+
message: `Session ${sessionId} started for ${project} on ${tool}`,
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
catch (err) {
|
|
2025
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2026
|
+
return { error: `Session start failed: ${msg}` };
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
case 'cell_session_status': {
|
|
2030
|
+
const { SessionMemory } = require('../core/session-memory');
|
|
2031
|
+
const memory = new SessionMemory();
|
|
2032
|
+
try {
|
|
2033
|
+
const sessionId = args.sessionId;
|
|
2034
|
+
const db = (0, database_1.getDb)();
|
|
2035
|
+
const sess = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);
|
|
2036
|
+
if (!sess)
|
|
2037
|
+
return { error: 'Session not found', sessionId };
|
|
2038
|
+
const files = memory.getSessionFiles(sessionId);
|
|
2039
|
+
const approaches = memory.getSessionApproaches(sessionId);
|
|
2040
|
+
const isActive = !sess.end_time;
|
|
2041
|
+
return {
|
|
2042
|
+
sessionId,
|
|
2043
|
+
tool: String(sess.tool_name || ''),
|
|
2044
|
+
project: String(sess.project || ''),
|
|
2045
|
+
startTime: String(sess.start_time || ''),
|
|
2046
|
+
endTime: sess.end_time ? String(sess.end_time) : null,
|
|
2047
|
+
isActive,
|
|
2048
|
+
filesCount: files.length,
|
|
2049
|
+
approachesCount: approaches.length,
|
|
2050
|
+
files: files.slice(0, 10),
|
|
2051
|
+
approaches: approaches.slice(0, 10),
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
catch (err) {
|
|
2055
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2056
|
+
return { error: `Session status failed: ${msg}` };
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
case 'cell_session_recent': {
|
|
2060
|
+
const { SessionMemory } = require('../core/session-memory');
|
|
2061
|
+
const memory = new SessionMemory();
|
|
2062
|
+
try {
|
|
2063
|
+
const project = args.project;
|
|
2064
|
+
const limit = args.limit || 10;
|
|
2065
|
+
let sessions = memory.getRecentSessions(limit);
|
|
2066
|
+
if (project)
|
|
2067
|
+
sessions = sessions.filter((s) => s.project === project);
|
|
2068
|
+
return {
|
|
2069
|
+
count: sessions.length,
|
|
2070
|
+
sessions: sessions.slice(0, limit).map((s) => ({
|
|
2071
|
+
sessionId: s.id,
|
|
2072
|
+
tool: s.toolName,
|
|
2073
|
+
project: s.project,
|
|
2074
|
+
startTime: s.startTime,
|
|
2075
|
+
endTime: s.endTime || null,
|
|
2076
|
+
filesCount: s.filesTouched.length,
|
|
2077
|
+
decisionsCount: s.keyDecisions.length,
|
|
2078
|
+
})),
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2081
|
+
catch (err) {
|
|
2082
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2083
|
+
return { error: `Session recent failed: ${msg}` };
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
case 'cell_session_recommendations': {
|
|
2087
|
+
const { SessionMemory } = require('../core/session-memory');
|
|
2088
|
+
const memory = new SessionMemory();
|
|
2089
|
+
try {
|
|
2090
|
+
const project = args.project || 'unknown';
|
|
2091
|
+
const triedApproaches = memory.getRecentApproaches(project, 10);
|
|
2092
|
+
const openQuestions = memory.getOpenQuestions(project);
|
|
2093
|
+
const hotFiles = memory.getMostTouchedFiles(project, 10);
|
|
2094
|
+
const lastSession = memory.getLastSessionForProject(project);
|
|
2095
|
+
const avoid = [];
|
|
2096
|
+
for (const a of triedApproaches) {
|
|
2097
|
+
if (a.status === 'failed') {
|
|
2098
|
+
avoid.push({ approach: a.approach, reason: `failed ${a.count}x` });
|
|
2099
|
+
}
|
|
2100
|
+
else if (a.status === 'partial') {
|
|
2101
|
+
avoid.push({ approach: a.approach, reason: `partial (${a.count}x)` });
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
return {
|
|
2105
|
+
avoid,
|
|
2106
|
+
openQuestions: openQuestions.map((q) => ({ id: q.id, question: q.question, priority: q.priority })),
|
|
2107
|
+
hotFiles: hotFiles.slice(0, 5).map((f) => ({ path: f.filePath, touches: f.touchCount })),
|
|
2108
|
+
lastSession: lastSession ? {
|
|
2109
|
+
tool: lastSession.toolName,
|
|
2110
|
+
when: lastSession.endTime,
|
|
2111
|
+
context: lastSession.contextSnapshot,
|
|
2112
|
+
} : null,
|
|
2113
|
+
message: avoid.length > 0
|
|
2114
|
+
? `Don't suggest: ${avoid.map(a => a.approach).slice(0, 3).join(', ')}`
|
|
2115
|
+
: 'No anti-repetition data yet',
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
catch (err) {
|
|
2119
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2120
|
+
return { error: `Session recommendations failed: ${msg}` };
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
case 'cell_list_projects': {
|
|
2124
|
+
const { ProjectRegistry } = require('../core/project-registry');
|
|
2125
|
+
const registry = new ProjectRegistry();
|
|
2126
|
+
try {
|
|
2127
|
+
const projects = registry.listProjects();
|
|
2128
|
+
return {
|
|
2129
|
+
count: projects.length,
|
|
2130
|
+
projects: projects.map((p) => ({
|
|
2131
|
+
name: p.name,
|
|
2132
|
+
path: p.path,
|
|
2133
|
+
stack: p.stack,
|
|
2134
|
+
fileCount: p.fileCount,
|
|
2135
|
+
lastScanAt: p.lastScanAt,
|
|
2136
|
+
})),
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
catch (err) {
|
|
2140
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2141
|
+
return { error: `List projects failed: ${msg}` };
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
case 'cell_get_project': {
|
|
2145
|
+
const { ProjectRegistry } = require('../core/project-registry');
|
|
2146
|
+
const registry = new ProjectRegistry();
|
|
2147
|
+
try {
|
|
2148
|
+
const name = args.name || '';
|
|
2149
|
+
const project = registry.getProject(name);
|
|
2150
|
+
if (!project)
|
|
2151
|
+
return { error: 'Project not found', name };
|
|
2152
|
+
return {
|
|
2153
|
+
name: project.name,
|
|
2154
|
+
path: project.path,
|
|
2155
|
+
stack: project.stack,
|
|
2156
|
+
fileCount: project.fileCount,
|
|
2157
|
+
lastScanAt: project.lastScanAt,
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
catch (err) {
|
|
2161
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2162
|
+
return { error: `Get project failed: ${msg}` };
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
case 'cell_detect_project': {
|
|
2166
|
+
const { ProjectRegistry } = require('../core/project-registry');
|
|
2167
|
+
const registry = new ProjectRegistry();
|
|
2168
|
+
try {
|
|
2169
|
+
const cwd = args.cwd || '';
|
|
2170
|
+
const detected = registry.detectProjectFromCwd(cwd);
|
|
2171
|
+
if (!detected) {
|
|
2172
|
+
return { detected: null, cwd, message: 'No matching project found for this path' };
|
|
2173
|
+
}
|
|
2174
|
+
const project = registry.getProject(detected);
|
|
2175
|
+
return {
|
|
2176
|
+
detected,
|
|
2177
|
+
cwd,
|
|
2178
|
+
project: project ? {
|
|
2179
|
+
name: project.name,
|
|
2180
|
+
stack: project.stack,
|
|
2181
|
+
fileCount: project.fileCount,
|
|
2182
|
+
lastScanAt: project.lastScanAt,
|
|
2183
|
+
} : null,
|
|
2184
|
+
message: `Detected project: ${detected}`,
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
catch (err) {
|
|
2188
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2189
|
+
return { error: `Detect project failed: ${msg}` };
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
case 'cell_register_project': {
|
|
2193
|
+
const { ProjectRegistry } = require('../core/project-registry');
|
|
2194
|
+
const registry = new ProjectRegistry();
|
|
2195
|
+
try {
|
|
2196
|
+
const id = registry.registerProject({
|
|
2197
|
+
name: args.name || '',
|
|
2198
|
+
path: args.path || '',
|
|
2199
|
+
stack: args.stack,
|
|
2200
|
+
fileCount: args.fileCount,
|
|
2201
|
+
});
|
|
2202
|
+
return { id, name: args.name, registered: true };
|
|
2203
|
+
}
|
|
2204
|
+
catch (err) {
|
|
2205
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2206
|
+
return { error: `Register project failed: ${msg}` };
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
case 'cell_graph_files': {
|
|
2210
|
+
try {
|
|
2211
|
+
const project = args.project || '';
|
|
2212
|
+
const limit = args.limit || 20;
|
|
2213
|
+
const { getFileKnowledgeGraph } = require('../core/knowledge-graph-store');
|
|
2214
|
+
const graph = getFileKnowledgeGraph(project);
|
|
2215
|
+
return {
|
|
2216
|
+
stats: graph.stats,
|
|
2217
|
+
topRiskFiles: graph.files.slice(0, limit),
|
|
2218
|
+
entryPoints: graph.files.filter((f) => f.isEntryPoint).slice(0, 10),
|
|
2219
|
+
sampleDeps: graph.deps.slice(0, limit),
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
catch (err) {
|
|
2223
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2224
|
+
return { error: `Graph files failed: ${msg}` };
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
case 'cell_graph_blast_radius': {
|
|
2228
|
+
try {
|
|
2229
|
+
const project = args.project || '';
|
|
2230
|
+
const file = args.file || '';
|
|
2231
|
+
const { computeBlastRadius } = require('../core/knowledge-graph-store');
|
|
2232
|
+
const radius = computeBlastRadius(project, file);
|
|
2233
|
+
return radius;
|
|
2234
|
+
}
|
|
2235
|
+
catch (err) {
|
|
2236
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2237
|
+
return { error: `Blast radius failed: ${msg}` };
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
case 'cell_graph_tech': {
|
|
2241
|
+
try {
|
|
2242
|
+
const project = args.project || '';
|
|
2243
|
+
const { getTechProficiency } = require('../core/knowledge-graph-store');
|
|
2244
|
+
const techs = getTechProficiency(project);
|
|
2245
|
+
const strong = techs.filter((t) => t.proficiencyScore >= 70);
|
|
2246
|
+
const learning = techs.filter((t) => t.proficiencyScore < 50);
|
|
2247
|
+
return {
|
|
2248
|
+
totalTechs: techs.length,
|
|
2249
|
+
strong,
|
|
2250
|
+
learning,
|
|
2251
|
+
all: techs,
|
|
2252
|
+
message: strong.length > 0
|
|
2253
|
+
? `Strong in: ${strong.map((t) => t.technology).slice(0, 3).join(', ')}`
|
|
2254
|
+
: 'No strong tech tracked yet',
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
catch (err) {
|
|
2258
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2259
|
+
return { error: `Graph tech failed: ${msg}` };
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
case 'cell_graph_stats': {
|
|
2263
|
+
try {
|
|
2264
|
+
const project = args.project || '';
|
|
2265
|
+
const { getPatternGraphStats, getFileKnowledgeGraph, getTechProficiency } = require('../core/knowledge-graph-store');
|
|
2266
|
+
const patternStats = getPatternGraphStats(project);
|
|
2267
|
+
const fileGraph = getFileKnowledgeGraph(project);
|
|
2268
|
+
const techs = getTechProficiency(project);
|
|
2269
|
+
return {
|
|
2270
|
+
patternGraph: patternStats,
|
|
2271
|
+
fileGraph: fileGraph.stats,
|
|
2272
|
+
techCount: techs.length,
|
|
2273
|
+
highRiskFiles: fileGraph.files.filter((f) => f.riskScore >= 50).length,
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
catch (err) {
|
|
2277
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2278
|
+
return { error: `Graph stats failed: ${msg}` };
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
case 'cell_team_silos': {
|
|
2282
|
+
try {
|
|
2283
|
+
const { detectKnowledgeSilos } = require('../team-collaboration');
|
|
2284
|
+
const silos = detectKnowledgeSilos();
|
|
2285
|
+
return {
|
|
2286
|
+
count: silos.length,
|
|
2287
|
+
high: silos.filter((s) => s.riskLevel === 'high').length,
|
|
2288
|
+
silos,
|
|
2289
|
+
message: silos.length > 0
|
|
2290
|
+
? `${silos.filter((s) => s.riskLevel === 'high').length} high-risk silos detected`
|
|
2291
|
+
: 'No knowledge silos detected',
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
catch (err) {
|
|
2295
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2296
|
+
return { error: `Team silos failed: ${msg}` };
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
case 'cell_team_bus_factor': {
|
|
2300
|
+
try {
|
|
2301
|
+
const { calculateBusFactor } = require('../team-collaboration');
|
|
2302
|
+
return calculateBusFactor();
|
|
2303
|
+
}
|
|
2304
|
+
catch (err) {
|
|
2305
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2306
|
+
return { error: `Bus factor failed: ${msg}` };
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
case 'cell_team_health': {
|
|
2310
|
+
try {
|
|
2311
|
+
const { getTeamHealth } = require('../team-collaboration');
|
|
2312
|
+
return getTeamHealth(args.project || undefined);
|
|
2313
|
+
}
|
|
2314
|
+
catch (err) {
|
|
2315
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2316
|
+
return { error: `Team health failed: ${msg}` };
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
case 'cell_team_onboard': {
|
|
2320
|
+
try {
|
|
2321
|
+
const { generateOnboardingDoc } = require('../team-collaboration');
|
|
2322
|
+
const teamName = args.teamName || 'Team';
|
|
2323
|
+
const project = args.project || undefined;
|
|
2324
|
+
return generateOnboardingDoc(teamName, project);
|
|
2325
|
+
}
|
|
2326
|
+
catch (err) {
|
|
2327
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2328
|
+
return { error: `Onboard failed: ${msg}` };
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
case 'cell_team_retro': {
|
|
2332
|
+
try {
|
|
2333
|
+
const { generateSprintRetro } = require('../team-collaboration');
|
|
2334
|
+
const days = args.days || 14;
|
|
2335
|
+
const project = args.project || undefined;
|
|
2336
|
+
return generateSprintRetro(days, project);
|
|
2337
|
+
}
|
|
2338
|
+
catch (err) {
|
|
2339
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2340
|
+
return { error: `Retro failed: ${msg}` };
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
case 'cell_team_handoff': {
|
|
2344
|
+
try {
|
|
2345
|
+
const { generateHandoff } = require('../team-collaboration');
|
|
2346
|
+
return generateHandoff(args.from || '', args.to || '', args.project || '');
|
|
2347
|
+
}
|
|
2348
|
+
catch (err) {
|
|
2349
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2350
|
+
return { error: `Handoff failed: ${msg}` };
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
case 'cell_team_knowledge_transfer': {
|
|
2354
|
+
try {
|
|
2355
|
+
const { suggestKnowledgeTransfer } = require('../team-collaboration');
|
|
2356
|
+
const suggestions = suggestKnowledgeTransfer();
|
|
2357
|
+
return {
|
|
2358
|
+
count: suggestions.length,
|
|
2359
|
+
suggestions,
|
|
2360
|
+
message: suggestions.length > 0
|
|
2361
|
+
? `${suggestions.length} transfer pairs suggested`
|
|
2362
|
+
: 'No urgent transfers needed',
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
catch (err) {
|
|
2366
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2367
|
+
return { error: `Knowledge transfer failed: ${msg}` };
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
case 'cell_team_style': {
|
|
2371
|
+
try {
|
|
2372
|
+
const { composeTeamStyle } = require('../team-collaboration');
|
|
2373
|
+
return composeTeamStyle(args.project || undefined);
|
|
2374
|
+
}
|
|
2375
|
+
catch (err) {
|
|
2376
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2377
|
+
return { error: `Team style failed: ${msg}` };
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
case 'cell_team_dashboard': {
|
|
2381
|
+
try {
|
|
2382
|
+
const { detectKnowledgeSilos, calculateBusFactor, getTeamHealth, composeTeamStyle } = require('../team-collaboration');
|
|
2383
|
+
const project = args.project || undefined;
|
|
2384
|
+
const silos = detectKnowledgeSilos();
|
|
2385
|
+
const busFactor = calculateBusFactor();
|
|
2386
|
+
const health = getTeamHealth(project);
|
|
2387
|
+
const style = composeTeamStyle(project);
|
|
2388
|
+
return {
|
|
2389
|
+
silos: { count: silos.length, high: silos.filter((s) => s.riskLevel === 'high').length, top: silos.slice(0, 5) },
|
|
2390
|
+
busFactor,
|
|
2391
|
+
health,
|
|
2392
|
+
style,
|
|
2393
|
+
generatedAt: new Date().toISOString(),
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
catch (err) {
|
|
2397
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2398
|
+
return { error: `Team dashboard failed: ${msg}` };
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
case 'cell_community_stats': {
|
|
2402
|
+
try {
|
|
2403
|
+
const { getCommunityStats } = require('../core/community-store');
|
|
2404
|
+
return getCommunityStats();
|
|
2405
|
+
}
|
|
2406
|
+
catch (err) {
|
|
2407
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2408
|
+
return { error: `Community stats failed: ${msg}` };
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
case 'cell_community_share': {
|
|
2412
|
+
try {
|
|
2413
|
+
const { shareCommunityPattern, validatePrivacy } = require('../core/community-store');
|
|
2414
|
+
const rule = args.rule || '';
|
|
2415
|
+
const validation = validatePrivacy(rule);
|
|
2416
|
+
if (!validation.safe) {
|
|
2417
|
+
return { shared: false, errors: validation.errors, warnings: validation.warnings };
|
|
2418
|
+
}
|
|
2419
|
+
const result = shareCommunityPattern({
|
|
2420
|
+
category: args.category || 'unknown',
|
|
2421
|
+
rule,
|
|
2422
|
+
language: args.language,
|
|
2423
|
+
framework: args.framework,
|
|
2424
|
+
successRate: args.successRate,
|
|
2425
|
+
});
|
|
2426
|
+
return { ...result, warnings: [...validation.warnings, ...result.warnings] };
|
|
2427
|
+
}
|
|
2428
|
+
catch (err) {
|
|
2429
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2430
|
+
return { error: `Community share failed: ${msg}` };
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
case 'cell_community_patterns': {
|
|
2434
|
+
try {
|
|
2435
|
+
const { getCommunityPatterns } = require('../core/community-store');
|
|
2436
|
+
const patterns = getCommunityPatterns({
|
|
2437
|
+
category: args.category,
|
|
2438
|
+
language: args.language,
|
|
2439
|
+
minVotes: args.minVotes,
|
|
2440
|
+
limit: args.limit || 20,
|
|
2441
|
+
});
|
|
2442
|
+
return { count: patterns.length, patterns };
|
|
2443
|
+
}
|
|
2444
|
+
catch (err) {
|
|
2445
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2446
|
+
return { error: `Community patterns failed: ${msg}` };
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
case 'cell_community_vote': {
|
|
2450
|
+
try {
|
|
2451
|
+
const { voteCommunityPattern } = require('../core/community-store');
|
|
2452
|
+
return voteCommunityPattern(args.patternId || '', args.upvote !== false);
|
|
2453
|
+
}
|
|
2454
|
+
catch (err) {
|
|
2455
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2456
|
+
return { error: `Community vote failed: ${msg}` };
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
case 'cell_community_insights': {
|
|
2460
|
+
try {
|
|
2461
|
+
const { getCommunityInsights } = require('../core/community-store');
|
|
2462
|
+
const insights = getCommunityInsights({
|
|
2463
|
+
severity: args.severity,
|
|
2464
|
+
category: args.category,
|
|
2465
|
+
limit: args.limit || 20,
|
|
2466
|
+
});
|
|
2467
|
+
return { count: insights.length, insights };
|
|
2468
|
+
}
|
|
2469
|
+
catch (err) {
|
|
2470
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2471
|
+
return { error: `Community insights failed: ${msg}` };
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
case 'cell_community_benchmarks': {
|
|
2475
|
+
try {
|
|
2476
|
+
const { getBenchmarks } = require('../core/community-store');
|
|
2477
|
+
const benchmarks = getBenchmarks({
|
|
2478
|
+
category: args.category,
|
|
2479
|
+
limit: args.limit || 50,
|
|
2480
|
+
});
|
|
2481
|
+
return { count: benchmarks.length, benchmarks };
|
|
2482
|
+
}
|
|
2483
|
+
catch (err) {
|
|
2484
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2485
|
+
return { error: `Community benchmarks failed: ${msg}` };
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
case 'cell_community_trends': {
|
|
2489
|
+
try {
|
|
2490
|
+
const { getTechEvolution } = require('../core/community-store');
|
|
2491
|
+
return getTechEvolution();
|
|
2492
|
+
}
|
|
2493
|
+
catch (err) {
|
|
2494
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2495
|
+
return { error: `Community trends failed: ${msg}` };
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
case 'cell_community_failures': {
|
|
2499
|
+
try {
|
|
2500
|
+
const { getFailurePatterns } = require('../core/community-store');
|
|
2501
|
+
const failures = getFailurePatterns({
|
|
2502
|
+
language: args.language,
|
|
2503
|
+
limit: args.limit || 10,
|
|
2504
|
+
});
|
|
2505
|
+
return { count: failures.length, failures };
|
|
2506
|
+
}
|
|
2507
|
+
catch (err) {
|
|
2508
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2509
|
+
return { error: `Community failures failed: ${msg}` };
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
case 'cell_community_validate': {
|
|
2513
|
+
try {
|
|
2514
|
+
const { validatePrivacy } = require('../core/community-store');
|
|
2515
|
+
return validatePrivacy(args.rule || '');
|
|
2516
|
+
}
|
|
2517
|
+
catch (err) {
|
|
2518
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2519
|
+
return { error: `Community validate failed: ${msg}` };
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
case 'cell_community_dashboard': {
|
|
2523
|
+
try {
|
|
2524
|
+
const { getCommunityStats, getTechEvolution, getCommunityInsights, getBenchmarks } = require('../core/community-store');
|
|
2525
|
+
const stats = getCommunityStats();
|
|
2526
|
+
const trends = getTechEvolution();
|
|
2527
|
+
const insights = getCommunityInsights({ severity: 'critical', limit: 5 });
|
|
2528
|
+
const benchmarks = getBenchmarks({ limit: 5 });
|
|
2529
|
+
return {
|
|
2530
|
+
stats,
|
|
2531
|
+
trends: trends.slice(0, 10),
|
|
2532
|
+
insights,
|
|
2533
|
+
benchmarks,
|
|
2534
|
+
generatedAt: new Date().toISOString(),
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
catch (err) {
|
|
2538
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2539
|
+
return { error: `Community dashboard failed: ${msg}` };
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
case 'cell_usage_decision_log': {
|
|
2543
|
+
try {
|
|
2544
|
+
const { logDecisionOutcome } = require('../core/usage-intelligence-store');
|
|
2545
|
+
return logDecisionOutcome({
|
|
2546
|
+
project: args.project || 'unknown',
|
|
2547
|
+
technology: args.technology || '',
|
|
2548
|
+
reason: args.reason || '',
|
|
2549
|
+
outcome: args.outcome || '',
|
|
2550
|
+
wasRight: args.wasRight === true,
|
|
2551
|
+
confidence: args.confidence,
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
catch (err) {
|
|
2555
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2556
|
+
return { error: `Decision log failed: ${msg}` };
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
case 'cell_usage_decision_patterns': {
|
|
2560
|
+
try {
|
|
2561
|
+
const { getDecisionPatterns } = require('../core/usage-intelligence-store');
|
|
2562
|
+
const patterns = getDecisionPatterns(args.project || 'unknown');
|
|
2563
|
+
return {
|
|
2564
|
+
count: patterns.length,
|
|
2565
|
+
patterns,
|
|
2566
|
+
message: patterns.length > 0
|
|
2567
|
+
? `Analyzed ${patterns.length} technology decision patterns`
|
|
2568
|
+
: 'Need more decisions (>= 2 per tech) to detect patterns',
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
catch (err) {
|
|
2572
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2573
|
+
return { error: `Decision patterns failed: ${msg}` };
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
case 'cell_usage_repeat_mistakes': {
|
|
2577
|
+
try {
|
|
2578
|
+
const { detectRepeatMistakes } = require('../core/usage-intelligence-store');
|
|
2579
|
+
const mistakes = detectRepeatMistakes(args.project || 'unknown');
|
|
2580
|
+
return {
|
|
2581
|
+
count: mistakes.length,
|
|
2582
|
+
critical: mistakes.filter((m) => m.severity === 'critical').length,
|
|
2583
|
+
mistakes: mistakes.slice(0, 20),
|
|
2584
|
+
message: mistakes.length > 0
|
|
2585
|
+
? `Found ${mistakes.length} repeated error patterns`
|
|
2586
|
+
: 'No repeated mistakes — clean track record',
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
catch (err) {
|
|
2590
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2591
|
+
return { error: `Repeat mistakes failed: ${msg}` };
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
case 'cell_usage_ai_stats': {
|
|
2595
|
+
try {
|
|
2596
|
+
const { getAIInteractionStats } = require('../core/usage-intelligence-store');
|
|
2597
|
+
return getAIInteractionStats(args.project || 'unknown');
|
|
2598
|
+
}
|
|
2599
|
+
catch (err) {
|
|
2600
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2601
|
+
return { error: `AI stats failed: ${msg}` };
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
case 'cell_usage_productivity': {
|
|
2605
|
+
try {
|
|
2606
|
+
const { logProductivitySignal, getProductivitySignals } = require('../core/usage-intelligence-store');
|
|
2607
|
+
const project = args.project || 'unknown';
|
|
2608
|
+
// If signalType + duration provided, log; otherwise just read
|
|
2609
|
+
if (args.signalType && args.durationMinutes !== undefined) {
|
|
2610
|
+
const result = logProductivitySignal({
|
|
2611
|
+
project,
|
|
2612
|
+
signalType: args.signalType,
|
|
2613
|
+
durationMinutes: args.durationMinutes,
|
|
2614
|
+
filesTouched: args.filesTouched || 0,
|
|
2615
|
+
contextSwitches: args.contextSwitches || 0,
|
|
2616
|
+
});
|
|
2617
|
+
return { logged: true, ...result };
|
|
2618
|
+
}
|
|
2619
|
+
return getProductivitySignals(project, 30);
|
|
2620
|
+
}
|
|
2621
|
+
catch (err) {
|
|
2622
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2623
|
+
return { error: `Productivity failed: ${msg}` };
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
case 'cell_usage_skills': {
|
|
2627
|
+
try {
|
|
2628
|
+
const { getSkillProgression } = require('../core/usage-intelligence-store');
|
|
2629
|
+
return getSkillProgression(args.project || 'unknown');
|
|
2630
|
+
}
|
|
2631
|
+
catch (err) {
|
|
2632
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2633
|
+
return { error: `Skill progression failed: ${msg}` };
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
case 'cell_usage_burnout': {
|
|
2637
|
+
try {
|
|
2638
|
+
const { getBurnoutSignals } = require('../core/usage-intelligence-store');
|
|
2639
|
+
return getBurnoutSignals(args.project || 'unknown');
|
|
2640
|
+
}
|
|
2641
|
+
catch (err) {
|
|
2642
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2643
|
+
return { error: `Burnout check failed: ${msg}` };
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
case 'cell_usage_dashboard': {
|
|
2647
|
+
try {
|
|
2648
|
+
const { getUsageDashboard } = require('../core/usage-intelligence-store');
|
|
2649
|
+
return getUsageDashboard(args.project || 'unknown');
|
|
2650
|
+
}
|
|
2651
|
+
catch (err) {
|
|
2652
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2653
|
+
return { error: `Usage dashboard failed: ${msg}` };
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
case 'cell_watch_start': {
|
|
2657
|
+
try {
|
|
2658
|
+
const { startWatcher } = require('../core/live-watcher');
|
|
2659
|
+
return startWatcher(args.project || 'unknown', args.dir || process.cwd());
|
|
2660
|
+
}
|
|
2661
|
+
catch (err) {
|
|
2662
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2663
|
+
return { error: `Watch start failed: ${msg}` };
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
case 'cell_watch_stop': {
|
|
2667
|
+
try {
|
|
2668
|
+
const { stopWatcher } = require('../core/live-watcher');
|
|
2669
|
+
return stopWatcher(args.project || 'unknown');
|
|
2670
|
+
}
|
|
2671
|
+
catch (err) {
|
|
2672
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2673
|
+
return { error: `Watch stop failed: ${msg}` };
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
case 'cell_watch_status': {
|
|
2677
|
+
try {
|
|
2678
|
+
const { getWatcherStatus, getActiveWatchers } = require('../core/live-watcher');
|
|
2679
|
+
if (args.project) {
|
|
2680
|
+
const status = getWatcherStatus(args.project);
|
|
2681
|
+
if (!status)
|
|
2682
|
+
return { project: args.project, active: false, message: 'No watcher found' };
|
|
2683
|
+
const isLive = status.active;
|
|
2684
|
+
return { ...status, message: isLive ? 'Live' : 'Stopped' };
|
|
2685
|
+
}
|
|
2686
|
+
return { active: getActiveWatchers() };
|
|
2687
|
+
}
|
|
2688
|
+
catch (err) {
|
|
2689
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2690
|
+
return { error: `Watch status failed: ${msg}` };
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
case 'cell_watch_events': {
|
|
2694
|
+
try {
|
|
2695
|
+
const { getLiveEvents } = require('../core/live-watcher');
|
|
2696
|
+
return { events: getLiveEvents(args.project || 'unknown', args.limit || 50, args.eventType) };
|
|
2697
|
+
}
|
|
2698
|
+
catch (err) {
|
|
2699
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2700
|
+
return { error: `Watch events failed: ${msg}` };
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
case 'cell_watch_refresh': {
|
|
2704
|
+
try {
|
|
2705
|
+
const { performAutoRefresh } = require('../core/live-watcher');
|
|
2706
|
+
return performAutoRefresh(args.project || 'unknown', args.force === true);
|
|
2707
|
+
}
|
|
2708
|
+
catch (err) {
|
|
2709
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2710
|
+
return { error: `Auto-refresh failed: ${msg}` };
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
case 'cell_watch_check': {
|
|
2714
|
+
try {
|
|
2715
|
+
const { checkIfNeedsRefresh } = require('../core/live-watcher');
|
|
2716
|
+
return checkIfNeedsRefresh(args.project || 'unknown');
|
|
2717
|
+
}
|
|
2718
|
+
catch (err) {
|
|
2719
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2720
|
+
return { error: `Refresh check failed: ${msg}` };
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
case 'cell_blindspots_scan': {
|
|
2724
|
+
try {
|
|
2725
|
+
const { scanDirectoryForBlindSpots } = require('../core/enhanced-blind-spots');
|
|
2726
|
+
const dir = args.dir || process.cwd();
|
|
2727
|
+
const max = args.maxFiles || 200;
|
|
2728
|
+
const spots = scanDirectoryForBlindSpots(dir, max);
|
|
2729
|
+
return { directory: dir, scanned: max, count: spots.length, blindSpots: spots };
|
|
2730
|
+
}
|
|
2731
|
+
catch (err) {
|
|
2732
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2733
|
+
return { error: `Blind spot scan failed: ${msg}` };
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
case 'cell_blindspots_report': {
|
|
2737
|
+
try {
|
|
2738
|
+
const { scanDirectoryForBlindSpots, formatBlindSpotReport, summarizeBlindSpots } = require('../core/enhanced-blind-spots');
|
|
2739
|
+
const dir = args.dir || process.cwd();
|
|
2740
|
+
const max = args.maxFiles || 200;
|
|
2741
|
+
const spots = scanDirectoryForBlindSpots(dir, max);
|
|
2742
|
+
return {
|
|
2743
|
+
directory: dir,
|
|
2744
|
+
summary: summarizeBlindSpots(spots),
|
|
2745
|
+
report: formatBlindSpotReport(spots),
|
|
2746
|
+
blindSpots: spots,
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
catch (err) {
|
|
2750
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2751
|
+
return { error: `Blind spot report failed: ${msg}` };
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
case 'cell_blindspots_summary': {
|
|
2755
|
+
try {
|
|
2756
|
+
const { getCategoryCounts } = require('../core/enhanced-blind-spots');
|
|
2757
|
+
const dir = args.dir || process.cwd();
|
|
2758
|
+
return { directory: dir, byCategory: getCategoryCounts(dir) };
|
|
2759
|
+
}
|
|
2760
|
+
catch (err) {
|
|
2761
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2762
|
+
return { error: `Blind spot summary failed: ${msg}` };
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
1865
2765
|
case 'cell_predict': {
|
|
1866
2766
|
const { generatePredictions } = require('../predictive-engine');
|
|
1867
2767
|
return generatePredictions(args.project);
|
|
@@ -1887,9 +2787,28 @@ async function handleMCPToolCall(name, args) {
|
|
|
1887
2787
|
}
|
|
1888
2788
|
}
|
|
1889
2789
|
app.post('/mcp', async (req, res) => {
|
|
2790
|
+
// ─── Security: rate limit + input validation ─────────────────────────
|
|
2791
|
+
const { checkRateLimit, getClientId, validateToolArgs, securityHeaders } = require('../core/security');
|
|
2792
|
+
for (const [k, v] of Object.entries(securityHeaders()))
|
|
2793
|
+
res.setHeader(k, v);
|
|
2794
|
+
const clientId = getClientId(req);
|
|
2795
|
+
const rl = checkRateLimit(clientId);
|
|
2796
|
+
res.setHeader('X-RateLimit-Remaining', String(rl.remaining));
|
|
2797
|
+
if (!rl.allowed) {
|
|
2798
|
+
res.status(429).json({ jsonrpc: '2.0', id: null, error: { code: -32000, message: `Rate limit exceeded. Retry in ${Math.ceil(rl.resetIn / 1000)}s` } });
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
1890
2801
|
const rpc = req.body;
|
|
1891
2802
|
const { method, params, id } = rpc;
|
|
1892
2803
|
try {
|
|
2804
|
+
// Validate tool args before execution
|
|
2805
|
+
if (method === 'tools/call') {
|
|
2806
|
+
const validation = validateToolArgs(params?.name || '', params?.arguments);
|
|
2807
|
+
if (!validation.valid) {
|
|
2808
|
+
res.status(400).json({ jsonrpc: '2.0', id, error: { code: -32602, message: `Invalid arguments: ${validation.reason}` } });
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
1893
2812
|
let result;
|
|
1894
2813
|
switch (method) {
|
|
1895
2814
|
case 'initialize':
|