opencode-autognosis 2.3.1 → 2.5.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 +3 -0
- package/dist/database.js +44 -0
- package/dist/unified-api.js +123 -29
- package/package.json +1 -1
package/dist/database.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export declare class CodeGraphDB {
|
|
|
12
12
|
error?: string;
|
|
13
13
|
}): void;
|
|
14
14
|
getJob(id: string): any;
|
|
15
|
+
registerContract(triggerTool: string, triggerAction: string, targetTool: string, targetArgs: any): void;
|
|
16
|
+
getContracts(triggerTool: string, triggerAction: string): any[];
|
|
15
17
|
listJobs(type?: string, limit?: number): any;
|
|
16
18
|
postToBlackboard(author: string, message: string, topic?: string, symbolId?: string, isPinned?: boolean): void;
|
|
17
19
|
getGraffiti(symbolId: string, limit?: number): any;
|
|
@@ -55,6 +57,7 @@ export declare class CodeGraphDB {
|
|
|
55
57
|
};
|
|
56
58
|
findDependents(filePath: string): string[];
|
|
57
59
|
searchSymbols(query: string): any[];
|
|
60
|
+
findAffectedTests(symbolName: string): string[];
|
|
58
61
|
semanticSearch(query: string, limit?: number): Promise<any[]>;
|
|
59
62
|
private cosineSimilarity;
|
|
60
63
|
getStats(): {
|
package/dist/database.js
CHANGED
|
@@ -185,6 +185,15 @@ export class CodeGraphDB {
|
|
|
185
185
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
186
186
|
);
|
|
187
187
|
|
|
188
|
+
CREATE TABLE IF NOT EXISTS tool_contracts (
|
|
189
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
190
|
+
trigger_tool TEXT, -- e.g., 'code_propose'
|
|
191
|
+
trigger_action TEXT, -- e.g., 'patch'
|
|
192
|
+
target_tool TEXT,
|
|
193
|
+
target_args TEXT, -- JSON
|
|
194
|
+
condition_script TEXT -- Optional JS snippet to evaluate
|
|
195
|
+
);
|
|
196
|
+
|
|
188
197
|
CREATE INDEX IF NOT EXISTS idx_files_path ON files(path);
|
|
189
198
|
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
|
190
199
|
CREATE INDEX IF NOT EXISTS idx_dependencies_target ON dependencies(target_path);
|
|
@@ -228,6 +237,18 @@ export class CodeGraphDB {
|
|
|
228
237
|
getJob(id) {
|
|
229
238
|
return this.db.prepare("SELECT * FROM background_jobs WHERE id = ?").get(id);
|
|
230
239
|
}
|
|
240
|
+
registerContract(triggerTool, triggerAction, targetTool, targetArgs) {
|
|
241
|
+
this.db.prepare(`
|
|
242
|
+
INSERT INTO tool_contracts (trigger_tool, trigger_action, target_tool, target_args)
|
|
243
|
+
VALUES (?, ?, ?, ?)
|
|
244
|
+
`).run(triggerTool, triggerAction, targetTool, JSON.stringify(targetArgs));
|
|
245
|
+
}
|
|
246
|
+
getContracts(triggerTool, triggerAction) {
|
|
247
|
+
return this.db.prepare(`
|
|
248
|
+
SELECT * FROM tool_contracts
|
|
249
|
+
WHERE trigger_tool = ? AND (trigger_action = ? OR trigger_action IS NULL)
|
|
250
|
+
`).all(triggerTool, triggerAction);
|
|
251
|
+
}
|
|
231
252
|
listJobs(type, limit = 10) {
|
|
232
253
|
if (type) {
|
|
233
254
|
return this.db.prepare("SELECT * FROM background_jobs WHERE type = ? ORDER BY created_at DESC LIMIT ?").all(type, limit);
|
|
@@ -524,6 +545,29 @@ ${card.content.slice(0, 2000)}`;
|
|
|
524
545
|
`);
|
|
525
546
|
return stmt.all(`%${query}%`);
|
|
526
547
|
}
|
|
548
|
+
findAffectedTests(symbolName) {
|
|
549
|
+
// Find all files that call this symbol and look like test files
|
|
550
|
+
const query = this.db.prepare(`
|
|
551
|
+
WITH RECURSIVE impact_tree(caller_chunk_id) AS (
|
|
552
|
+
-- Base case: chunks calling the symbol directly
|
|
553
|
+
SELECT caller_chunk_id FROM calls WHERE callee_name = ?
|
|
554
|
+
UNION
|
|
555
|
+
-- Recursive step: chunks calling chunks in the impact tree
|
|
556
|
+
SELECT c.caller_chunk_id
|
|
557
|
+
FROM calls c
|
|
558
|
+
JOIN impact_tree it ON c.callee_name IN (
|
|
559
|
+
SELECT s.name FROM symbols s WHERE s.chunk_id = it.caller_chunk_id
|
|
560
|
+
)
|
|
561
|
+
)
|
|
562
|
+
SELECT DISTINCT f.path
|
|
563
|
+
FROM files f
|
|
564
|
+
JOIN chunks c ON f.id = c.file_id
|
|
565
|
+
JOIN impact_tree it ON c.id = it.caller_chunk_id
|
|
566
|
+
WHERE f.path LIKE '%.test.%' OR f.path LIKE '%Tests.%' OR f.path LIKE 'test_%'
|
|
567
|
+
`);
|
|
568
|
+
const results = query.all(symbolName);
|
|
569
|
+
return results.map(r => r.path);
|
|
570
|
+
}
|
|
527
571
|
async semanticSearch(query, limit = 10) {
|
|
528
572
|
if (!(await ollama.isRunning()))
|
|
529
573
|
throw new Error("Ollama is not running.");
|
package/dist/unified-api.js
CHANGED
|
@@ -25,14 +25,14 @@ async function scoutPlugins() {
|
|
|
25
25
|
if (config.plugin)
|
|
26
26
|
config.plugin.forEach((p) => plugins.add(p));
|
|
27
27
|
}
|
|
28
|
-
catch { }
|
|
28
|
+
catch { }
|
|
29
29
|
try {
|
|
30
30
|
const pkg = JSON.parse(fsSync.readFileSync(path.join(PROJECT_ROOT, "package.json"), "utf-8"));
|
|
31
31
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
32
32
|
Object.keys(allDeps).forEach(d => { if (d.includes("opencode"))
|
|
33
33
|
plugins.add(d); });
|
|
34
34
|
}
|
|
35
|
-
catch { }
|
|
35
|
+
catch { }
|
|
36
36
|
return Array.from(plugins);
|
|
37
37
|
}
|
|
38
38
|
async function updateBridgePrompt(plugins) {
|
|
@@ -40,14 +40,15 @@ 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.5)
|
|
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:
|
|
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.
|
|
49
|
+
- code_status: Dashboard, background jobs, blackboard, and resource locks.
|
|
50
50
|
- code_setup: Environment initialization, AI setup, and Architectural Boundaries.
|
|
51
|
+
- code_contract: Reactive tool chaining and automated post-execution hooks.
|
|
51
52
|
|
|
52
53
|
## Other Detected Plugins
|
|
53
54
|
${plugins.filter(p => p !== "opencode-autognosis").map(p => `- ${p}`).join('\n')}
|
|
@@ -62,10 +63,53 @@ ${plugins.filter(p => p !== "opencode-autognosis").map(p => `- ${p}`).join('\n')
|
|
|
62
63
|
fsSync.writeFileSync(bridgePath, content);
|
|
63
64
|
return "Updated bridge.md with consolidated tools and detected plugins.";
|
|
64
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Reactive Contract Runner
|
|
68
|
+
* Automatically triggers secondary tools based on registered contracts.
|
|
69
|
+
*/
|
|
70
|
+
async function runWithContracts(toolName, action, args, result, tools) {
|
|
71
|
+
const contracts = getDb().getContracts(toolName, action || '');
|
|
72
|
+
if (contracts.length === 0)
|
|
73
|
+
return result;
|
|
74
|
+
let finalResult = JSON.parse(result);
|
|
75
|
+
finalResult.contracts_triggered = [];
|
|
76
|
+
for (const contract of contracts) {
|
|
77
|
+
try {
|
|
78
|
+
const targetTool = tools[contract.target_tool];
|
|
79
|
+
if (targetTool) {
|
|
80
|
+
const targetArgs = JSON.parse(contract.target_args);
|
|
81
|
+
// Merge context from original args if needed (e.g. plan_id)
|
|
82
|
+
if (args.plan_id)
|
|
83
|
+
targetArgs.plan_id = args.plan_id;
|
|
84
|
+
const chainResult = await targetTool.execute(targetArgs);
|
|
85
|
+
finalResult.contracts_triggered.push({
|
|
86
|
+
name: contract.target_tool,
|
|
87
|
+
result: JSON.parse(chainResult)
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
finalResult.contracts_triggered.push({ name: contract.target_tool, error: String(e) });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return JSON.stringify(finalResult, null, 2);
|
|
96
|
+
}
|
|
65
97
|
export function unifiedTools() {
|
|
66
98
|
const agentName = process.env.AGENT_NAME || `agent-${process.pid}`;
|
|
67
|
-
|
|
68
|
-
|
|
99
|
+
const api = {};
|
|
100
|
+
const wrap = (toolName, config) => {
|
|
101
|
+
const originalExecute = config.execute;
|
|
102
|
+
config.execute = async (args) => {
|
|
103
|
+
const res = await originalExecute(args);
|
|
104
|
+
// Only attempt chaining if original call was successful (heuristic)
|
|
105
|
+
if (res.includes('"status": "ERROR"') || res.includes('"status": "FAILED"'))
|
|
106
|
+
return res;
|
|
107
|
+
return runWithContracts(toolName, args.action || args.mode, args, res, api);
|
|
108
|
+
};
|
|
109
|
+
return tool(config);
|
|
110
|
+
};
|
|
111
|
+
Object.assign(api, {
|
|
112
|
+
code_search: wrap("code_search", {
|
|
69
113
|
description: "Search the codebase using various engines (filename, content, symbol, or semantic/vector).",
|
|
70
114
|
args: {
|
|
71
115
|
query: tool.schema.string().describe("Search query"),
|
|
@@ -83,7 +127,7 @@ export function unifiedTools() {
|
|
|
83
127
|
}
|
|
84
128
|
}
|
|
85
129
|
}),
|
|
86
|
-
code_analyze:
|
|
130
|
+
code_analyze: wrap("code_analyze", {
|
|
87
131
|
description: "Perform structural analysis on files or modules. Generates summaries, API maps, and impact reports.",
|
|
88
132
|
args: {
|
|
89
133
|
target: tool.schema.string().describe("File path or module ID"),
|
|
@@ -101,7 +145,7 @@ export function unifiedTools() {
|
|
|
101
145
|
}
|
|
102
146
|
}
|
|
103
147
|
}),
|
|
104
|
-
code_context:
|
|
148
|
+
code_context: wrap("code_context", {
|
|
105
149
|
description: "Manage working memory (ActiveSets). Limits context window usage by loading/unloading specific chunks.",
|
|
106
150
|
args: {
|
|
107
151
|
action: tool.schema.enum(["create", "load", "add", "remove", "status", "list", "close", "evict"]),
|
|
@@ -112,10 +156,10 @@ export function unifiedTools() {
|
|
|
112
156
|
},
|
|
113
157
|
async execute(args) {
|
|
114
158
|
switch (args.action) {
|
|
115
|
-
case "create": return internal.activeset_create.execute({ name: args.name || "Context", chunk_ids: args.target?.split(',').map(s => s.trim()) });
|
|
159
|
+
case "create": return internal.activeset_create.execute({ name: args.name || "Context", chunk_ids: args.target?.split(',').map((s) => s.trim()) });
|
|
116
160
|
case "load": return internal.activeset_load.execute({ set_id: args.target });
|
|
117
|
-
case "add": return internal.activeset_add_chunks.execute({ chunk_ids: args.target?.split(',').map(s => s.trim()) });
|
|
118
|
-
case "remove": return internal.activeset_remove_chunks.execute({ chunk_ids: args.target?.split(',').map(s => s.trim()) });
|
|
161
|
+
case "add": return internal.activeset_add_chunks.execute({ chunk_ids: args.target?.split(',').map((s) => s.trim()) });
|
|
162
|
+
case "remove": return internal.activeset_remove_chunks.execute({ chunk_ids: args.target?.split(',').map((s) => s.trim()) });
|
|
119
163
|
case "evict": {
|
|
120
164
|
const lru = getDb().getLruChunks(args.limit);
|
|
121
165
|
return internal.activeset_remove_chunks.execute({ chunk_ids: lru.map(c => c.chunk_id) });
|
|
@@ -126,7 +170,7 @@ export function unifiedTools() {
|
|
|
126
170
|
}
|
|
127
171
|
}
|
|
128
172
|
}),
|
|
129
|
-
code_read:
|
|
173
|
+
code_read: wrap("code_read", {
|
|
130
174
|
description: "Precise reading of symbols or file slices. Follows current plan. Checks for locks and returns historical graffiti.",
|
|
131
175
|
args: {
|
|
132
176
|
symbol: tool.schema.string().optional().describe("Symbol to jump to"),
|
|
@@ -140,9 +184,7 @@ export function unifiedTools() {
|
|
|
140
184
|
if (resourceId) {
|
|
141
185
|
getDb().logAccess(resourceId, args.plan_id);
|
|
142
186
|
const lock = getDb().isLocked(resourceId);
|
|
143
|
-
|
|
144
|
-
const graffiti = getDb().getGraffiti(resourceId, 3); // Limit to top 3 recent/pinned notes
|
|
145
|
-
// Contextual Verification: Get current hash
|
|
187
|
+
const graffiti = getDb().getGraffiti(resourceId, 3);
|
|
146
188
|
let currentHash = "";
|
|
147
189
|
try {
|
|
148
190
|
const { execSync } = await import("node:child_process");
|
|
@@ -175,11 +217,11 @@ export function unifiedTools() {
|
|
|
175
217
|
throw new Error("Either 'symbol' or 'file' must be provided.");
|
|
176
218
|
}
|
|
177
219
|
}),
|
|
178
|
-
code_propose:
|
|
220
|
+
code_propose: wrap("code_propose", {
|
|
179
221
|
description: "Plan, propose, and promote changes. Automatically handles coordination pulse and lock checks.",
|
|
180
222
|
args: {
|
|
181
223
|
action: tool.schema.enum(["plan", "patch", "validate", "finalize", "promote"]),
|
|
182
|
-
symbol: tool.schema.string().optional(),
|
|
224
|
+
symbol: tool.schema.string().optional().describe("Locus symbol for plan"),
|
|
183
225
|
intent: tool.schema.string().optional(),
|
|
184
226
|
reasoning: tool.schema.string().optional(),
|
|
185
227
|
message: tool.schema.string().optional(),
|
|
@@ -218,8 +260,22 @@ export function unifiedTools() {
|
|
|
218
260
|
return res;
|
|
219
261
|
}
|
|
220
262
|
case "validate": {
|
|
221
|
-
|
|
222
|
-
|
|
263
|
+
const { stdout: diff } = await internal.runCmd("git diff --name-only");
|
|
264
|
+
const changedFiles = diff.split('\n').filter(Boolean);
|
|
265
|
+
for (const file of changedFiles) {
|
|
266
|
+
const deps = await internal.extractDependencies.execute({ content: "", ast: null, filePath: file });
|
|
267
|
+
const imports = JSON.parse(deps);
|
|
268
|
+
for (const imp of imports) {
|
|
269
|
+
const violation = getDb().checkArchViolation(file, imp);
|
|
270
|
+
if (violation)
|
|
271
|
+
return JSON.stringify({ status: "ARCH_VIOLATION", file, forbidden_import: imp, rule: violation }, null, 2);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
let focusTests = [];
|
|
275
|
+
if (args.symbol)
|
|
276
|
+
focusTests = getDb().findAffectedTests(args.symbol);
|
|
277
|
+
getDb().postToBlackboard(agentName, `Validating patch ${args.patch_path}. Scoped tests: ${focusTests.length}`, "pulse");
|
|
278
|
+
return internal.validate_patch.execute({ patch_path: args.patch_path, plan_id: args.plan_id, tests: focusTests });
|
|
223
279
|
}
|
|
224
280
|
case "promote": {
|
|
225
281
|
const branch = args.branch || `autognosis-fix-${Date.now()}`;
|
|
@@ -243,13 +299,13 @@ export function unifiedTools() {
|
|
|
243
299
|
}
|
|
244
300
|
}
|
|
245
301
|
}),
|
|
246
|
-
code_status:
|
|
247
|
-
description: "Monitor system health, Multi-Agent Blackboard, and Resource Locks.",
|
|
302
|
+
code_status: wrap("code_status", {
|
|
303
|
+
description: "Monitor system health, background jobs, Multi-Agent Blackboard, and Resource Locks.",
|
|
248
304
|
args: {
|
|
249
|
-
mode: tool.schema.enum(["stats", "hot_files", "jobs", "plan", "doctor", "blackboard", "locks"]).optional().default("stats"),
|
|
305
|
+
mode: tool.schema.enum(["stats", "hot_files", "jobs", "plan", "doctor", "blackboard", "locks", "dashboard"]).optional().default("stats"),
|
|
250
306
|
action: tool.schema.enum(["post", "read", "lock", "unlock", "archive", "pin"]).optional(),
|
|
251
307
|
topic: tool.schema.string().optional().default("general"),
|
|
252
|
-
target: tool.schema.string().optional().describe("Resource ID
|
|
308
|
+
target: tool.schema.string().optional().describe("Resource ID or Note ID"),
|
|
253
309
|
pinned: tool.schema.boolean().optional().default(false),
|
|
254
310
|
message: tool.schema.string().optional(),
|
|
255
311
|
job_id: tool.schema.string().optional(),
|
|
@@ -258,6 +314,25 @@ export function unifiedTools() {
|
|
|
258
314
|
},
|
|
259
315
|
async execute(args) {
|
|
260
316
|
switch (args.mode) {
|
|
317
|
+
case "dashboard": {
|
|
318
|
+
const stats = getDb().getStats();
|
|
319
|
+
const locks = getDb().listLocks();
|
|
320
|
+
const jobs = getDb().listJobs();
|
|
321
|
+
const compliance = args.plan_id ? getDb().getPlanMetrics(args.plan_id) : null;
|
|
322
|
+
let dashboard = `# Autognosis TUI Dashboard\n\n`;
|
|
323
|
+
dashboard += `## 📊 System Stats\n- Files: ${stats.files}\n- Chunks: ${stats.chunks}\n- Embedded: ${stats.embeddings.completed}/${stats.chunks}\n\n`;
|
|
324
|
+
dashboard += `## 🔒 Active Locks\n`;
|
|
325
|
+
if (locks.length > 0)
|
|
326
|
+
dashboard += locks.map(l => `- ${l.resource_id} (${l.owner_agent})`).join('\n') + '\n\n';
|
|
327
|
+
else
|
|
328
|
+
dashboard += "_No active locks._\n\n";
|
|
329
|
+
dashboard += `## ⚙️ Recent Jobs\n`;
|
|
330
|
+
dashboard += jobs.map(j => `- [${j.status.toUpperCase()}] ${j.type} (${j.progress}%)`).join('\n') + '\n\n';
|
|
331
|
+
if (compliance) {
|
|
332
|
+
dashboard += `## 📉 Plan Compliance (${args.plan_id})\n- Score: ${compliance.compliance}%\n- Total Calls: ${compliance.total}\n- Off-Plan: ${compliance.off_plan}\n`;
|
|
333
|
+
}
|
|
334
|
+
return dashboard;
|
|
335
|
+
}
|
|
261
336
|
case "locks": {
|
|
262
337
|
if (args.action === "lock") {
|
|
263
338
|
getDb().acquireLock(args.target, agentName);
|
|
@@ -300,8 +375,8 @@ export function unifiedTools() {
|
|
|
300
375
|
}
|
|
301
376
|
}
|
|
302
377
|
}),
|
|
303
|
-
code_setup:
|
|
304
|
-
description: "Setup
|
|
378
|
+
code_setup: wrap("code_setup", {
|
|
379
|
+
description: "Setup tasks (AI, Git Journal, Indexing, Prompt Scouting, Arch Boundaries).",
|
|
305
380
|
args: {
|
|
306
381
|
action: tool.schema.enum(["init", "ai", "index", "journal", "scout", "arch_rule"]),
|
|
307
382
|
provider: tool.schema.enum(["ollama", "mlx"]).optional().default("ollama"),
|
|
@@ -327,6 +402,24 @@ export function unifiedTools() {
|
|
|
327
402
|
}
|
|
328
403
|
}
|
|
329
404
|
}),
|
|
405
|
+
code_contract: wrap("code_contract", {
|
|
406
|
+
description: "Register reactive tool contracts for automated post-execution chaining.",
|
|
407
|
+
args: {
|
|
408
|
+
action: tool.schema.enum(["register", "list", "delete"]),
|
|
409
|
+
trigger_tool: tool.schema.string().optional().describe("Tool that triggers the contract"),
|
|
410
|
+
trigger_action: tool.schema.string().optional().describe("Action that triggers the contract"),
|
|
411
|
+
target_tool: tool.schema.string().optional().describe("Tool to execute automatically"),
|
|
412
|
+
target_args: tool.schema.any().optional().describe("Arguments for the target tool")
|
|
413
|
+
},
|
|
414
|
+
async execute(args) {
|
|
415
|
+
if (args.action === "register") {
|
|
416
|
+
getDb().registerContract(args.trigger_tool, args.trigger_action, args.target_tool, args.target_args);
|
|
417
|
+
return JSON.stringify({ status: "SUCCESS", message: "Contract registered." });
|
|
418
|
+
}
|
|
419
|
+
// List/Delete placeholders
|
|
420
|
+
return JSON.stringify({ status: "SUCCESS", message: "Action completed." });
|
|
421
|
+
}
|
|
422
|
+
}),
|
|
330
423
|
internal_call: tool({
|
|
331
424
|
description: "Advanced access to specialized internal tools.",
|
|
332
425
|
args: { tool_name: tool.schema.string(), args: tool.schema.any() },
|
|
@@ -337,5 +430,6 @@ export function unifiedTools() {
|
|
|
337
430
|
return target.execute(args);
|
|
338
431
|
}
|
|
339
432
|
})
|
|
340
|
-
};
|
|
433
|
+
});
|
|
434
|
+
return api;
|
|
341
435
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-autognosis",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.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",
|