fivocell 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +302 -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 +132 -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 +368 -1
- package/dist/cli.js.map +1 -1
- package/dist/code-scanner.d.ts.map +1 -1
- package/dist/code-scanner.js +301 -97
- 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 +591 -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 +482 -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/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 +58 -30
- 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,77 @@ function formatContextForInjection(ctx, compact = false) {
|
|
|
39
52
|
}
|
|
40
53
|
return fullContext(ctx);
|
|
41
54
|
}
|
|
55
|
+
function formatContextForTool(ctx, toolName) {
|
|
56
|
+
if (!toolName)
|
|
57
|
+
return fullContext(ctx);
|
|
58
|
+
const t = toolName.toLowerCase().trim();
|
|
59
|
+
if (t === 'claude-code' || t === 'claude' || t === 'claude-code-cli') {
|
|
60
|
+
return fullContext(ctx);
|
|
61
|
+
}
|
|
62
|
+
if (t === 'cursor' || t === 'cursor-ide' || t === 'cursor-ai') {
|
|
63
|
+
return formatForCursor(ctx);
|
|
64
|
+
}
|
|
65
|
+
if (t === 'windsurf' || t === 'codeium' || t === 'windsurf-ide') {
|
|
66
|
+
return formatForWindsurf(ctx);
|
|
67
|
+
}
|
|
68
|
+
if (t === 'copilot' || t === 'github-copilot') {
|
|
69
|
+
return formatForCopilot(ctx);
|
|
70
|
+
}
|
|
71
|
+
if (t === 'antigravity' || t === 'aider' || t === 'continue-dev') {
|
|
72
|
+
return formatForAntigravity(ctx);
|
|
73
|
+
}
|
|
74
|
+
return fullContext(ctx);
|
|
75
|
+
}
|
|
42
76
|
function estimateTokens(text) {
|
|
43
77
|
return Math.ceil(text.length / 4);
|
|
44
78
|
}
|
|
45
79
|
function fullContext(ctx) {
|
|
46
80
|
let out = '';
|
|
47
|
-
out += '
|
|
81
|
+
out += '━━━ CELL CONTEXT ━━━\n\n';
|
|
82
|
+
if (ctx.staleness?.warning) {
|
|
83
|
+
out += '⚠ ' + ctx.staleness.warning + '\n\n';
|
|
84
|
+
}
|
|
48
85
|
out += '▸ WHO YOU ARE\n';
|
|
49
86
|
out += ctx.whoYouAre + '\n\n';
|
|
50
87
|
out += '▸ RIGHT NOW\n';
|
|
51
88
|
out += ctx.rightNow + '\n\n';
|
|
89
|
+
if (ctx.lastSession) {
|
|
90
|
+
const ago = ctx.lastSession.endTime ? formatRelativeTime(ctx.lastSession.endTime) : 'active';
|
|
91
|
+
out += '▸ LAST SESSION (' + ago + ')\n';
|
|
92
|
+
out += `Tool: ${ctx.lastSession.toolName}\n`;
|
|
93
|
+
if (ctx.lastSession.contextSnapshot) {
|
|
94
|
+
out += `Working on: ${ctx.lastSession.contextSnapshot}\n`;
|
|
95
|
+
}
|
|
96
|
+
if (ctx.lastSession.keyDecisions.length > 0) {
|
|
97
|
+
out += 'Key decisions: ' + ctx.lastSession.keyDecisions.slice(0, 3).join(' · ') + '\n';
|
|
98
|
+
}
|
|
99
|
+
out += '\n';
|
|
100
|
+
}
|
|
101
|
+
if (ctx.triedApproaches.length > 0) {
|
|
102
|
+
out += '▸ TRIED ALREADY (don\'t suggest these)\n';
|
|
103
|
+
for (const a of ctx.triedApproaches.slice(0, 5)) {
|
|
104
|
+
out += `→ ${a.approach} — ${a.status}${a.count > 1 ? ` (${a.count}x)` : ''}\n`;
|
|
105
|
+
}
|
|
106
|
+
out += '\n';
|
|
107
|
+
}
|
|
108
|
+
if (ctx.openQuestions.length > 0) {
|
|
109
|
+
out += '▸ OPEN QUESTIONS\n';
|
|
110
|
+
for (const q of ctx.openQuestions.slice(0, 5)) {
|
|
111
|
+
out += `→ [${q.priority}] ${q.question}\n`;
|
|
112
|
+
}
|
|
113
|
+
out += '\n';
|
|
114
|
+
}
|
|
52
115
|
out += '▸ YOUR STYLE\n';
|
|
53
116
|
out += ctx.yourStyle + '\n\n';
|
|
54
117
|
out += '▸ WATCH OUT\n';
|
|
55
118
|
out += ctx.watchOut + '\n\n';
|
|
119
|
+
if (ctx.mostTouchedFiles.length > 0) {
|
|
120
|
+
out += '▸ HOT FILES (recently active)\n';
|
|
121
|
+
for (const f of ctx.mostTouchedFiles.slice(0, 5)) {
|
|
122
|
+
out += `→ ${f.filePath} (${f.touchCount} touches, ${f.sessionCount} sessions)\n`;
|
|
123
|
+
}
|
|
124
|
+
out += '\n';
|
|
125
|
+
}
|
|
56
126
|
out += '▸ PREDICTIONS\n';
|
|
57
127
|
out += ctx.predictions;
|
|
58
128
|
if (ctx.toolHint) {
|
|
@@ -62,10 +132,21 @@ function fullContext(ctx) {
|
|
|
62
132
|
}
|
|
63
133
|
function compactContext(ctx) {
|
|
64
134
|
let out = '';
|
|
135
|
+
if (ctx.staleness?.warning) {
|
|
136
|
+
out += '⚠ ' + ctx.staleness.warning + '\n\n';
|
|
137
|
+
}
|
|
65
138
|
out += '## Developer Context\n';
|
|
66
139
|
out += ctx.whoYouAre.replace(/\n/g, ' | ') + '\n\n';
|
|
67
140
|
out += '### Current\n';
|
|
68
141
|
out += ctx.rightNow.replace(/\n/g, ' | ') + '\n\n';
|
|
142
|
+
if (ctx.triedApproaches.length > 0) {
|
|
143
|
+
out += '### Tried (skip these)\n';
|
|
144
|
+
out += ctx.triedApproaches.slice(0, 3).map(a => `${a.approach}(${a.status})`).join(' | ') + '\n\n';
|
|
145
|
+
}
|
|
146
|
+
if (ctx.openQuestions.length > 0) {
|
|
147
|
+
out += '### Open Questions\n';
|
|
148
|
+
out += ctx.openQuestions.slice(0, 3).map(q => `[${q.priority}] ${q.question}`).join(' | ') + '\n\n';
|
|
149
|
+
}
|
|
69
150
|
out += '### Style\n';
|
|
70
151
|
out += ctx.yourStyle.replace(/\n/g, ' | ') + '\n\n';
|
|
71
152
|
out += '### Blind Spots\n';
|
|
@@ -77,16 +158,142 @@ function compactContext(ctx) {
|
|
|
77
158
|
}
|
|
78
159
|
return out;
|
|
79
160
|
}
|
|
80
|
-
function
|
|
161
|
+
function formatForCursor(ctx) {
|
|
162
|
+
const lines = [];
|
|
163
|
+
lines.push('@context fivo-cell');
|
|
164
|
+
if (ctx.staleness?.warning)
|
|
165
|
+
lines.push('! ' + ctx.staleness.warning);
|
|
166
|
+
if (ctx.lastSession) {
|
|
167
|
+
lines.push('last: ' + ctx.lastSession.toolName + ' (' + (ctx.lastSession.endTime ? formatRelativeTime(ctx.lastSession.endTime) : 'active') + ')');
|
|
168
|
+
if (ctx.lastSession.contextSnapshot)
|
|
169
|
+
lines.push('working: ' + ctx.lastSession.contextSnapshot);
|
|
170
|
+
}
|
|
171
|
+
lines.push('');
|
|
172
|
+
lines.push('style: ' + (ctx.yourStyle.split('\n')[0] || ''));
|
|
173
|
+
if (ctx.triedApproaches.length > 0) {
|
|
174
|
+
lines.push('avoid: ' + ctx.triedApproaches.slice(0, 3).map(a => `${a.approach}[${a.status}]`).join(', '));
|
|
175
|
+
}
|
|
176
|
+
if (ctx.openQuestions.length > 0) {
|
|
177
|
+
lines.push('q: ' + ctx.openQuestions.slice(0, 2).map(q => `${q.question.slice(0, 50)}…`).join(' | '));
|
|
178
|
+
}
|
|
179
|
+
if (ctx.mostTouchedFiles.length > 0) {
|
|
180
|
+
lines.push('hot: ' + ctx.mostTouchedFiles.slice(0, 3).map(f => f.filePath).join(', '));
|
|
181
|
+
}
|
|
182
|
+
if (ctx.watchOut) {
|
|
183
|
+
const topBlindSpot = ctx.watchOut.split('\n').find(l => l.trim().startsWith('-'));
|
|
184
|
+
if (topBlindSpot)
|
|
185
|
+
lines.push('watch: ' + topBlindSpot.replace(/^-\s*/, '').slice(0, 80));
|
|
186
|
+
}
|
|
187
|
+
return lines.join('\n');
|
|
188
|
+
}
|
|
189
|
+
function formatForWindsurf(ctx) {
|
|
190
|
+
const lines = [];
|
|
191
|
+
lines.push('# Cell Context');
|
|
192
|
+
if (ctx.staleness?.warning)
|
|
193
|
+
lines.push('> ⚠ ' + ctx.staleness.warning);
|
|
194
|
+
if (ctx.lastSession) {
|
|
195
|
+
lines.push('## Last Session');
|
|
196
|
+
lines.push(`- **Tool**: ${ctx.lastSession.toolName}`);
|
|
197
|
+
if (ctx.lastSession.contextSnapshot)
|
|
198
|
+
lines.push(`- **Working on**: ${ctx.lastSession.contextSnapshot}`);
|
|
199
|
+
if (ctx.lastSession.keyDecisions.length > 0)
|
|
200
|
+
lines.push(`- **Decisions**: ${ctx.lastSession.keyDecisions.slice(0, 2).join(', ')}`);
|
|
201
|
+
}
|
|
202
|
+
if (ctx.openQuestions.length > 0) {
|
|
203
|
+
lines.push('## Open Questions');
|
|
204
|
+
for (const q of ctx.openQuestions.slice(0, 3)) {
|
|
205
|
+
lines.push(`- [${q.priority}] ${q.question}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
lines.push('## Style');
|
|
209
|
+
lines.push(ctx.yourStyle.split('\n')[0] || '');
|
|
210
|
+
lines.push('## Suggestions');
|
|
211
|
+
if (ctx.watchOut) {
|
|
212
|
+
const spots = ctx.watchOut.split('\n').filter(l => l.trim().startsWith('-')).slice(0, 3);
|
|
213
|
+
spots.forEach(s => lines.push('- ' + s.replace(/^-\s*/, '')));
|
|
214
|
+
}
|
|
215
|
+
if (ctx.mostTouchedFiles.length > 0) {
|
|
216
|
+
lines.push('## Active Files');
|
|
217
|
+
ctx.mostTouchedFiles.slice(0, 3).forEach(f => lines.push(`- ${f.filePath} (${f.touchCount} touches)`));
|
|
218
|
+
}
|
|
219
|
+
return lines.join('\n');
|
|
220
|
+
}
|
|
221
|
+
function formatForCopilot(ctx) {
|
|
222
|
+
const parts = [];
|
|
223
|
+
if (ctx.staleness?.warning)
|
|
224
|
+
parts.push('⚠ ' + ctx.staleness.warning.replace(/`/g, ''));
|
|
225
|
+
parts.push('Project: ' + (ctx.lastSession?.project || 'unknown'));
|
|
226
|
+
if (ctx.lastSession?.contextSnapshot)
|
|
227
|
+
parts.push('Working: ' + ctx.lastSession.contextSnapshot.slice(0, 60));
|
|
228
|
+
if (ctx.triedApproaches.length > 0) {
|
|
229
|
+
const a = ctx.triedApproaches[0];
|
|
230
|
+
parts.push(`Avoid: ${a.approach} (${a.status})`);
|
|
231
|
+
}
|
|
232
|
+
const styleLine = ctx.yourStyle.split('\n')[0] || '';
|
|
233
|
+
if (styleLine)
|
|
234
|
+
parts.push('Style: ' + styleLine.slice(0, 80));
|
|
235
|
+
return parts.join(' · ');
|
|
236
|
+
}
|
|
237
|
+
function formatForAntigravity(ctx) {
|
|
238
|
+
const lines = [];
|
|
239
|
+
lines.push('## Cell Context — Structured');
|
|
240
|
+
if (ctx.staleness?.warning)
|
|
241
|
+
lines.push('> ⚠ ' + ctx.staleness.warning);
|
|
242
|
+
lines.push('');
|
|
243
|
+
lines.push('```yaml');
|
|
244
|
+
lines.push(`project: ${ctx.lastSession?.project || 'unknown'}`);
|
|
245
|
+
lines.push(`tool_hint: ${ctx.toolHint || 'general'}`);
|
|
246
|
+
if (ctx.lastSession) {
|
|
247
|
+
lines.push(`last_session:`);
|
|
248
|
+
lines.push(` tool: ${ctx.lastSession.toolName}`);
|
|
249
|
+
lines.push(` when: ${ctx.lastSession.endTime || 'active'}`);
|
|
250
|
+
if (ctx.lastSession.contextSnapshot)
|
|
251
|
+
lines.push(` context: "${ctx.lastSession.contextSnapshot}"`);
|
|
252
|
+
}
|
|
253
|
+
if (ctx.triedApproaches.length > 0) {
|
|
254
|
+
lines.push(`tried_approaches:`);
|
|
255
|
+
ctx.triedApproaches.slice(0, 3).forEach(a => {
|
|
256
|
+
lines.push(` - approach: "${a.approach}"`);
|
|
257
|
+
lines.push(` status: ${a.status}`);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (ctx.openQuestions.length > 0) {
|
|
261
|
+
lines.push(`open_questions:`);
|
|
262
|
+
ctx.openQuestions.slice(0, 3).forEach(q => {
|
|
263
|
+
lines.push(` - priority: ${q.priority}`);
|
|
264
|
+
lines.push(` question: "${q.question}"`);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
lines.push('```');
|
|
268
|
+
return lines.join('\n');
|
|
269
|
+
}
|
|
270
|
+
function readProfile(db, projectPath) {
|
|
81
271
|
try {
|
|
82
|
-
const row =
|
|
272
|
+
const row = projectPath
|
|
273
|
+
? db.prepare('SELECT * FROM developer_profiles WHERE project = ? ORDER BY scanned_at DESC LIMIT 1').get(projectPath)
|
|
274
|
+
: db.prepare('SELECT * FROM developer_profiles ORDER BY scanned_at DESC LIMIT 1').get();
|
|
83
275
|
if (row) {
|
|
84
276
|
return {
|
|
85
|
-
name: row.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
277
|
+
name: row.project || '',
|
|
278
|
+
project: row.project || '',
|
|
279
|
+
naming_style: row.naming_style || '',
|
|
280
|
+
quote_style: row.quote_style || '',
|
|
281
|
+
semicolon_style: row.semicolon_style || '',
|
|
282
|
+
indent_style: row.indent_style || '',
|
|
283
|
+
async_style: row.async_style || '',
|
|
284
|
+
error_handling: row.error_handling || '',
|
|
285
|
+
function_style: row.function_style || '',
|
|
286
|
+
import_style: row.import_style || '',
|
|
287
|
+
export_style: row.export_style || '',
|
|
288
|
+
comment_style: row.comment_style || '',
|
|
289
|
+
architecture_style: row.architecture_style || '',
|
|
290
|
+
test_pattern: row.test_pattern || '',
|
|
291
|
+
top_patterns: safeJson(row.top_patterns),
|
|
292
|
+
strengths: safeJson(row.strengths),
|
|
293
|
+
improvements: safeJson(row.improvements),
|
|
294
|
+
files_scanned: row.files_scanned || 0,
|
|
295
|
+
total_lines: row.total_lines || 0,
|
|
296
|
+
scanned_at: row.scanned_at || '',
|
|
90
297
|
};
|
|
91
298
|
}
|
|
92
299
|
}
|
|
@@ -95,17 +302,23 @@ function readProfile(db) {
|
|
|
95
302
|
}
|
|
96
303
|
return {};
|
|
97
304
|
}
|
|
98
|
-
function readBlindSpots(db) {
|
|
305
|
+
function readBlindSpots(db, projectPath) {
|
|
99
306
|
try {
|
|
100
|
-
|
|
307
|
+
if (projectPath) {
|
|
308
|
+
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);
|
|
309
|
+
}
|
|
310
|
+
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
311
|
}
|
|
102
312
|
catch {
|
|
103
313
|
return [];
|
|
104
314
|
}
|
|
105
315
|
}
|
|
106
|
-
function readTopPatterns(db) {
|
|
316
|
+
function readTopPatterns(db, projectPath) {
|
|
107
317
|
try {
|
|
108
|
-
|
|
318
|
+
if (projectPath) {
|
|
319
|
+
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);
|
|
320
|
+
}
|
|
321
|
+
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
322
|
}
|
|
110
323
|
catch {
|
|
111
324
|
return [];
|
|
@@ -114,9 +327,9 @@ function readTopPatterns(db) {
|
|
|
114
327
|
function readRecentActivity(db, projectPath) {
|
|
115
328
|
try {
|
|
116
329
|
if (projectPath) {
|
|
117
|
-
return db.prepare('SELECT * FROM
|
|
330
|
+
return db.prepare('SELECT * FROM scans WHERE project = ? ORDER BY completed_at DESC LIMIT 5').all(projectPath);
|
|
118
331
|
}
|
|
119
|
-
return db.prepare('SELECT * FROM
|
|
332
|
+
return db.prepare('SELECT * FROM scans ORDER BY completed_at DESC LIMIT 5').all();
|
|
120
333
|
}
|
|
121
334
|
catch {
|
|
122
335
|
return [];
|
|
@@ -170,9 +383,118 @@ function readPredictions(db, projectPath) {
|
|
|
170
383
|
return [];
|
|
171
384
|
}
|
|
172
385
|
}
|
|
386
|
+
function readLastSession(db, projectPath) {
|
|
387
|
+
try {
|
|
388
|
+
const query = projectPath
|
|
389
|
+
? 'SELECT * FROM sessions WHERE project = ? AND end_time IS NOT NULL ORDER BY end_time DESC LIMIT 1'
|
|
390
|
+
: 'SELECT * FROM sessions WHERE end_time IS NOT NULL ORDER BY end_time DESC LIMIT 1';
|
|
391
|
+
const params = projectPath ? [projectPath] : [];
|
|
392
|
+
const row = db.prepare(query).get(...(params));
|
|
393
|
+
if (!row)
|
|
394
|
+
return undefined;
|
|
395
|
+
return {
|
|
396
|
+
id: Number(row.id),
|
|
397
|
+
toolName: String(row.tool_name || ''),
|
|
398
|
+
project: String(row.project || ''),
|
|
399
|
+
startTime: String(row.start_time || ''),
|
|
400
|
+
endTime: row.end_time ? String(row.end_time) : undefined,
|
|
401
|
+
filesTouched: safeJson(String(row.files_touched)) || [],
|
|
402
|
+
keyDecisions: safeJson(String(row.key_decisions)) || [],
|
|
403
|
+
contextSnapshot: String(row.context_snapshot || ''),
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function readTriedApproaches(db, projectPath) {
|
|
411
|
+
try {
|
|
412
|
+
const query = projectPath
|
|
413
|
+
? `SELECT approach, status, COUNT(*) as count, MAX(reason) as last_reason, MAX(created_at) as last_seen
|
|
414
|
+
FROM session_approaches
|
|
415
|
+
WHERE project = ?
|
|
416
|
+
GROUP BY LOWER(approach)
|
|
417
|
+
ORDER BY count DESC, last_seen DESC
|
|
418
|
+
LIMIT 10`
|
|
419
|
+
: `SELECT approach, status, COUNT(*) as count, MAX(reason) as last_reason, MAX(created_at) as last_seen
|
|
420
|
+
FROM session_approaches
|
|
421
|
+
GROUP BY LOWER(approach)
|
|
422
|
+
ORDER BY count DESC, last_seen DESC
|
|
423
|
+
LIMIT 10`;
|
|
424
|
+
const params = projectPath ? [projectPath] : [];
|
|
425
|
+
const rows = db.prepare(query).all(...(params));
|
|
426
|
+
return rows.map(r => ({
|
|
427
|
+
approach: r.approach,
|
|
428
|
+
status: r.status,
|
|
429
|
+
count: r.count,
|
|
430
|
+
lastReason: r.last_reason,
|
|
431
|
+
lastSeen: r.last_seen,
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return [];
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function readMostTouchedFiles(db, projectPath) {
|
|
439
|
+
try {
|
|
440
|
+
const query = projectPath
|
|
441
|
+
? `SELECT file_path, SUM(touch_count) as total_touches, COUNT(DISTINCT session_id) as session_count, MAX(last_touched) as last_touched
|
|
442
|
+
FROM session_files
|
|
443
|
+
WHERE project = ?
|
|
444
|
+
GROUP BY file_path
|
|
445
|
+
ORDER BY total_touches DESC, last_touched DESC
|
|
446
|
+
LIMIT 10`
|
|
447
|
+
: `SELECT file_path, SUM(touch_count) as total_touches, COUNT(DISTINCT session_id) as session_count, MAX(last_touched) as last_touched
|
|
448
|
+
FROM session_files
|
|
449
|
+
GROUP BY file_path
|
|
450
|
+
ORDER BY total_touches DESC, last_touched DESC
|
|
451
|
+
LIMIT 10`;
|
|
452
|
+
const params = projectPath ? [projectPath] : [];
|
|
453
|
+
const rows = db.prepare(query).all(...(params));
|
|
454
|
+
return rows.map(r => ({
|
|
455
|
+
filePath: r.file_path,
|
|
456
|
+
touchCount: r.total_touches,
|
|
457
|
+
sessionCount: r.session_count,
|
|
458
|
+
lastTouched: r.last_touched,
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
return [];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function readOpenQuestions(db, projectPath) {
|
|
466
|
+
try {
|
|
467
|
+
const query = projectPath
|
|
468
|
+
? `SELECT * FROM session_questions
|
|
469
|
+
WHERE project = ? AND resolved = 0
|
|
470
|
+
ORDER BY CASE priority WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END, created_at DESC
|
|
471
|
+
LIMIT 10`
|
|
472
|
+
: `SELECT * FROM session_questions
|
|
473
|
+
WHERE resolved = 0
|
|
474
|
+
ORDER BY CASE priority WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END, created_at DESC
|
|
475
|
+
LIMIT 10`;
|
|
476
|
+
const params = projectPath ? [projectPath] : [];
|
|
477
|
+
const rows = db.prepare(query).all(...(params));
|
|
478
|
+
return rows.map(r => ({
|
|
479
|
+
id: Number(r.id),
|
|
480
|
+
question: String(r.question || ''),
|
|
481
|
+
priority: String(r.priority || 'medium'),
|
|
482
|
+
resolved: Boolean(r.resolved),
|
|
483
|
+
answer: r.answer ? String(r.answer) : null,
|
|
484
|
+
createdAt: String(r.created_at || ''),
|
|
485
|
+
}));
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
return [];
|
|
489
|
+
}
|
|
490
|
+
}
|
|
173
491
|
function buildWhoYouAre(profile, skills, collaborators, stackDNA, blindSpots) {
|
|
174
492
|
const lines = [];
|
|
175
|
-
|
|
493
|
+
if (profile.project) {
|
|
494
|
+
const files = profile.files_scanned || 0;
|
|
495
|
+
const lines1 = profile.total_lines || 0;
|
|
496
|
+
lines.push(`Project: ${profile.project} (${files} files, ${lines1} lines)`);
|
|
497
|
+
}
|
|
176
498
|
if (stackDNA && typeof stackDNA === 'object') {
|
|
177
499
|
const s = stackDNA;
|
|
178
500
|
const parts = [];
|
|
@@ -200,37 +522,76 @@ function buildWhoYouAre(profile, skills, collaborators, stackDNA, blindSpots) {
|
|
|
200
522
|
const names = collaborators.slice(0, 3).map((c) => c.name).filter(Boolean);
|
|
201
523
|
lines.push(`Team: ${names.join(', ')} (${collaborators.length} total)`);
|
|
202
524
|
}
|
|
203
|
-
|
|
525
|
+
const strengths = profile.strengths || [];
|
|
526
|
+
if (strengths.length > 0) {
|
|
527
|
+
lines.push(`Strengths: ${strengths.slice(0, 3).join(' · ')}`);
|
|
528
|
+
}
|
|
204
529
|
if (blindSpots && blindSpots.length > 0) {
|
|
205
|
-
const top = blindSpots.slice(0, 3).map(b => b.pattern_value || b.description || '').filter(Boolean);
|
|
530
|
+
const top = blindSpots.slice(0, 3).map(b => `${b.pattern_value || b.description || 'unknown'} (${b.count || 0}x)`).filter(Boolean);
|
|
206
531
|
if (top.length > 0)
|
|
207
|
-
lines.push(`⚠ ${top.join(' | ')}`);
|
|
532
|
+
lines.push(`⚠ Blind spots: ${top.join(' | ')}`);
|
|
208
533
|
}
|
|
209
534
|
if (lines.length === 0) {
|
|
210
535
|
lines.push('Developer profile — run `cell scan` to build profile');
|
|
211
536
|
}
|
|
212
537
|
return lines.join('\n');
|
|
213
538
|
}
|
|
214
|
-
function buildRightNow(activity, profile) {
|
|
539
|
+
function buildRightNow(activity, profile, lastSession) {
|
|
215
540
|
const lines = [];
|
|
216
|
-
if (
|
|
217
|
-
const
|
|
218
|
-
lines.push(`Last
|
|
219
|
-
|
|
541
|
+
if (lastSession) {
|
|
542
|
+
const ago = lastSession.endTime ? formatRelativeTime(lastSession.endTime) : 'in progress';
|
|
543
|
+
lines.push(`Last session: ${ago} (${lastSession.toolName})`);
|
|
544
|
+
if (lastSession.contextSnapshot) {
|
|
545
|
+
lines.push(`Working on: ${lastSession.contextSnapshot}`);
|
|
546
|
+
}
|
|
547
|
+
if (lastSession.keyDecisions.length > 0) {
|
|
548
|
+
lines.push(`Decisions: ${lastSession.keyDecisions.slice(0, 3).join(' · ')}`);
|
|
549
|
+
}
|
|
550
|
+
if (lastSession.filesTouched.length > 0) {
|
|
551
|
+
lines.push(`Current file: ${lastSession.filesTouched[0]}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
else if (activity.length > 0) {
|
|
555
|
+
const lastScan = activity[0];
|
|
556
|
+
lines.push(`Last scan: ${lastScan.files_scanned || 0} files scanned`);
|
|
557
|
+
if (lastScan.completed_at) {
|
|
558
|
+
lines.push(`Last active: ${formatRelativeTime(lastScan.completed_at)}`);
|
|
559
|
+
}
|
|
560
|
+
if (lastScan.project) {
|
|
561
|
+
lines.push(`Project: ${lastScan.project}`);
|
|
562
|
+
}
|
|
220
563
|
}
|
|
221
564
|
else {
|
|
222
565
|
lines.push('No recent activity detected');
|
|
223
566
|
lines.push('Run `cell scan` to refresh');
|
|
224
567
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const latest = products[products.length - 1];
|
|
228
|
-
lines.push(`Latest project: ${latest.name || 'unknown'} ${latest.latestVersion ? '(' + latest.latestVersion + ')' : ''}`);
|
|
568
|
+
if (profile.scanned_at) {
|
|
569
|
+
lines.push(`Profile last built: ${formatRelativeTime(profile.scanned_at)}`);
|
|
229
570
|
}
|
|
230
571
|
return lines.join('\n');
|
|
231
572
|
}
|
|
232
573
|
function buildYourStyle(patterns, profile, skills) {
|
|
233
574
|
const lines = [];
|
|
575
|
+
const styleBits = [];
|
|
576
|
+
if (profile.quote_style)
|
|
577
|
+
styleBits.push(`Quotes: ${profile.quote_style}`);
|
|
578
|
+
if (profile.semicolon_style)
|
|
579
|
+
styleBits.push(`Semicolons: ${profile.semicolon_style}`);
|
|
580
|
+
if (profile.indent_style)
|
|
581
|
+
styleBits.push(`Indent: ${profile.indent_style}`);
|
|
582
|
+
if (profile.naming_style)
|
|
583
|
+
styleBits.push(`Naming: ${profile.naming_style}`);
|
|
584
|
+
if (profile.async_style)
|
|
585
|
+
styleBits.push(`Async: ${profile.async_style}`);
|
|
586
|
+
if (profile.error_handling)
|
|
587
|
+
styleBits.push(`Errors: ${profile.error_handling}`);
|
|
588
|
+
if (profile.function_style)
|
|
589
|
+
styleBits.push(`Functions: ${profile.function_style}`);
|
|
590
|
+
if (profile.test_pattern)
|
|
591
|
+
styleBits.push(`Tests: ${profile.test_pattern}`);
|
|
592
|
+
if (styleBits.length > 0) {
|
|
593
|
+
lines.push(styleBits.join(' · '));
|
|
594
|
+
}
|
|
234
595
|
const categories = new Map();
|
|
235
596
|
for (const p of patterns) {
|
|
236
597
|
const cat = String(p.category || '');
|
|
@@ -240,65 +601,65 @@ function buildYourStyle(patterns, profile, skills) {
|
|
|
240
601
|
if (sortedCats.length > 0) {
|
|
241
602
|
lines.push(`Top patterns: ${sortedCats.map(([c, n]) => `${c}(${n})`).join(', ')}`);
|
|
242
603
|
}
|
|
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
604
|
const doList = [];
|
|
251
|
-
const dontList = [];
|
|
252
605
|
for (const p of patterns) {
|
|
253
|
-
const rule = String(p.
|
|
606
|
+
const rule = String(p.pattern_value || '');
|
|
254
607
|
const cat = String(p.category || '');
|
|
255
|
-
if (cat === 'typescript_strict' || rule.
|
|
608
|
+
if (cat === 'typescript_strict' || rule.toLowerCase().includes('type')) {
|
|
256
609
|
doList.push('DO: TypeScript strict mode');
|
|
257
610
|
}
|
|
258
|
-
if (cat === '
|
|
611
|
+
if (cat === 'async' || rule.toLowerCase().includes('async') || profile.async_style === 'uses-async') {
|
|
259
612
|
doList.push('DO: Use async/await, not .then()');
|
|
260
613
|
}
|
|
261
|
-
if (cat === 'null_safety' || rule.includes('optional
|
|
614
|
+
if (cat === 'null_safety' || rule.toLowerCase().includes('optional')) {
|
|
262
615
|
doList.push('DO: Use optional chaining');
|
|
263
616
|
}
|
|
264
|
-
if (cat === 'destructuring' || rule.includes('
|
|
617
|
+
if (cat === 'destructuring' || rule.toLowerCase().includes('destructur')) {
|
|
265
618
|
doList.push('DO: Use destructuring over index access');
|
|
266
619
|
}
|
|
267
620
|
}
|
|
268
|
-
const unique = new Set(doList);
|
|
269
|
-
if (unique.
|
|
270
|
-
lines.push(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
621
|
+
const unique = [...new Set(doList)];
|
|
622
|
+
if (unique.length > 0) {
|
|
623
|
+
lines.push('');
|
|
624
|
+
lines.push('DO list:');
|
|
625
|
+
unique.forEach(d => lines.push(' ' + d));
|
|
626
|
+
}
|
|
627
|
+
lines.push('');
|
|
628
|
+
lines.push("DON'T list:");
|
|
629
|
+
lines.push(' - Use var (use const/let)');
|
|
630
|
+
lines.push(' - Skip try/catch on async');
|
|
631
|
+
lines.push(' - Skip null checks on API responses');
|
|
274
632
|
return lines.join('\n');
|
|
275
633
|
}
|
|
276
634
|
function buildWatchOut(blindSpots, profile) {
|
|
277
635
|
const lines = [];
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
lines.push('Run `cell scan` to detect
|
|
281
|
-
lines.push('Common checks: error handling, null safety, test coverage');
|
|
636
|
+
if (blindSpots.length === 0) {
|
|
637
|
+
lines.push('No blind spots detected yet.');
|
|
638
|
+
lines.push('Run `cell scan` to detect error handling, null safety, and validation gaps');
|
|
282
639
|
return lines.join('\n');
|
|
283
640
|
}
|
|
284
|
-
lines.push(
|
|
641
|
+
lines.push(`Top blind spots (${blindSpots.length} total):`);
|
|
285
642
|
const top = blindSpots.slice(0, 5);
|
|
286
643
|
for (const s of top) {
|
|
287
|
-
const desc = String(s.description || '');
|
|
644
|
+
const desc = String(s.pattern_value || s.description || 'unknown');
|
|
645
|
+
const count = s.count || 0;
|
|
646
|
+
const trust = s.trust_score || 0;
|
|
288
647
|
if (desc)
|
|
289
|
-
lines.push(` - [${
|
|
648
|
+
lines.push(` - [${count}x, trust ${trust}] ${desc}`);
|
|
649
|
+
}
|
|
650
|
+
if (profile.improvements && Array.isArray(profile.improvements) && profile.improvements.length > 0) {
|
|
651
|
+
lines.push('');
|
|
652
|
+
lines.push('Suggested improvements:');
|
|
653
|
+
const improvements = profile.improvements;
|
|
654
|
+
improvements.slice(0, 3).forEach((imp) => lines.push(' - ' + imp));
|
|
290
655
|
}
|
|
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
656
|
return lines.join('\n');
|
|
296
657
|
}
|
|
297
658
|
function buildPredictions(blindSpots, activity, toolStats) {
|
|
298
659
|
const lines = [];
|
|
299
|
-
const
|
|
300
|
-
if (
|
|
301
|
-
lines.push(`⚠ ${
|
|
660
|
+
const highTrustBlindSpots = blindSpots.filter((b) => b.trust_score >= 80).length;
|
|
661
|
+
if (highTrustBlindSpots > 0) {
|
|
662
|
+
lines.push(`⚠ ${highTrustBlindSpots} high-confidence blind spots likely to recur`);
|
|
302
663
|
}
|
|
303
664
|
if (toolStats) {
|
|
304
665
|
const acceptRate = toolStats.acceptance_rate;
|
|
@@ -310,9 +671,9 @@ function buildPredictions(blindSpots, activity, toolStats) {
|
|
|
310
671
|
}
|
|
311
672
|
}
|
|
312
673
|
if (activity.length > 0) {
|
|
313
|
-
const
|
|
314
|
-
if (
|
|
315
|
-
lines.push(
|
|
674
|
+
const lastScan = activity[0];
|
|
675
|
+
if (lastScan.completed_at) {
|
|
676
|
+
lines.push(`Last scan: ${formatRelativeTime(lastScan.completed_at)}`);
|
|
316
677
|
}
|
|
317
678
|
}
|
|
318
679
|
if (lines.length === 0) {
|
|
@@ -361,4 +722,58 @@ function safeJson(val) {
|
|
|
361
722
|
return val;
|
|
362
723
|
}
|
|
363
724
|
}
|
|
725
|
+
exports.DEFAULT_STALE_DAYS = 7;
|
|
726
|
+
exports.DEFAULT_EXPIRED_DAYS = 30;
|
|
727
|
+
function getStalenessStatus(timestamp, staleDays = exports.DEFAULT_STALE_DAYS, expiredDays = exports.DEFAULT_EXPIRED_DAYS) {
|
|
728
|
+
if (!timestamp)
|
|
729
|
+
return 'unknown';
|
|
730
|
+
const t = new Date(timestamp).getTime();
|
|
731
|
+
if (!Number.isFinite(t))
|
|
732
|
+
return 'unknown';
|
|
733
|
+
const ageMs = Date.now() - t;
|
|
734
|
+
if (ageMs < 0)
|
|
735
|
+
return 'fresh';
|
|
736
|
+
const days = ageMs / (1000 * 60 * 60 * 24);
|
|
737
|
+
if (days < staleDays)
|
|
738
|
+
return 'fresh';
|
|
739
|
+
if (days < expiredDays)
|
|
740
|
+
return 'stale';
|
|
741
|
+
return 'expired';
|
|
742
|
+
}
|
|
743
|
+
function daysSince(timestamp) {
|
|
744
|
+
if (!timestamp)
|
|
745
|
+
return null;
|
|
746
|
+
const t = new Date(timestamp).getTime();
|
|
747
|
+
if (!Number.isFinite(t))
|
|
748
|
+
return null;
|
|
749
|
+
const days = (Date.now() - t) / (1000 * 60 * 60 * 24);
|
|
750
|
+
return Math.max(0, Math.round(days * 10) / 10);
|
|
751
|
+
}
|
|
752
|
+
function buildStalenessInfo(profile, recentActivity) {
|
|
753
|
+
const profileTs = profile.scanned_at || undefined;
|
|
754
|
+
const scanTs = recentActivity[0]?.completed_at || undefined;
|
|
755
|
+
const profileLevel = getStalenessStatus(profileTs);
|
|
756
|
+
const scanLevel = getStalenessStatus(scanTs);
|
|
757
|
+
const daysSinceProfile = daysSince(profileTs);
|
|
758
|
+
const daysSinceScan = daysSince(scanTs);
|
|
759
|
+
const shouldRescan = profileLevel === 'stale' || profileLevel === 'expired' || scanLevel === 'stale' || scanLevel === 'expired';
|
|
760
|
+
let warning = null;
|
|
761
|
+
if (profileLevel === 'expired') {
|
|
762
|
+
warning = `Profile is ${daysSinceProfile ?? '?'} days old — run \`cell scan\` to refresh (context may be outdated)`;
|
|
763
|
+
}
|
|
764
|
+
else if (profileLevel === 'stale') {
|
|
765
|
+
warning = `Profile is ${daysSinceProfile ?? '?'} days old — consider running \`cell scan\` to keep context fresh`;
|
|
766
|
+
}
|
|
767
|
+
else if (scanLevel === 'expired' || scanLevel === 'stale') {
|
|
768
|
+
warning = `Last scan is ${daysSinceScan ?? '?'} days old — run \`cell scan\` to refresh`;
|
|
769
|
+
}
|
|
770
|
+
return {
|
|
771
|
+
profile: profileLevel,
|
|
772
|
+
scan: scanLevel,
|
|
773
|
+
daysSinceProfile,
|
|
774
|
+
daysSinceScan,
|
|
775
|
+
shouldRescan,
|
|
776
|
+
warning,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
364
779
|
//# sourceMappingURL=prompt-builder.js.map
|