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
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_EXPIRED_DAYS = exports.DEFAULT_STALE_DAYS = void 0;
|
|
3
4
|
exports.buildContext = buildContext;
|
|
4
5
|
exports.formatContextForInjection = formatContextForInjection;
|
|
6
|
+
exports.formatContextForTool = formatContextForTool;
|
|
5
7
|
exports.estimateTokens = estimateTokens;
|
|
8
|
+
exports.getStalenessStatus = getStalenessStatus;
|
|
6
9
|
const database_1 = require("./database");
|
|
7
10
|
const logger_1 = require("./logger");
|
|
8
11
|
function buildContext(projectPath, toolName) {
|
|
9
12
|
const db = (0, database_1.getDb)();
|
|
10
|
-
const profile = readProfile(db);
|
|
11
|
-
const blindSpots = readBlindSpots(db);
|
|
12
|
-
const topPatterns = readTopPatterns(db);
|
|
13
|
+
const profile = readProfile(db, projectPath);
|
|
14
|
+
const blindSpots = readBlindSpots(db, projectPath);
|
|
15
|
+
const topPatterns = readTopPatterns(db, projectPath);
|
|
13
16
|
const recentActivity = readRecentActivity(db, projectPath);
|
|
14
17
|
const skills = readSkills(db);
|
|
15
18
|
const collaborators = readCollaborators(db);
|
|
16
19
|
const toolStats = toolName ? readToolStats(db, toolName) : null;
|
|
17
20
|
const stackDNA = readStackDNA(db, projectPath);
|
|
18
21
|
const dbPredictions = readPredictions(db, projectPath);
|
|
22
|
+
const lastSession = readLastSession(db, projectPath);
|
|
23
|
+
const triedApproaches = readTriedApproaches(db, projectPath);
|
|
24
|
+
const mostTouchedFiles = readMostTouchedFiles(db, projectPath);
|
|
25
|
+
const openQuestions = readOpenQuestions(db, projectPath);
|
|
19
26
|
const whoYouAre = buildWhoYouAre(profile, skills, collaborators, stackDNA, blindSpots);
|
|
20
|
-
const rightNow = buildRightNow(recentActivity, profile);
|
|
27
|
+
const rightNow = buildRightNow(recentActivity, profile, lastSession);
|
|
21
28
|
const yourStyle = buildYourStyle(topPatterns, profile, skills);
|
|
22
29
|
const watchOut = buildWatchOut(blindSpots, profile);
|
|
23
30
|
const predictions = buildPredictions(blindSpots, recentActivity, toolStats);
|
|
24
31
|
const toolHint = toolName ? buildToolHint(toolName) : undefined;
|
|
32
|
+
const staleness = buildStalenessInfo(profile, recentActivity);
|
|
25
33
|
return {
|
|
26
34
|
whoYouAre,
|
|
27
35
|
rightNow,
|
|
@@ -31,6 +39,11 @@ function buildContext(projectPath, toolName) {
|
|
|
31
39
|
dbPredictions,
|
|
32
40
|
injectedAt: new Date().toISOString(),
|
|
33
41
|
toolHint,
|
|
42
|
+
lastSession,
|
|
43
|
+
triedApproaches,
|
|
44
|
+
mostTouchedFiles,
|
|
45
|
+
openQuestions,
|
|
46
|
+
staleness,
|
|
34
47
|
};
|
|
35
48
|
}
|
|
36
49
|
function formatContextForInjection(ctx, compact = false) {
|
|
@@ -39,20 +52,135 @@ function formatContextForInjection(ctx, compact = false) {
|
|
|
39
52
|
}
|
|
40
53
|
return fullContext(ctx);
|
|
41
54
|
}
|
|
55
|
+
function formatContextForTool(ctx, toolName) {
|
|
56
|
+
if (!toolName)
|
|
57
|
+
return atCellBlock(ctx);
|
|
58
|
+
const t = toolName.toLowerCase().trim();
|
|
59
|
+
if (t === 'cell' || t === '@cell' || t === 'cell-block') {
|
|
60
|
+
return atCellBlock(ctx);
|
|
61
|
+
}
|
|
62
|
+
if (t === 'claude-code' || t === 'claude' || t === 'claude-code-cli') {
|
|
63
|
+
return atCellBlock(ctx);
|
|
64
|
+
}
|
|
65
|
+
if (t === 'cursor' || t === 'cursor-ide' || t === 'cursor-ai') {
|
|
66
|
+
return formatForCursor(ctx);
|
|
67
|
+
}
|
|
68
|
+
if (t === 'windsurf' || t === 'codeium' || t === 'windsurf-ide') {
|
|
69
|
+
return formatForWindsurf(ctx);
|
|
70
|
+
}
|
|
71
|
+
if (t === 'copilot' || t === 'github-copilot') {
|
|
72
|
+
return formatForCopilot(ctx);
|
|
73
|
+
}
|
|
74
|
+
if (t === 'antigravity' || t === 'aider' || t === 'continue-dev') {
|
|
75
|
+
return formatForAntigravity(ctx);
|
|
76
|
+
}
|
|
77
|
+
return atCellBlock(ctx);
|
|
78
|
+
}
|
|
79
|
+
// Compact `@cell` block format: matches the @cell Context convention from CLAUDE.md
|
|
80
|
+
function atCellBlock(ctx) {
|
|
81
|
+
const lines = [];
|
|
82
|
+
lines.push('# ─── @cell Context — Auto-generated by FIVO Cell ───');
|
|
83
|
+
// Developer
|
|
84
|
+
const developer = [];
|
|
85
|
+
if (ctx.whoYouAre)
|
|
86
|
+
developer.push(ctx.whoYouAre.split('\n')[0] || '');
|
|
87
|
+
lines.push('## Developer Context');
|
|
88
|
+
lines.push(developer.filter(Boolean).join(' | '));
|
|
89
|
+
// Current
|
|
90
|
+
lines.push('');
|
|
91
|
+
lines.push('### Current');
|
|
92
|
+
lines.push(ctx.rightNow ? ctx.rightNow.split('\n').filter(Boolean).slice(0, 3).join(' | ') : 'No recent activity detected');
|
|
93
|
+
// Style
|
|
94
|
+
lines.push('');
|
|
95
|
+
lines.push('### Style');
|
|
96
|
+
const styleLine = ctx.yourStyle ? ctx.yourStyle.split('\n').filter(Boolean).slice(0, 4).join(' | ') : '';
|
|
97
|
+
lines.push(styleLine || 'unknown');
|
|
98
|
+
// Blind spots
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push('### Blind Spots');
|
|
101
|
+
lines.push(ctx.watchOut ? ctx.watchOut.split('\n').filter(Boolean).slice(0, 4).join(' | ') : 'none detected');
|
|
102
|
+
// Predictions
|
|
103
|
+
lines.push('');
|
|
104
|
+
lines.push('### Predictions');
|
|
105
|
+
lines.push(ctx.predictions ? ctx.predictions.split('\n').filter(Boolean).slice(0, 2).join(' | ') : 'none');
|
|
106
|
+
// Last session
|
|
107
|
+
if (ctx.lastSession) {
|
|
108
|
+
lines.push('');
|
|
109
|
+
lines.push('### Last Session');
|
|
110
|
+
const ago = ctx.lastSession.endTime ? formatRelativeTime(ctx.lastSession.endTime) : 'active';
|
|
111
|
+
const tool = ctx.lastSession.toolName || 'unknown';
|
|
112
|
+
const task = ctx.lastSession.contextSnapshot ? ctx.lastSession.contextSnapshot.split('\n')[0] : '';
|
|
113
|
+
lines.push(`${ago} · ${tool}${task ? ' · ' + task : ''}`);
|
|
114
|
+
}
|
|
115
|
+
// Tried approaches
|
|
116
|
+
if (ctx.triedApproaches.length > 0) {
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push('### Tried (skip these)');
|
|
119
|
+
lines.push(ctx.triedApproaches.slice(0, 3).map(a => `${a.approach}(${a.status})`).join(' | '));
|
|
120
|
+
}
|
|
121
|
+
// Tool hint
|
|
122
|
+
if (ctx.toolHint) {
|
|
123
|
+
lines.push('');
|
|
124
|
+
lines.push('### Tool Note');
|
|
125
|
+
lines.push(ctx.toolHint.split('\n').filter(Boolean).slice(0, 2).join(' | '));
|
|
126
|
+
}
|
|
127
|
+
// Staleness warning
|
|
128
|
+
if (ctx.staleness?.warning) {
|
|
129
|
+
lines.unshift('> ' + ctx.staleness.warning);
|
|
130
|
+
}
|
|
131
|
+
lines.push('# ─── End @cell Context ───');
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
}
|
|
42
134
|
function estimateTokens(text) {
|
|
43
135
|
return Math.ceil(text.length / 4);
|
|
44
136
|
}
|
|
45
137
|
function fullContext(ctx) {
|
|
46
138
|
let out = '';
|
|
47
|
-
out += '
|
|
139
|
+
out += '━━━ CELL CONTEXT ━━━\n\n';
|
|
140
|
+
if (ctx.staleness?.warning) {
|
|
141
|
+
out += '⚠ ' + ctx.staleness.warning + '\n\n';
|
|
142
|
+
}
|
|
48
143
|
out += '▸ WHO YOU ARE\n';
|
|
49
144
|
out += ctx.whoYouAre + '\n\n';
|
|
50
145
|
out += '▸ RIGHT NOW\n';
|
|
51
146
|
out += ctx.rightNow + '\n\n';
|
|
147
|
+
if (ctx.lastSession) {
|
|
148
|
+
const ago = ctx.lastSession.endTime ? formatRelativeTime(ctx.lastSession.endTime) : 'active';
|
|
149
|
+
out += '▸ LAST SESSION (' + ago + ')\n';
|
|
150
|
+
out += `Tool: ${ctx.lastSession.toolName}\n`;
|
|
151
|
+
if (ctx.lastSession.contextSnapshot) {
|
|
152
|
+
out += `Working on: ${ctx.lastSession.contextSnapshot}\n`;
|
|
153
|
+
}
|
|
154
|
+
if (ctx.lastSession.keyDecisions.length > 0) {
|
|
155
|
+
out += 'Key decisions: ' + ctx.lastSession.keyDecisions.slice(0, 3).join(' · ') + '\n';
|
|
156
|
+
}
|
|
157
|
+
out += '\n';
|
|
158
|
+
}
|
|
159
|
+
if (ctx.triedApproaches.length > 0) {
|
|
160
|
+
out += '▸ TRIED ALREADY (don\'t suggest these)\n';
|
|
161
|
+
for (const a of ctx.triedApproaches.slice(0, 5)) {
|
|
162
|
+
out += `→ ${a.approach} — ${a.status}${a.count > 1 ? ` (${a.count}x)` : ''}\n`;
|
|
163
|
+
}
|
|
164
|
+
out += '\n';
|
|
165
|
+
}
|
|
166
|
+
if (ctx.openQuestions.length > 0) {
|
|
167
|
+
out += '▸ OPEN QUESTIONS\n';
|
|
168
|
+
for (const q of ctx.openQuestions.slice(0, 5)) {
|
|
169
|
+
out += `→ [${q.priority}] ${q.question}\n`;
|
|
170
|
+
}
|
|
171
|
+
out += '\n';
|
|
172
|
+
}
|
|
52
173
|
out += '▸ YOUR STYLE\n';
|
|
53
174
|
out += ctx.yourStyle + '\n\n';
|
|
54
175
|
out += '▸ WATCH OUT\n';
|
|
55
176
|
out += ctx.watchOut + '\n\n';
|
|
177
|
+
if (ctx.mostTouchedFiles.length > 0) {
|
|
178
|
+
out += '▸ HOT FILES (recently active)\n';
|
|
179
|
+
for (const f of ctx.mostTouchedFiles.slice(0, 5)) {
|
|
180
|
+
out += `→ ${f.filePath} (${f.touchCount} touches, ${f.sessionCount} sessions)\n`;
|
|
181
|
+
}
|
|
182
|
+
out += '\n';
|
|
183
|
+
}
|
|
56
184
|
out += '▸ PREDICTIONS\n';
|
|
57
185
|
out += ctx.predictions;
|
|
58
186
|
if (ctx.toolHint) {
|
|
@@ -62,10 +190,21 @@ function fullContext(ctx) {
|
|
|
62
190
|
}
|
|
63
191
|
function compactContext(ctx) {
|
|
64
192
|
let out = '';
|
|
193
|
+
if (ctx.staleness?.warning) {
|
|
194
|
+
out += '⚠ ' + ctx.staleness.warning + '\n\n';
|
|
195
|
+
}
|
|
65
196
|
out += '## Developer Context\n';
|
|
66
197
|
out += ctx.whoYouAre.replace(/\n/g, ' | ') + '\n\n';
|
|
67
198
|
out += '### Current\n';
|
|
68
199
|
out += ctx.rightNow.replace(/\n/g, ' | ') + '\n\n';
|
|
200
|
+
if (ctx.triedApproaches.length > 0) {
|
|
201
|
+
out += '### Tried (skip these)\n';
|
|
202
|
+
out += ctx.triedApproaches.slice(0, 3).map(a => `${a.approach}(${a.status})`).join(' | ') + '\n\n';
|
|
203
|
+
}
|
|
204
|
+
if (ctx.openQuestions.length > 0) {
|
|
205
|
+
out += '### Open Questions\n';
|
|
206
|
+
out += ctx.openQuestions.slice(0, 3).map(q => `[${q.priority}] ${q.question}`).join(' | ') + '\n\n';
|
|
207
|
+
}
|
|
69
208
|
out += '### Style\n';
|
|
70
209
|
out += ctx.yourStyle.replace(/\n/g, ' | ') + '\n\n';
|
|
71
210
|
out += '### Blind Spots\n';
|
|
@@ -77,16 +216,142 @@ function compactContext(ctx) {
|
|
|
77
216
|
}
|
|
78
217
|
return out;
|
|
79
218
|
}
|
|
80
|
-
function
|
|
219
|
+
function formatForCursor(ctx) {
|
|
220
|
+
const lines = [];
|
|
221
|
+
lines.push('@context fivo-cell');
|
|
222
|
+
if (ctx.staleness?.warning)
|
|
223
|
+
lines.push('! ' + ctx.staleness.warning);
|
|
224
|
+
if (ctx.lastSession) {
|
|
225
|
+
lines.push('last: ' + ctx.lastSession.toolName + ' (' + (ctx.lastSession.endTime ? formatRelativeTime(ctx.lastSession.endTime) : 'active') + ')');
|
|
226
|
+
if (ctx.lastSession.contextSnapshot)
|
|
227
|
+
lines.push('working: ' + ctx.lastSession.contextSnapshot);
|
|
228
|
+
}
|
|
229
|
+
lines.push('');
|
|
230
|
+
lines.push('style: ' + (ctx.yourStyle.split('\n')[0] || ''));
|
|
231
|
+
if (ctx.triedApproaches.length > 0) {
|
|
232
|
+
lines.push('avoid: ' + ctx.triedApproaches.slice(0, 3).map(a => `${a.approach}[${a.status}]`).join(', '));
|
|
233
|
+
}
|
|
234
|
+
if (ctx.openQuestions.length > 0) {
|
|
235
|
+
lines.push('q: ' + ctx.openQuestions.slice(0, 2).map(q => `${q.question.slice(0, 50)}…`).join(' | '));
|
|
236
|
+
}
|
|
237
|
+
if (ctx.mostTouchedFiles.length > 0) {
|
|
238
|
+
lines.push('hot: ' + ctx.mostTouchedFiles.slice(0, 3).map(f => f.filePath).join(', '));
|
|
239
|
+
}
|
|
240
|
+
if (ctx.watchOut) {
|
|
241
|
+
const topBlindSpot = ctx.watchOut.split('\n').find(l => l.trim().startsWith('-'));
|
|
242
|
+
if (topBlindSpot)
|
|
243
|
+
lines.push('watch: ' + topBlindSpot.replace(/^-\s*/, '').slice(0, 80));
|
|
244
|
+
}
|
|
245
|
+
return lines.join('\n');
|
|
246
|
+
}
|
|
247
|
+
function formatForWindsurf(ctx) {
|
|
248
|
+
const lines = [];
|
|
249
|
+
lines.push('# Cell Context');
|
|
250
|
+
if (ctx.staleness?.warning)
|
|
251
|
+
lines.push('> ⚠ ' + ctx.staleness.warning);
|
|
252
|
+
if (ctx.lastSession) {
|
|
253
|
+
lines.push('## Last Session');
|
|
254
|
+
lines.push(`- **Tool**: ${ctx.lastSession.toolName}`);
|
|
255
|
+
if (ctx.lastSession.contextSnapshot)
|
|
256
|
+
lines.push(`- **Working on**: ${ctx.lastSession.contextSnapshot}`);
|
|
257
|
+
if (ctx.lastSession.keyDecisions.length > 0)
|
|
258
|
+
lines.push(`- **Decisions**: ${ctx.lastSession.keyDecisions.slice(0, 2).join(', ')}`);
|
|
259
|
+
}
|
|
260
|
+
if (ctx.openQuestions.length > 0) {
|
|
261
|
+
lines.push('## Open Questions');
|
|
262
|
+
for (const q of ctx.openQuestions.slice(0, 3)) {
|
|
263
|
+
lines.push(`- [${q.priority}] ${q.question}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
lines.push('## Style');
|
|
267
|
+
lines.push(ctx.yourStyle.split('\n')[0] || '');
|
|
268
|
+
lines.push('## Suggestions');
|
|
269
|
+
if (ctx.watchOut) {
|
|
270
|
+
const spots = ctx.watchOut.split('\n').filter(l => l.trim().startsWith('-')).slice(0, 3);
|
|
271
|
+
spots.forEach(s => lines.push('- ' + s.replace(/^-\s*/, '')));
|
|
272
|
+
}
|
|
273
|
+
if (ctx.mostTouchedFiles.length > 0) {
|
|
274
|
+
lines.push('## Active Files');
|
|
275
|
+
ctx.mostTouchedFiles.slice(0, 3).forEach(f => lines.push(`- ${f.filePath} (${f.touchCount} touches)`));
|
|
276
|
+
}
|
|
277
|
+
return lines.join('\n');
|
|
278
|
+
}
|
|
279
|
+
function formatForCopilot(ctx) {
|
|
280
|
+
const parts = [];
|
|
281
|
+
if (ctx.staleness?.warning)
|
|
282
|
+
parts.push('⚠ ' + ctx.staleness.warning.replace(/`/g, ''));
|
|
283
|
+
parts.push('Project: ' + (ctx.lastSession?.project || 'unknown'));
|
|
284
|
+
if (ctx.lastSession?.contextSnapshot)
|
|
285
|
+
parts.push('Working: ' + ctx.lastSession.contextSnapshot.slice(0, 60));
|
|
286
|
+
if (ctx.triedApproaches.length > 0) {
|
|
287
|
+
const a = ctx.triedApproaches[0];
|
|
288
|
+
parts.push(`Avoid: ${a.approach} (${a.status})`);
|
|
289
|
+
}
|
|
290
|
+
const styleLine = ctx.yourStyle.split('\n')[0] || '';
|
|
291
|
+
if (styleLine)
|
|
292
|
+
parts.push('Style: ' + styleLine.slice(0, 80));
|
|
293
|
+
return parts.join(' · ');
|
|
294
|
+
}
|
|
295
|
+
function formatForAntigravity(ctx) {
|
|
296
|
+
const lines = [];
|
|
297
|
+
lines.push('## Cell Context — Structured');
|
|
298
|
+
if (ctx.staleness?.warning)
|
|
299
|
+
lines.push('> ⚠ ' + ctx.staleness.warning);
|
|
300
|
+
lines.push('');
|
|
301
|
+
lines.push('```yaml');
|
|
302
|
+
lines.push(`project: ${ctx.lastSession?.project || 'unknown'}`);
|
|
303
|
+
lines.push(`tool_hint: ${ctx.toolHint || 'general'}`);
|
|
304
|
+
if (ctx.lastSession) {
|
|
305
|
+
lines.push(`last_session:`);
|
|
306
|
+
lines.push(` tool: ${ctx.lastSession.toolName}`);
|
|
307
|
+
lines.push(` when: ${ctx.lastSession.endTime || 'active'}`);
|
|
308
|
+
if (ctx.lastSession.contextSnapshot)
|
|
309
|
+
lines.push(` context: "${ctx.lastSession.contextSnapshot}"`);
|
|
310
|
+
}
|
|
311
|
+
if (ctx.triedApproaches.length > 0) {
|
|
312
|
+
lines.push(`tried_approaches:`);
|
|
313
|
+
ctx.triedApproaches.slice(0, 3).forEach(a => {
|
|
314
|
+
lines.push(` - approach: "${a.approach}"`);
|
|
315
|
+
lines.push(` status: ${a.status}`);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (ctx.openQuestions.length > 0) {
|
|
319
|
+
lines.push(`open_questions:`);
|
|
320
|
+
ctx.openQuestions.slice(0, 3).forEach(q => {
|
|
321
|
+
lines.push(` - priority: ${q.priority}`);
|
|
322
|
+
lines.push(` question: "${q.question}"`);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
lines.push('```');
|
|
326
|
+
return lines.join('\n');
|
|
327
|
+
}
|
|
328
|
+
function readProfile(db, projectPath) {
|
|
81
329
|
try {
|
|
82
|
-
const row =
|
|
330
|
+
const row = projectPath
|
|
331
|
+
? db.prepare('SELECT * FROM developer_profiles WHERE project = ? ORDER BY scanned_at DESC LIMIT 1').get(projectPath)
|
|
332
|
+
: db.prepare('SELECT * FROM developer_profiles ORDER BY scanned_at DESC LIMIT 1').get();
|
|
83
333
|
if (row) {
|
|
84
334
|
return {
|
|
85
|
-
name: row.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
335
|
+
name: row.project || '',
|
|
336
|
+
project: row.project || '',
|
|
337
|
+
naming_style: row.naming_style || '',
|
|
338
|
+
quote_style: row.quote_style || '',
|
|
339
|
+
semicolon_style: row.semicolon_style || '',
|
|
340
|
+
indent_style: row.indent_style || '',
|
|
341
|
+
async_style: row.async_style || '',
|
|
342
|
+
error_handling: row.error_handling || '',
|
|
343
|
+
function_style: row.function_style || '',
|
|
344
|
+
import_style: row.import_style || '',
|
|
345
|
+
export_style: row.export_style || '',
|
|
346
|
+
comment_style: row.comment_style || '',
|
|
347
|
+
architecture_style: row.architecture_style || '',
|
|
348
|
+
test_pattern: row.test_pattern || '',
|
|
349
|
+
top_patterns: safeJson(row.top_patterns),
|
|
350
|
+
strengths: safeJson(row.strengths),
|
|
351
|
+
improvements: safeJson(row.improvements),
|
|
352
|
+
files_scanned: row.files_scanned || 0,
|
|
353
|
+
total_lines: row.total_lines || 0,
|
|
354
|
+
scanned_at: row.scanned_at || '',
|
|
90
355
|
};
|
|
91
356
|
}
|
|
92
357
|
}
|
|
@@ -95,17 +360,23 @@ function readProfile(db) {
|
|
|
95
360
|
}
|
|
96
361
|
return {};
|
|
97
362
|
}
|
|
98
|
-
function readBlindSpots(db) {
|
|
363
|
+
function readBlindSpots(db, projectPath) {
|
|
99
364
|
try {
|
|
100
|
-
|
|
365
|
+
if (projectPath) {
|
|
366
|
+
return db.prepare("SELECT pattern_type, pattern_value, count, trust_score FROM code_patterns WHERE category = 'blind_spots' AND project = ? ORDER BY trust_score DESC, count DESC LIMIT 10").all(projectPath);
|
|
367
|
+
}
|
|
368
|
+
return db.prepare("SELECT pattern_type, pattern_value, count, trust_score FROM code_patterns WHERE category = 'blind_spots' ORDER BY trust_score DESC, count DESC LIMIT 10").all();
|
|
101
369
|
}
|
|
102
370
|
catch {
|
|
103
371
|
return [];
|
|
104
372
|
}
|
|
105
373
|
}
|
|
106
|
-
function readTopPatterns(db) {
|
|
374
|
+
function readTopPatterns(db, projectPath) {
|
|
107
375
|
try {
|
|
108
|
-
|
|
376
|
+
if (projectPath) {
|
|
377
|
+
return db.prepare("SELECT pattern_type, pattern_value, category, count, trust_score FROM code_patterns WHERE project = ? AND category != 'blind_spots' ORDER BY trust_score DESC, count DESC LIMIT 15").all(projectPath);
|
|
378
|
+
}
|
|
379
|
+
return db.prepare("SELECT pattern_type, pattern_value, category, count, trust_score FROM code_patterns WHERE category != 'blind_spots' ORDER BY trust_score DESC, count DESC LIMIT 15").all();
|
|
109
380
|
}
|
|
110
381
|
catch {
|
|
111
382
|
return [];
|
|
@@ -114,9 +385,9 @@ function readTopPatterns(db) {
|
|
|
114
385
|
function readRecentActivity(db, projectPath) {
|
|
115
386
|
try {
|
|
116
387
|
if (projectPath) {
|
|
117
|
-
return db.prepare('SELECT * FROM
|
|
388
|
+
return db.prepare('SELECT * FROM scans WHERE project = ? ORDER BY completed_at DESC LIMIT 5').all(projectPath);
|
|
118
389
|
}
|
|
119
|
-
return db.prepare('SELECT * FROM
|
|
390
|
+
return db.prepare('SELECT * FROM scans ORDER BY completed_at DESC LIMIT 5').all();
|
|
120
391
|
}
|
|
121
392
|
catch {
|
|
122
393
|
return [];
|
|
@@ -170,9 +441,118 @@ function readPredictions(db, projectPath) {
|
|
|
170
441
|
return [];
|
|
171
442
|
}
|
|
172
443
|
}
|
|
444
|
+
function readLastSession(db, projectPath) {
|
|
445
|
+
try {
|
|
446
|
+
const query = projectPath
|
|
447
|
+
? 'SELECT * FROM sessions WHERE project = ? AND end_time IS NOT NULL ORDER BY end_time DESC LIMIT 1'
|
|
448
|
+
: 'SELECT * FROM sessions WHERE end_time IS NOT NULL ORDER BY end_time DESC LIMIT 1';
|
|
449
|
+
const params = projectPath ? [projectPath] : [];
|
|
450
|
+
const row = db.prepare(query).get(...(params));
|
|
451
|
+
if (!row)
|
|
452
|
+
return undefined;
|
|
453
|
+
return {
|
|
454
|
+
id: Number(row.id),
|
|
455
|
+
toolName: String(row.tool_name || ''),
|
|
456
|
+
project: String(row.project || ''),
|
|
457
|
+
startTime: String(row.start_time || ''),
|
|
458
|
+
endTime: row.end_time ? String(row.end_time) : undefined,
|
|
459
|
+
filesTouched: safeJson(String(row.files_touched)) || [],
|
|
460
|
+
keyDecisions: safeJson(String(row.key_decisions)) || [],
|
|
461
|
+
contextSnapshot: String(row.context_snapshot || ''),
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
return undefined;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function readTriedApproaches(db, projectPath) {
|
|
469
|
+
try {
|
|
470
|
+
const query = projectPath
|
|
471
|
+
? `SELECT approach, status, COUNT(*) as count, MAX(reason) as last_reason, MAX(created_at) as last_seen
|
|
472
|
+
FROM session_approaches
|
|
473
|
+
WHERE project = ?
|
|
474
|
+
GROUP BY LOWER(approach)
|
|
475
|
+
ORDER BY count DESC, last_seen DESC
|
|
476
|
+
LIMIT 10`
|
|
477
|
+
: `SELECT approach, status, COUNT(*) as count, MAX(reason) as last_reason, MAX(created_at) as last_seen
|
|
478
|
+
FROM session_approaches
|
|
479
|
+
GROUP BY LOWER(approach)
|
|
480
|
+
ORDER BY count DESC, last_seen DESC
|
|
481
|
+
LIMIT 10`;
|
|
482
|
+
const params = projectPath ? [projectPath] : [];
|
|
483
|
+
const rows = db.prepare(query).all(...(params));
|
|
484
|
+
return rows.map(r => ({
|
|
485
|
+
approach: r.approach,
|
|
486
|
+
status: r.status,
|
|
487
|
+
count: r.count,
|
|
488
|
+
lastReason: r.last_reason,
|
|
489
|
+
lastSeen: r.last_seen,
|
|
490
|
+
}));
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function readMostTouchedFiles(db, projectPath) {
|
|
497
|
+
try {
|
|
498
|
+
const query = projectPath
|
|
499
|
+
? `SELECT file_path, SUM(touch_count) as total_touches, COUNT(DISTINCT session_id) as session_count, MAX(last_touched) as last_touched
|
|
500
|
+
FROM session_files
|
|
501
|
+
WHERE project = ?
|
|
502
|
+
GROUP BY file_path
|
|
503
|
+
ORDER BY total_touches DESC, last_touched DESC
|
|
504
|
+
LIMIT 10`
|
|
505
|
+
: `SELECT file_path, SUM(touch_count) as total_touches, COUNT(DISTINCT session_id) as session_count, MAX(last_touched) as last_touched
|
|
506
|
+
FROM session_files
|
|
507
|
+
GROUP BY file_path
|
|
508
|
+
ORDER BY total_touches DESC, last_touched DESC
|
|
509
|
+
LIMIT 10`;
|
|
510
|
+
const params = projectPath ? [projectPath] : [];
|
|
511
|
+
const rows = db.prepare(query).all(...(params));
|
|
512
|
+
return rows.map(r => ({
|
|
513
|
+
filePath: r.file_path,
|
|
514
|
+
touchCount: r.total_touches,
|
|
515
|
+
sessionCount: r.session_count,
|
|
516
|
+
lastTouched: r.last_touched,
|
|
517
|
+
}));
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
return [];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function readOpenQuestions(db, projectPath) {
|
|
524
|
+
try {
|
|
525
|
+
const query = projectPath
|
|
526
|
+
? `SELECT * FROM session_questions
|
|
527
|
+
WHERE project = ? AND resolved = 0
|
|
528
|
+
ORDER BY CASE priority WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END, created_at DESC
|
|
529
|
+
LIMIT 10`
|
|
530
|
+
: `SELECT * FROM session_questions
|
|
531
|
+
WHERE resolved = 0
|
|
532
|
+
ORDER BY CASE priority WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END, created_at DESC
|
|
533
|
+
LIMIT 10`;
|
|
534
|
+
const params = projectPath ? [projectPath] : [];
|
|
535
|
+
const rows = db.prepare(query).all(...(params));
|
|
536
|
+
return rows.map(r => ({
|
|
537
|
+
id: Number(r.id),
|
|
538
|
+
question: String(r.question || ''),
|
|
539
|
+
priority: String(r.priority || 'medium'),
|
|
540
|
+
resolved: Boolean(r.resolved),
|
|
541
|
+
answer: r.answer ? String(r.answer) : null,
|
|
542
|
+
createdAt: String(r.created_at || ''),
|
|
543
|
+
}));
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
return [];
|
|
547
|
+
}
|
|
548
|
+
}
|
|
173
549
|
function buildWhoYouAre(profile, skills, collaborators, stackDNA, blindSpots) {
|
|
174
550
|
const lines = [];
|
|
175
|
-
|
|
551
|
+
if (profile.project) {
|
|
552
|
+
const files = profile.files_scanned || 0;
|
|
553
|
+
const lines1 = profile.total_lines || 0;
|
|
554
|
+
lines.push(`Project: ${profile.project} (${files} files, ${lines1} lines)`);
|
|
555
|
+
}
|
|
176
556
|
if (stackDNA && typeof stackDNA === 'object') {
|
|
177
557
|
const s = stackDNA;
|
|
178
558
|
const parts = [];
|
|
@@ -200,37 +580,76 @@ function buildWhoYouAre(profile, skills, collaborators, stackDNA, blindSpots) {
|
|
|
200
580
|
const names = collaborators.slice(0, 3).map((c) => c.name).filter(Boolean);
|
|
201
581
|
lines.push(`Team: ${names.join(', ')} (${collaborators.length} total)`);
|
|
202
582
|
}
|
|
203
|
-
|
|
583
|
+
const strengths = profile.strengths || [];
|
|
584
|
+
if (strengths.length > 0) {
|
|
585
|
+
lines.push(`Strengths: ${strengths.slice(0, 3).join(' · ')}`);
|
|
586
|
+
}
|
|
204
587
|
if (blindSpots && blindSpots.length > 0) {
|
|
205
|
-
const top = blindSpots.slice(0, 3).map(b => b.pattern_value || b.description || '').filter(Boolean);
|
|
588
|
+
const top = blindSpots.slice(0, 3).map(b => `${b.pattern_value || b.description || 'unknown'} (${b.count || 0}x)`).filter(Boolean);
|
|
206
589
|
if (top.length > 0)
|
|
207
|
-
lines.push(`⚠ ${top.join(' | ')}`);
|
|
590
|
+
lines.push(`⚠ Blind spots: ${top.join(' | ')}`);
|
|
208
591
|
}
|
|
209
592
|
if (lines.length === 0) {
|
|
210
593
|
lines.push('Developer profile — run `cell scan` to build profile');
|
|
211
594
|
}
|
|
212
595
|
return lines.join('\n');
|
|
213
596
|
}
|
|
214
|
-
function buildRightNow(activity, profile) {
|
|
597
|
+
function buildRightNow(activity, profile, lastSession) {
|
|
215
598
|
const lines = [];
|
|
216
|
-
if (
|
|
217
|
-
const
|
|
218
|
-
lines.push(`Last
|
|
219
|
-
|
|
599
|
+
if (lastSession) {
|
|
600
|
+
const ago = lastSession.endTime ? formatRelativeTime(lastSession.endTime) : 'in progress';
|
|
601
|
+
lines.push(`Last session: ${ago} (${lastSession.toolName})`);
|
|
602
|
+
if (lastSession.contextSnapshot) {
|
|
603
|
+
lines.push(`Working on: ${lastSession.contextSnapshot}`);
|
|
604
|
+
}
|
|
605
|
+
if (lastSession.keyDecisions.length > 0) {
|
|
606
|
+
lines.push(`Decisions: ${lastSession.keyDecisions.slice(0, 3).join(' · ')}`);
|
|
607
|
+
}
|
|
608
|
+
if (lastSession.filesTouched.length > 0) {
|
|
609
|
+
lines.push(`Current file: ${lastSession.filesTouched[0]}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
else if (activity.length > 0) {
|
|
613
|
+
const lastScan = activity[0];
|
|
614
|
+
lines.push(`Last scan: ${lastScan.files_scanned || 0} files scanned`);
|
|
615
|
+
if (lastScan.completed_at) {
|
|
616
|
+
lines.push(`Last active: ${formatRelativeTime(lastScan.completed_at)}`);
|
|
617
|
+
}
|
|
618
|
+
if (lastScan.project) {
|
|
619
|
+
lines.push(`Project: ${lastScan.project}`);
|
|
620
|
+
}
|
|
220
621
|
}
|
|
221
622
|
else {
|
|
222
623
|
lines.push('No recent activity detected');
|
|
223
624
|
lines.push('Run `cell scan` to refresh');
|
|
224
625
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const latest = products[products.length - 1];
|
|
228
|
-
lines.push(`Latest project: ${latest.name || 'unknown'} ${latest.latestVersion ? '(' + latest.latestVersion + ')' : ''}`);
|
|
626
|
+
if (profile.scanned_at) {
|
|
627
|
+
lines.push(`Profile last built: ${formatRelativeTime(profile.scanned_at)}`);
|
|
229
628
|
}
|
|
230
629
|
return lines.join('\n');
|
|
231
630
|
}
|
|
232
631
|
function buildYourStyle(patterns, profile, skills) {
|
|
233
632
|
const lines = [];
|
|
633
|
+
const styleBits = [];
|
|
634
|
+
if (profile.quote_style)
|
|
635
|
+
styleBits.push(`Quotes: ${profile.quote_style}`);
|
|
636
|
+
if (profile.semicolon_style)
|
|
637
|
+
styleBits.push(`Semicolons: ${profile.semicolon_style}`);
|
|
638
|
+
if (profile.indent_style)
|
|
639
|
+
styleBits.push(`Indent: ${profile.indent_style}`);
|
|
640
|
+
if (profile.naming_style)
|
|
641
|
+
styleBits.push(`Naming: ${profile.naming_style}`);
|
|
642
|
+
if (profile.async_style)
|
|
643
|
+
styleBits.push(`Async: ${profile.async_style}`);
|
|
644
|
+
if (profile.error_handling)
|
|
645
|
+
styleBits.push(`Errors: ${profile.error_handling}`);
|
|
646
|
+
if (profile.function_style)
|
|
647
|
+
styleBits.push(`Functions: ${profile.function_style}`);
|
|
648
|
+
if (profile.test_pattern)
|
|
649
|
+
styleBits.push(`Tests: ${profile.test_pattern}`);
|
|
650
|
+
if (styleBits.length > 0) {
|
|
651
|
+
lines.push(styleBits.join(' · '));
|
|
652
|
+
}
|
|
234
653
|
const categories = new Map();
|
|
235
654
|
for (const p of patterns) {
|
|
236
655
|
const cat = String(p.category || '');
|
|
@@ -240,65 +659,65 @@ function buildYourStyle(patterns, profile, skills) {
|
|
|
240
659
|
if (sortedCats.length > 0) {
|
|
241
660
|
lines.push(`Top patterns: ${sortedCats.map(([c, n]) => `${c}(${n})`).join(', ')}`);
|
|
242
661
|
}
|
|
243
|
-
const topRules = patterns.slice(0, 5).map((p) => p.rule).filter(Boolean);
|
|
244
|
-
if (topRules.length > 0) {
|
|
245
|
-
lines.push('Style preferences:');
|
|
246
|
-
for (const r of topRules) {
|
|
247
|
-
lines.push(` - ${r}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
662
|
const doList = [];
|
|
251
|
-
const dontList = [];
|
|
252
663
|
for (const p of patterns) {
|
|
253
|
-
const rule = String(p.
|
|
664
|
+
const rule = String(p.pattern_value || '');
|
|
254
665
|
const cat = String(p.category || '');
|
|
255
|
-
if (cat === 'typescript_strict' || rule.
|
|
666
|
+
if (cat === 'typescript_strict' || rule.toLowerCase().includes('type')) {
|
|
256
667
|
doList.push('DO: TypeScript strict mode');
|
|
257
668
|
}
|
|
258
|
-
if (cat === '
|
|
669
|
+
if (cat === 'async' || rule.toLowerCase().includes('async') || profile.async_style === 'uses-async') {
|
|
259
670
|
doList.push('DO: Use async/await, not .then()');
|
|
260
671
|
}
|
|
261
|
-
if (cat === 'null_safety' || rule.includes('optional
|
|
672
|
+
if (cat === 'null_safety' || rule.toLowerCase().includes('optional')) {
|
|
262
673
|
doList.push('DO: Use optional chaining');
|
|
263
674
|
}
|
|
264
|
-
if (cat === 'destructuring' || rule.includes('
|
|
675
|
+
if (cat === 'destructuring' || rule.toLowerCase().includes('destructur')) {
|
|
265
676
|
doList.push('DO: Use destructuring over index access');
|
|
266
677
|
}
|
|
267
678
|
}
|
|
268
|
-
const unique = new Set(doList);
|
|
269
|
-
if (unique.
|
|
270
|
-
lines.push(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
679
|
+
const unique = [...new Set(doList)];
|
|
680
|
+
if (unique.length > 0) {
|
|
681
|
+
lines.push('');
|
|
682
|
+
lines.push('DO list:');
|
|
683
|
+
unique.forEach(d => lines.push(' ' + d));
|
|
684
|
+
}
|
|
685
|
+
lines.push('');
|
|
686
|
+
lines.push("DON'T list:");
|
|
687
|
+
lines.push(' - Use var (use const/let)');
|
|
688
|
+
lines.push(' - Skip try/catch on async');
|
|
689
|
+
lines.push(' - Skip null checks on API responses');
|
|
274
690
|
return lines.join('\n');
|
|
275
691
|
}
|
|
276
692
|
function buildWatchOut(blindSpots, profile) {
|
|
277
693
|
const lines = [];
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
lines.push('Run `cell scan` to detect
|
|
281
|
-
lines.push('Common checks: error handling, null safety, test coverage');
|
|
694
|
+
if (blindSpots.length === 0) {
|
|
695
|
+
lines.push('No blind spots detected yet.');
|
|
696
|
+
lines.push('Run `cell scan` to detect error handling, null safety, and validation gaps');
|
|
282
697
|
return lines.join('\n');
|
|
283
698
|
}
|
|
284
|
-
lines.push(
|
|
699
|
+
lines.push(`Top blind spots (${blindSpots.length} total):`);
|
|
285
700
|
const top = blindSpots.slice(0, 5);
|
|
286
701
|
for (const s of top) {
|
|
287
|
-
const desc = String(s.description || '');
|
|
702
|
+
const desc = String(s.pattern_value || s.description || 'unknown');
|
|
703
|
+
const count = s.count || 0;
|
|
704
|
+
const trust = s.trust_score || 0;
|
|
288
705
|
if (desc)
|
|
289
|
-
lines.push(` - [${
|
|
706
|
+
lines.push(` - [${count}x, trust ${trust}] ${desc}`);
|
|
707
|
+
}
|
|
708
|
+
if (profile.improvements && Array.isArray(profile.improvements) && profile.improvements.length > 0) {
|
|
709
|
+
lines.push('');
|
|
710
|
+
lines.push('Suggested improvements:');
|
|
711
|
+
const improvements = profile.improvements;
|
|
712
|
+
improvements.slice(0, 3).forEach((imp) => lines.push(' - ' + imp));
|
|
290
713
|
}
|
|
291
|
-
lines.push('Always flag if:');
|
|
292
|
-
lines.push(' - async function missing try/catch');
|
|
293
|
-
lines.push(' - API response used without null check');
|
|
294
|
-
lines.push(' - Database operation without validation');
|
|
295
714
|
return lines.join('\n');
|
|
296
715
|
}
|
|
297
716
|
function buildPredictions(blindSpots, activity, toolStats) {
|
|
298
717
|
const lines = [];
|
|
299
|
-
const
|
|
300
|
-
if (
|
|
301
|
-
lines.push(`⚠ ${
|
|
718
|
+
const highTrustBlindSpots = blindSpots.filter((b) => b.trust_score >= 80).length;
|
|
719
|
+
if (highTrustBlindSpots > 0) {
|
|
720
|
+
lines.push(`⚠ ${highTrustBlindSpots} high-confidence blind spots likely to recur`);
|
|
302
721
|
}
|
|
303
722
|
if (toolStats) {
|
|
304
723
|
const acceptRate = toolStats.acceptance_rate;
|
|
@@ -310,9 +729,9 @@ function buildPredictions(blindSpots, activity, toolStats) {
|
|
|
310
729
|
}
|
|
311
730
|
}
|
|
312
731
|
if (activity.length > 0) {
|
|
313
|
-
const
|
|
314
|
-
if (
|
|
315
|
-
lines.push(
|
|
732
|
+
const lastScan = activity[0];
|
|
733
|
+
if (lastScan.completed_at) {
|
|
734
|
+
lines.push(`Last scan: ${formatRelativeTime(lastScan.completed_at)}`);
|
|
316
735
|
}
|
|
317
736
|
}
|
|
318
737
|
if (lines.length === 0) {
|
|
@@ -361,4 +780,58 @@ function safeJson(val) {
|
|
|
361
780
|
return val;
|
|
362
781
|
}
|
|
363
782
|
}
|
|
783
|
+
exports.DEFAULT_STALE_DAYS = 7;
|
|
784
|
+
exports.DEFAULT_EXPIRED_DAYS = 30;
|
|
785
|
+
function getStalenessStatus(timestamp, staleDays = exports.DEFAULT_STALE_DAYS, expiredDays = exports.DEFAULT_EXPIRED_DAYS) {
|
|
786
|
+
if (!timestamp)
|
|
787
|
+
return 'unknown';
|
|
788
|
+
const t = new Date(timestamp).getTime();
|
|
789
|
+
if (!Number.isFinite(t))
|
|
790
|
+
return 'unknown';
|
|
791
|
+
const ageMs = Date.now() - t;
|
|
792
|
+
if (ageMs < 0)
|
|
793
|
+
return 'fresh';
|
|
794
|
+
const days = ageMs / (1000 * 60 * 60 * 24);
|
|
795
|
+
if (days < staleDays)
|
|
796
|
+
return 'fresh';
|
|
797
|
+
if (days < expiredDays)
|
|
798
|
+
return 'stale';
|
|
799
|
+
return 'expired';
|
|
800
|
+
}
|
|
801
|
+
function daysSince(timestamp) {
|
|
802
|
+
if (!timestamp)
|
|
803
|
+
return null;
|
|
804
|
+
const t = new Date(timestamp).getTime();
|
|
805
|
+
if (!Number.isFinite(t))
|
|
806
|
+
return null;
|
|
807
|
+
const days = (Date.now() - t) / (1000 * 60 * 60 * 24);
|
|
808
|
+
return Math.max(0, Math.round(days * 10) / 10);
|
|
809
|
+
}
|
|
810
|
+
function buildStalenessInfo(profile, recentActivity) {
|
|
811
|
+
const profileTs = profile.scanned_at || undefined;
|
|
812
|
+
const scanTs = recentActivity[0]?.completed_at || undefined;
|
|
813
|
+
const profileLevel = getStalenessStatus(profileTs);
|
|
814
|
+
const scanLevel = getStalenessStatus(scanTs);
|
|
815
|
+
const daysSinceProfile = daysSince(profileTs);
|
|
816
|
+
const daysSinceScan = daysSince(scanTs);
|
|
817
|
+
const shouldRescan = profileLevel === 'stale' || profileLevel === 'expired' || scanLevel === 'stale' || scanLevel === 'expired';
|
|
818
|
+
let warning = null;
|
|
819
|
+
if (profileLevel === 'expired') {
|
|
820
|
+
warning = `Profile is ${daysSinceProfile ?? '?'} days old — run \`cell scan\` to refresh (context may be outdated)`;
|
|
821
|
+
}
|
|
822
|
+
else if (profileLevel === 'stale') {
|
|
823
|
+
warning = `Profile is ${daysSinceProfile ?? '?'} days old — consider running \`cell scan\` to keep context fresh`;
|
|
824
|
+
}
|
|
825
|
+
else if (scanLevel === 'expired' || scanLevel === 'stale') {
|
|
826
|
+
warning = `Last scan is ${daysSinceScan ?? '?'} days old — run \`cell scan\` to refresh`;
|
|
827
|
+
}
|
|
828
|
+
return {
|
|
829
|
+
profile: profileLevel,
|
|
830
|
+
scan: scanLevel,
|
|
831
|
+
daysSinceProfile,
|
|
832
|
+
daysSinceScan,
|
|
833
|
+
shouldRescan,
|
|
834
|
+
warning,
|
|
835
|
+
};
|
|
836
|
+
}
|
|
364
837
|
//# sourceMappingURL=prompt-builder.js.map
|