opencode-autognosis 2.3.0 → 2.4.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/database.d.ts +5 -2
- package/dist/database.js +68 -15
- package/dist/unified-api.js +81 -25
- package/package.json +1 -1
package/dist/database.d.ts
CHANGED
|
@@ -13,9 +13,11 @@ export declare class CodeGraphDB {
|
|
|
13
13
|
}): void;
|
|
14
14
|
getJob(id: string): any;
|
|
15
15
|
listJobs(type?: string, limit?: number): any;
|
|
16
|
-
postToBlackboard(author: string, message: string, topic?: string, symbolId?: string): void;
|
|
16
|
+
postToBlackboard(author: string, message: string, topic?: string, symbolId?: string, isPinned?: boolean): void;
|
|
17
|
+
getGraffiti(symbolId: string, limit?: number): any;
|
|
18
|
+
archiveGraffiti(symbolId: string): void;
|
|
19
|
+
pinGraffiti(id: number, pinned?: boolean): void;
|
|
17
20
|
readBlackboard(topic?: string, limit?: number): any;
|
|
18
|
-
getGraffiti(symbolId: string): any;
|
|
19
21
|
acquireLock(resourceId: string, agentName: string, ttlSeconds?: number): void;
|
|
20
22
|
releaseLock(resourceId: string, agentName: string): void;
|
|
21
23
|
isLocked(resourceId: string): {
|
|
@@ -53,6 +55,7 @@ export declare class CodeGraphDB {
|
|
|
53
55
|
};
|
|
54
56
|
findDependents(filePath: string): string[];
|
|
55
57
|
searchSymbols(query: string): any[];
|
|
58
|
+
findAffectedTests(symbolName: string): string[];
|
|
56
59
|
semanticSearch(query: string, limit?: number): Promise<any[]>;
|
|
57
60
|
private cosineSimilarity;
|
|
58
61
|
getStats(): {
|
package/dist/database.js
CHANGED
|
@@ -146,8 +146,11 @@ export class CodeGraphDB {
|
|
|
146
146
|
author TEXT,
|
|
147
147
|
message TEXT,
|
|
148
148
|
topic TEXT,
|
|
149
|
-
symbol_id TEXT,
|
|
150
|
-
|
|
149
|
+
symbol_id TEXT,
|
|
150
|
+
git_hash TEXT, -- Version of code when note was made
|
|
151
|
+
is_archived BOOLEAN DEFAULT 0,
|
|
152
|
+
is_pinned BOOLEAN DEFAULT 0,
|
|
153
|
+
embedding BLOB,
|
|
151
154
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
152
155
|
);
|
|
153
156
|
|
|
@@ -231,19 +234,54 @@ export class CodeGraphDB {
|
|
|
231
234
|
}
|
|
232
235
|
return this.db.prepare("SELECT * FROM background_jobs ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
233
236
|
}
|
|
234
|
-
postToBlackboard(author, message, topic = 'general', symbolId) {
|
|
237
|
+
postToBlackboard(author, message, topic = 'general', symbolId, isPinned = false) {
|
|
238
|
+
// Get current git hash for contextual verification
|
|
239
|
+
let currentHash = "unknown";
|
|
240
|
+
try {
|
|
241
|
+
const { execSync } = require("node:child_process");
|
|
242
|
+
currentHash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
|
243
|
+
}
|
|
244
|
+
catch { }
|
|
235
245
|
const insert = this.db.prepare(`
|
|
236
|
-
INSERT INTO blackboard (author, message, topic, symbol_id)
|
|
237
|
-
VALUES (?, ?, ?, ?)
|
|
246
|
+
INSERT INTO blackboard (author, message, topic, symbol_id, git_hash, is_pinned)
|
|
247
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
238
248
|
RETURNING id
|
|
239
249
|
`);
|
|
240
|
-
const res = insert.get(author, message, topic, symbolId || null);
|
|
241
|
-
// Queue for embedding (blackboard search)
|
|
250
|
+
const res = insert.get(author, message, topic, symbolId || null, currentHash, isPinned ? 1 : 0);
|
|
242
251
|
this.db.prepare(`
|
|
243
252
|
INSERT INTO embedding_queue (chunk_id, text_to_embed)
|
|
244
253
|
VALUES (?, ?)
|
|
245
254
|
`).run(`blackboard-${res.id}`, `${topic.toUpperCase()}: ${message}`);
|
|
246
255
|
}
|
|
256
|
+
getGraffiti(symbolId, limit = 3) {
|
|
257
|
+
// Automatically archive notes older than 7 days that aren't pinned
|
|
258
|
+
this.db.prepare(`
|
|
259
|
+
UPDATE blackboard
|
|
260
|
+
SET is_archived = 1
|
|
261
|
+
WHERE is_pinned = 0
|
|
262
|
+
AND timestamp < datetime('now', '-7 days')
|
|
263
|
+
`).run();
|
|
264
|
+
return this.db.prepare(`
|
|
265
|
+
SELECT author, message, timestamp, git_hash, is_pinned
|
|
266
|
+
FROM blackboard
|
|
267
|
+
WHERE symbol_id = ?
|
|
268
|
+
AND is_archived = 0
|
|
269
|
+
ORDER BY is_pinned DESC, timestamp DESC
|
|
270
|
+
LIMIT ?
|
|
271
|
+
`).all(symbolId, limit);
|
|
272
|
+
}
|
|
273
|
+
archiveGraffiti(symbolId) {
|
|
274
|
+
this.db.prepare(`
|
|
275
|
+
UPDATE blackboard
|
|
276
|
+
SET is_archived = 1
|
|
277
|
+
WHERE symbol_id = ? AND is_pinned = 0
|
|
278
|
+
`).run(symbolId);
|
|
279
|
+
}
|
|
280
|
+
pinGraffiti(id, pinned = true) {
|
|
281
|
+
this.db.prepare(`
|
|
282
|
+
UPDATE blackboard SET is_pinned = ? WHERE id = ?
|
|
283
|
+
`).run(pinned ? 1 : 0, id);
|
|
284
|
+
}
|
|
247
285
|
readBlackboard(topic, limit = 10) {
|
|
248
286
|
if (topic) {
|
|
249
287
|
return this.db.prepare(`
|
|
@@ -254,14 +292,6 @@ export class CodeGraphDB {
|
|
|
254
292
|
SELECT * FROM blackboard ORDER BY timestamp DESC LIMIT ?
|
|
255
293
|
`).all(limit);
|
|
256
294
|
}
|
|
257
|
-
getGraffiti(symbolId) {
|
|
258
|
-
return this.db.prepare(`
|
|
259
|
-
SELECT author, message, timestamp
|
|
260
|
-
FROM blackboard
|
|
261
|
-
WHERE symbol_id = ?
|
|
262
|
-
ORDER BY timestamp DESC
|
|
263
|
-
`).all(symbolId);
|
|
264
|
-
}
|
|
265
295
|
acquireLock(resourceId, agentName, ttlSeconds = 300) {
|
|
266
296
|
// Check if already locked by someone else
|
|
267
297
|
const current = this.isLocked(resourceId);
|
|
@@ -494,6 +524,29 @@ ${card.content.slice(0, 2000)}`;
|
|
|
494
524
|
`);
|
|
495
525
|
return stmt.all(`%${query}%`);
|
|
496
526
|
}
|
|
527
|
+
findAffectedTests(symbolName) {
|
|
528
|
+
// Find all files that call this symbol and look like test files
|
|
529
|
+
const query = this.db.prepare(`
|
|
530
|
+
WITH RECURSIVE impact_tree(caller_chunk_id) AS (
|
|
531
|
+
-- Base case: chunks calling the symbol directly
|
|
532
|
+
SELECT caller_chunk_id FROM calls WHERE callee_name = ?
|
|
533
|
+
UNION
|
|
534
|
+
-- Recursive step: chunks calling chunks in the impact tree
|
|
535
|
+
SELECT c.caller_chunk_id
|
|
536
|
+
FROM calls c
|
|
537
|
+
JOIN impact_tree it ON c.callee_name IN (
|
|
538
|
+
SELECT s.name FROM symbols s WHERE s.chunk_id = it.caller_chunk_id
|
|
539
|
+
)
|
|
540
|
+
)
|
|
541
|
+
SELECT DISTINCT f.path
|
|
542
|
+
FROM files f
|
|
543
|
+
JOIN chunks c ON f.id = c.file_id
|
|
544
|
+
JOIN impact_tree it ON c.id = it.caller_chunk_id
|
|
545
|
+
WHERE f.path LIKE '%.test.%' OR f.path LIKE '%Tests.%' OR f.path LIKE 'test_%'
|
|
546
|
+
`);
|
|
547
|
+
const results = query.all(symbolName);
|
|
548
|
+
return results.map(r => r.path);
|
|
549
|
+
}
|
|
497
550
|
async semanticSearch(query, limit = 10) {
|
|
498
551
|
if (!(await ollama.isRunning()))
|
|
499
552
|
throw new Error("Ollama is not running.");
|
package/dist/unified-api.js
CHANGED
|
@@ -40,14 +40,14 @@ async function updateBridgePrompt(plugins) {
|
|
|
40
40
|
if (!fsSync.existsSync(bridgePath))
|
|
41
41
|
return "bridge.md not found at " + bridgePath;
|
|
42
42
|
const toolsSection = `
|
|
43
|
-
## Current Consolidated Tools (Autognosis v2.
|
|
43
|
+
## Current Consolidated Tools (Autognosis v2.4)
|
|
44
44
|
- code_search: Universal search (semantic, symbol, filename, content).
|
|
45
45
|
- code_analyze: Deep structural analysis and impact reports.
|
|
46
|
-
- code_context: Working memory
|
|
47
|
-
- code_read: Precise
|
|
48
|
-
- code_propose: Planning,
|
|
49
|
-
- code_status:
|
|
50
|
-
- code_setup: Environment initialization
|
|
46
|
+
- code_context: Working memory management and LRU eviction.
|
|
47
|
+
- code_read: Precise reading with Mutex Lock checks and Graffiti retrieval.
|
|
48
|
+
- code_propose: Planning, patching, validation, and PR promotion. Now features surgical test scoping.
|
|
49
|
+
- code_status: Dashboard, background jobs, blackboard, and resource locks.
|
|
50
|
+
- code_setup: Environment initialization and architectural boundaries.
|
|
51
51
|
|
|
52
52
|
## Other Detected Plugins
|
|
53
53
|
${plugins.filter(p => p !== "opencode-autognosis").map(p => `- ${p}`).join('\n')}
|
|
@@ -127,7 +127,7 @@ export function unifiedTools() {
|
|
|
127
127
|
}
|
|
128
128
|
}),
|
|
129
129
|
code_read: tool({
|
|
130
|
-
description: "Precise reading of symbols or file slices. Follows current plan. Checks for locks and returns graffiti.",
|
|
130
|
+
description: "Precise reading of symbols or file slices. Follows current plan. Checks for locks and returns historical graffiti.",
|
|
131
131
|
args: {
|
|
132
132
|
symbol: tool.schema.string().optional().describe("Symbol to jump to"),
|
|
133
133
|
file: tool.schema.string().optional().describe("File path to read"),
|
|
@@ -140,7 +140,20 @@ export function unifiedTools() {
|
|
|
140
140
|
if (resourceId) {
|
|
141
141
|
getDb().logAccess(resourceId, args.plan_id);
|
|
142
142
|
const lock = getDb().isLocked(resourceId);
|
|
143
|
-
const graffiti = getDb().getGraffiti(resourceId);
|
|
143
|
+
const graffiti = getDb().getGraffiti(resourceId, 3);
|
|
144
|
+
let currentHash = "";
|
|
145
|
+
try {
|
|
146
|
+
const { execSync } = await import("node:child_process");
|
|
147
|
+
currentHash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
const verifiedGraffiti = graffiti.map((g) => ({
|
|
151
|
+
author: g.author,
|
|
152
|
+
message: g.message,
|
|
153
|
+
timestamp: g.timestamp,
|
|
154
|
+
status: g.git_hash !== currentHash ? "LEGACY (Potentially Outdated)" : "CURRENT",
|
|
155
|
+
is_pinned: !!g.is_pinned
|
|
156
|
+
}));
|
|
144
157
|
let result;
|
|
145
158
|
if (args.symbol) {
|
|
146
159
|
result = await internal.jump_to_symbol.execute({ symbol: args.symbol, plan_id: args.plan_id });
|
|
@@ -153,7 +166,7 @@ export function unifiedTools() {
|
|
|
153
166
|
...parsed,
|
|
154
167
|
coordination: {
|
|
155
168
|
lock_status: lock ? `LOCKED by ${lock.owner_agent}` : "FREE",
|
|
156
|
-
|
|
169
|
+
historical_notes: verifiedGraffiti.length > 0 ? verifiedGraffiti : undefined
|
|
157
170
|
}
|
|
158
171
|
}, null, 2);
|
|
159
172
|
}
|
|
@@ -161,10 +174,10 @@ export function unifiedTools() {
|
|
|
161
174
|
}
|
|
162
175
|
}),
|
|
163
176
|
code_propose: tool({
|
|
164
|
-
description: "Plan, propose, and promote changes.
|
|
177
|
+
description: "Plan, propose, and promote changes. Includes patch generation, surgical test scoping, and PR promotion.",
|
|
165
178
|
args: {
|
|
166
179
|
action: tool.schema.enum(["plan", "patch", "validate", "finalize", "promote"]),
|
|
167
|
-
symbol: tool.schema.string().optional(),
|
|
180
|
+
symbol: tool.schema.string().optional().describe("Locus symbol for plan"),
|
|
168
181
|
intent: tool.schema.string().optional(),
|
|
169
182
|
reasoning: tool.schema.string().optional(),
|
|
170
183
|
message: tool.schema.string().optional(),
|
|
@@ -180,22 +193,19 @@ export function unifiedTools() {
|
|
|
180
193
|
return internal.brief_fix_loop.execute({ symbol: args.symbol, intent: args.intent });
|
|
181
194
|
}
|
|
182
195
|
case "patch": {
|
|
183
|
-
// 1. Check for locks on all changed files
|
|
184
|
-
const { stdout: diff } = await internal.runCmd("git diff");
|
|
185
196
|
const { stdout: files } = await internal.runCmd("git diff --name-only");
|
|
186
197
|
const changedFiles = files.split('\n').filter(Boolean);
|
|
187
198
|
for (const file of changedFiles) {
|
|
188
199
|
const lock = getDb().isLocked(file);
|
|
189
200
|
if (lock && lock.owner_agent !== agentName) {
|
|
190
|
-
return JSON.stringify({ status: "COLLISION_PREVENTED", message: `File ${file} is locked by ${lock.owner_agent}
|
|
201
|
+
return JSON.stringify({ status: "COLLISION_PREVENTED", message: `File ${file} is locked by ${lock.owner_agent}.` });
|
|
191
202
|
}
|
|
192
203
|
}
|
|
193
|
-
|
|
204
|
+
const { stdout: diff } = await internal.runCmd("git diff");
|
|
194
205
|
const violations = policyEngine.checkDiff(diff);
|
|
195
206
|
if (violations.some(v => v.severity === "error")) {
|
|
196
207
|
return JSON.stringify({ status: "POLICY_VIOLATION", violations, message: "Patch rejected by policy engine." }, null, 2);
|
|
197
208
|
}
|
|
198
|
-
// 3. Prepare patch and record intent
|
|
199
209
|
const res = await internal.prepare_patch.execute({ message: args.message, plan_id: args.plan_id });
|
|
200
210
|
const json = JSON.parse(res);
|
|
201
211
|
if (json.status === "SUCCESS") {
|
|
@@ -206,8 +216,26 @@ export function unifiedTools() {
|
|
|
206
216
|
return res;
|
|
207
217
|
}
|
|
208
218
|
case "validate": {
|
|
209
|
-
|
|
210
|
-
|
|
219
|
+
// 1. Arch Check
|
|
220
|
+
const { stdout: diff } = await internal.runCmd("git diff --name-only");
|
|
221
|
+
const changedFiles = diff.split('\n').filter(Boolean);
|
|
222
|
+
for (const file of changedFiles) {
|
|
223
|
+
const deps = await internal.extractDependencies.execute({ content: "", ast: null, filePath: file });
|
|
224
|
+
const imports = JSON.parse(deps);
|
|
225
|
+
for (const imp of imports) {
|
|
226
|
+
const violation = getDb().checkArchViolation(file, imp);
|
|
227
|
+
if (violation)
|
|
228
|
+
return JSON.stringify({ status: "ARCH_VIOLATION", file, forbidden_import: imp, rule: violation }, null, 2);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// 2. Surgical Test Scoping
|
|
232
|
+
let focusTests = [];
|
|
233
|
+
if (args.symbol) {
|
|
234
|
+
focusTests = getDb().findAffectedTests(args.symbol);
|
|
235
|
+
}
|
|
236
|
+
getDb().postToBlackboard(agentName, `Validating patch ${args.patch_path}. Scoped tests: ${focusTests.length}`, "pulse");
|
|
237
|
+
// Call validation with scoped tests hint
|
|
238
|
+
return internal.validate_patch.execute({ patch_path: args.patch_path, plan_id: args.plan_id, tests: focusTests });
|
|
211
239
|
}
|
|
212
240
|
case "promote": {
|
|
213
241
|
const branch = args.branch || `autognosis-fix-${Date.now()}`;
|
|
@@ -234,10 +262,11 @@ export function unifiedTools() {
|
|
|
234
262
|
code_status: tool({
|
|
235
263
|
description: "Monitor system health, background jobs, Multi-Agent Blackboard, and Resource Locks.",
|
|
236
264
|
args: {
|
|
237
|
-
mode: tool.schema.enum(["stats", "hot_files", "jobs", "plan", "doctor", "blackboard", "locks"]).optional().default("stats"),
|
|
238
|
-
action: tool.schema.enum(["post", "read", "lock", "unlock"]).optional(),
|
|
265
|
+
mode: tool.schema.enum(["stats", "hot_files", "jobs", "plan", "doctor", "blackboard", "locks", "dashboard"]).optional().default("stats"),
|
|
266
|
+
action: tool.schema.enum(["post", "read", "lock", "unlock", "archive", "pin"]).optional(),
|
|
239
267
|
topic: tool.schema.string().optional().default("general"),
|
|
240
|
-
target: tool.schema.string().optional().describe("Resource ID
|
|
268
|
+
target: tool.schema.string().optional().describe("Resource ID or Note ID"),
|
|
269
|
+
pinned: tool.schema.boolean().optional().default(false),
|
|
241
270
|
message: tool.schema.string().optional(),
|
|
242
271
|
job_id: tool.schema.string().optional(),
|
|
243
272
|
plan_id: tool.schema.string().optional(),
|
|
@@ -245,6 +274,25 @@ export function unifiedTools() {
|
|
|
245
274
|
},
|
|
246
275
|
async execute(args) {
|
|
247
276
|
switch (args.mode) {
|
|
277
|
+
case "dashboard": {
|
|
278
|
+
const stats = getDb().getStats();
|
|
279
|
+
const locks = getDb().listLocks();
|
|
280
|
+
const jobs = getDb().listJobs();
|
|
281
|
+
const compliance = args.plan_id ? getDb().getPlanMetrics(args.plan_id) : null;
|
|
282
|
+
let dashboard = `# Autognosis TUI Dashboard\n\n`;
|
|
283
|
+
dashboard += `## 📊 System Stats\n- Files: ${stats.files}\n- Chunks: ${stats.chunks}\n- Embedded: ${stats.embeddings.completed}/${stats.chunks}\n\n`;
|
|
284
|
+
dashboard += `## 🔒 Active Locks\n`;
|
|
285
|
+
if (locks.length > 0)
|
|
286
|
+
dashboard += locks.map((l) => `- ${l.resource_id} (${l.owner_agent})`).join('\n') + '\n\n';
|
|
287
|
+
else
|
|
288
|
+
dashboard += "_No active locks._\n\n";
|
|
289
|
+
dashboard += `## ⚙️ Recent Jobs\n`;
|
|
290
|
+
dashboard += jobs.map((j) => `- [${j.status.toUpperCase()}] ${j.type} (${j.progress}%)`).join('\n') + '\n\n';
|
|
291
|
+
if (compliance) {
|
|
292
|
+
dashboard += `## 📉 Plan Compliance (${args.plan_id})\n- Score: ${compliance.compliance}%\n- Total Calls: ${compliance.total}\n- Off-Plan: ${compliance.off_plan}\n`;
|
|
293
|
+
}
|
|
294
|
+
return dashboard;
|
|
295
|
+
}
|
|
248
296
|
case "locks": {
|
|
249
297
|
if (args.action === "lock") {
|
|
250
298
|
getDb().acquireLock(args.target, agentName);
|
|
@@ -258,9 +306,17 @@ export function unifiedTools() {
|
|
|
258
306
|
}
|
|
259
307
|
case "blackboard": {
|
|
260
308
|
if (args.action === "post") {
|
|
261
|
-
getDb().postToBlackboard(agentName, args.message, args.topic, args.target);
|
|
309
|
+
getDb().postToBlackboard(agentName, args.message, args.topic, args.target, args.pinned);
|
|
262
310
|
return JSON.stringify({ status: "SUCCESS", message: "Posted to blackboard." });
|
|
263
311
|
}
|
|
312
|
+
else if (args.action === "archive") {
|
|
313
|
+
getDb().archiveGraffiti(args.target);
|
|
314
|
+
return JSON.stringify({ status: "SUCCESS", message: `Archived notes for ${args.target}` });
|
|
315
|
+
}
|
|
316
|
+
else if (args.action === "pin") {
|
|
317
|
+
getDb().pinGraffiti(parseInt(args.target, 10), true);
|
|
318
|
+
return JSON.stringify({ status: "SUCCESS", message: `Pinned note ${args.target}` });
|
|
319
|
+
}
|
|
264
320
|
return JSON.stringify({ status: "SUCCESS", entries: getDb().readBlackboard(args.topic) });
|
|
265
321
|
}
|
|
266
322
|
case "hot_files": return internal.journal_query_hot_files.execute({ path_prefix: args.path });
|
|
@@ -280,14 +336,14 @@ export function unifiedTools() {
|
|
|
280
336
|
}
|
|
281
337
|
}),
|
|
282
338
|
code_setup: tool({
|
|
283
|
-
description: "Setup
|
|
339
|
+
description: "Setup tasks (AI, Git Journal, Indexing, Prompt Scouting, Arch Boundaries).",
|
|
284
340
|
args: {
|
|
285
341
|
action: tool.schema.enum(["init", "ai", "index", "journal", "scout", "arch_rule"]),
|
|
286
342
|
provider: tool.schema.enum(["ollama", "mlx"]).optional().default("ollama"),
|
|
287
343
|
model: tool.schema.string().optional(),
|
|
288
344
|
limit: tool.schema.number().optional(),
|
|
289
|
-
source: tool.schema.string().optional()
|
|
290
|
-
target: tool.schema.string().optional()
|
|
345
|
+
source: tool.schema.string().optional(),
|
|
346
|
+
target: tool.schema.string().optional()
|
|
291
347
|
},
|
|
292
348
|
async execute(args) {
|
|
293
349
|
switch (args.action) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-autognosis",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Advanced RAG-powered codebase awareness for OpenCode agents. Features Chunk Cards synthesis, hierarchical reasoning, ActiveSet working memory, and performance optimization for enterprise-scale repositories.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|