opencode-autognosis 1.0.0 → 2.0.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.
@@ -0,0 +1,384 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { exec } from "node:child_process";
3
+ import * as fs from "node:fs/promises";
4
+ import * as fsSync from "node:fs";
5
+ import * as path from "node:path";
6
+ import { promisify } from "node:util";
7
+ import * as crypto from "node:crypto";
8
+ const execAsync = promisify(exec);
9
+ const PROJECT_ROOT = process.cwd();
10
+ const OPENCODE_DIR = path.join(PROJECT_ROOT, ".opencode");
11
+ const WORKTREE_DIR = path.join(OPENCODE_DIR, "worktrees");
12
+ // Internal logging
13
+ function log(message, data) {
14
+ console.error(`[GitWorktree] ${message}`, data || '');
15
+ }
16
+ // =============================================================================
17
+ // HELPERS
18
+ // =============================================================================
19
+ async function runCmd(cmd, cwd = PROJECT_ROOT, timeoutMs = 30000) {
20
+ try {
21
+ const { stdout, stderr } = await execAsync(cmd, {
22
+ cwd,
23
+ maxBuffer: 10 * 1024 * 1024,
24
+ timeout: timeoutMs
25
+ });
26
+ return { stdout: stdout.trim(), stderr: stderr.trim() };
27
+ }
28
+ catch (error) {
29
+ if (error.signal === 'SIGTERM' && error.code === undefined) {
30
+ return { stdout: "", stderr: `Command timed out after ${timeoutMs}ms`, error, timedOut: true };
31
+ }
32
+ return { stdout: "", stderr: error instanceof Error ? error.message : `${error}`, error };
33
+ }
34
+ }
35
+ async function ensureWorktreeDir() {
36
+ await fs.mkdir(WORKTREE_DIR, { recursive: true });
37
+ }
38
+ async function getGitState() {
39
+ const { stdout: head } = await runCmd("git rev-parse HEAD");
40
+ const { stdout: branch } = await runCmd("git rev-parse --abbrev-ref HEAD");
41
+ const { stdout: status } = await runCmd("git status --porcelain");
42
+ const { stdout: remotes } = await runCmd("git remote");
43
+ return {
44
+ head,
45
+ branch,
46
+ isDirty: status.length > 0,
47
+ statusLines: status.split('\n').filter(Boolean),
48
+ remotes: remotes.split('\n').filter(Boolean),
49
+ isMainBranch: branch === "main" || branch === "master"
50
+ };
51
+ }
52
+ async function discoverWorktrees() {
53
+ await ensureWorktreeDir();
54
+ try {
55
+ const worktrees = await fs.readdir(WORKTREE_DIR);
56
+ const results = [];
57
+ for (const worktree of worktrees) {
58
+ const worktreePath = path.join(WORKTREE_DIR, worktree);
59
+ const stat = await fs.stat(worktreePath);
60
+ if (stat.isDirectory()) {
61
+ const gitDir = path.join(worktreePath, ".git");
62
+ if (fsSync.existsSync(gitDir)) {
63
+ const { stdout: branch } = await runCmd("git rev-parse --abbrev-ref HEAD", worktreePath);
64
+ const { stdout: head } = await runCmd("git rev-parse HEAD", worktreePath);
65
+ const { stdout: status } = await runCmd("git status --porcelain", worktreePath);
66
+ results.push({
67
+ name: worktree,
68
+ path: worktreePath,
69
+ branch,
70
+ head,
71
+ isDirty: status.length > 0,
72
+ statusLines: status.split('\n').filter(Boolean)
73
+ });
74
+ }
75
+ }
76
+ }
77
+ return results;
78
+ }
79
+ catch (error) {
80
+ return [];
81
+ }
82
+ }
83
+ // =============================================================================
84
+ // AGENT CONTRACT INTEGRATION
85
+ // =============================================================================
86
+ const AGENT_USAGE_POLICY = `
87
+ AGENT USAGE POLICY:
88
+ - Use git.preflight before any repository operations
89
+ - Create checkpoints with git.checkpoint_create for clean session starts
90
+ - Work in integration branches, never directly on main/master
91
+ - Validate all patches before application
92
+ - Clean up worktrees after completion to avoid resource leaks
93
+ `.trim();
94
+ const WORKFLOW_PATTERNS = `
95
+ STANDARD WORKFLOW:
96
+ 1. git.preflight -> Validate repository state
97
+ 2. git.checkpoint_create -> Create clean starting point
98
+ 3. Work in feature/integration branch
99
+ 4. git.validate_patch -> Verify changes before commit
100
+ 5. Clean up worktrees and finalize
101
+ `.trim();
102
+ // =============================================================================
103
+ // GIT WORKTREE MANAGEMENT TOOLS
104
+ // =============================================================================
105
+ export function gitWorktreeTools() {
106
+ return {
107
+ git_preflight: tool({
108
+ description: `Validate repository state and check prerequisites for Git operations. ${AGENT_USAGE_POLICY} Returns comprehensive repository status including branch state, cleanliness, and available worktrees.`,
109
+ args: {
110
+ strict: tool.schema.boolean().optional().default(false).describe("Enable strict validation mode")
111
+ },
112
+ async execute({ strict }) {
113
+ log("Tool call: git_preflight", { strict });
114
+ try {
115
+ const state = await getGitState();
116
+ const worktrees = await discoverWorktrees();
117
+ const issues = [];
118
+ const warnings = [];
119
+ // Check for dirty working directory
120
+ if (state.isDirty) {
121
+ if (strict) {
122
+ issues.push("Working directory is dirty - commit or stash changes");
123
+ }
124
+ else {
125
+ warnings.push("Working directory is dirty");
126
+ }
127
+ }
128
+ // Check for main branch
129
+ if (state.isMainBranch && state.isDirty) {
130
+ issues.push("Never work directly on main/master branch with uncommitted changes");
131
+ }
132
+ // Check for worktree conflicts
133
+ const conflictingWorktrees = worktrees.filter(w => w.branch === state.branch);
134
+ if (conflictingWorktrees.length > 0) {
135
+ warnings.push(`Branch '${state.branch}' is checked out in ${conflictingWorktrees.length} worktree(s)`);
136
+ }
137
+ // Check for git availability
138
+ const { error: gitError } = await runCmd("git --version");
139
+ if (gitError) {
140
+ issues.push("Git is not available or not in PATH");
141
+ }
142
+ const result = {
143
+ status: issues.length > 0 ? "FAILED" : warnings.length > 0 ? "WARNING" : "OK",
144
+ repository: {
145
+ head: state.head,
146
+ branch: state.branch,
147
+ isDirty: state.isDirty,
148
+ isMainBranch: state.isMainBranch,
149
+ statusLines: state.statusLines,
150
+ remotes: state.remotes
151
+ },
152
+ worktrees: {
153
+ discovered: worktrees,
154
+ count: worktrees.length
155
+ },
156
+ validation: {
157
+ issues,
158
+ warnings,
159
+ strict
160
+ },
161
+ workflow: WORKFLOW_PATTERNS
162
+ };
163
+ return JSON.stringify(result, null, 2);
164
+ }
165
+ catch (error) {
166
+ return JSON.stringify({
167
+ status: "ERROR",
168
+ message: error instanceof Error ? error.message : `${error}`,
169
+ workflow: WORKFLOW_PATTERNS
170
+ }, null, 2);
171
+ }
172
+ }
173
+ }),
174
+ git_checkpoint_create: tool({
175
+ description: `Create a clean checkpoint for starting a new work session. ${AGENT_USAGE_POLICY} Creates a deterministic integration branch and optional worktree for isolated work.`,
176
+ args: {
177
+ branch_name: tool.schema.string().optional().describe("Custom branch name (auto-generated if not provided)"),
178
+ create_worktree: tool.schema.boolean().optional().default(true).describe("Create isolated worktree for the branch"),
179
+ message: tool.schema.string().optional().default("Agent checkpoint").describe("Checkpoint commit message")
180
+ },
181
+ async execute({ branch_name, create_worktree, message }) {
182
+ log("Tool call: git_checkpoint_create", { branch_name, create_worktree, message });
183
+ try {
184
+ const state = await getGitState();
185
+ // Generate branch name if not provided
186
+ const targetBranch = branch_name || `agent-integration-${Date.now()}`;
187
+ // Ensure we're on a clean state
188
+ if (state.isDirty) {
189
+ return JSON.stringify({
190
+ status: "ERROR",
191
+ message: "Cannot create checkpoint from dirty working directory. Commit or stash changes first.",
192
+ current_state: state
193
+ }, null, 2);
194
+ }
195
+ // Create and checkout new branch
196
+ const { error: branchError } = await runCmd(`git checkout -b ${targetBranch}`);
197
+ if (branchError) {
198
+ return JSON.stringify({
199
+ status: "ERROR",
200
+ message: `Failed to create branch: ${branchError instanceof Error ? branchError.message : `${branchError}`}`,
201
+ current_state: state
202
+ }, null, 2);
203
+ }
204
+ // Create initial commit if needed
205
+ const { stdout: log } = await runCmd("git log --oneline -1");
206
+ if (!log || log.includes("Initial commit")) {
207
+ await runCmd(`git commit --allow-empty -m "${message}"`);
208
+ }
209
+ let worktreePath = null;
210
+ if (create_worktree) {
211
+ await ensureWorktreeDir();
212
+ worktreePath = path.join(WORKTREE_DIR, targetBranch.replace(/[^a-zA-Z0-9-_]/g, '-'));
213
+ // Remove existing worktree if it exists
214
+ if (fsSync.existsSync(worktreePath)) {
215
+ const { error: removeError } = await runCmd(`git worktree remove ${worktreePath}`);
216
+ if (removeError) {
217
+ console.error("[GitWorktree] Warning: Failed to remove existing worktree", removeError);
218
+ }
219
+ }
220
+ // Create new worktree
221
+ const { error: worktreeError } = await runCmd(`git worktree add ${worktreePath} ${targetBranch}`);
222
+ if (worktreeError) {
223
+ return JSON.stringify({
224
+ status: "WARNING",
225
+ message: `Branch created but worktree failed: ${worktreeError instanceof Error ? worktreeError.message : `${worktreeError}`}`,
226
+ checkpoint: {
227
+ branch: targetBranch,
228
+ head: state.head,
229
+ created_at: new Date().toISOString()
230
+ }
231
+ }, null, 2);
232
+ }
233
+ }
234
+ const result = {
235
+ status: "SUCCESS",
236
+ checkpoint: {
237
+ branch: targetBranch,
238
+ head: state.head,
239
+ created_at: new Date().toISOString(),
240
+ message
241
+ },
242
+ worktree: worktreePath ? {
243
+ path: worktreePath,
244
+ branch: targetBranch
245
+ } : null,
246
+ next_steps: [
247
+ `Work in branch: ${targetBranch}`,
248
+ worktreePath ? `Use worktree: ${worktreePath}` : "Work in main repository",
249
+ "Validate patches before applying",
250
+ "Clean up worktree when done"
251
+ ]
252
+ };
253
+ return JSON.stringify(result, null, 2);
254
+ }
255
+ catch (error) {
256
+ return JSON.stringify({
257
+ status: "ERROR",
258
+ message: error instanceof Error ? error.message : `${error}`
259
+ }, null, 2);
260
+ }
261
+ }
262
+ }),
263
+ git_worktree_status: tool({
264
+ description: "Discover and report status of all worktrees. Returns detailed information about active worktrees, their branches, and cleanliness.",
265
+ args: {
266
+ include_clean: tool.schema.boolean().optional().default(false).describe("Include clean worktrees in results")
267
+ },
268
+ async execute({ include_clean }) {
269
+ log("Tool call: git_worktree_status", { include_clean });
270
+ try {
271
+ const worktrees = await discoverWorktrees();
272
+ const state = await getGitState();
273
+ let filteredWorktrees = worktrees;
274
+ if (!include_clean) {
275
+ filteredWorktrees = worktrees.filter(w => w.isDirty || w.branch === state.branch);
276
+ }
277
+ const result = {
278
+ status: "OK",
279
+ current_repository: {
280
+ branch: state.branch,
281
+ head: state.head,
282
+ isDirty: state.isDirty
283
+ },
284
+ worktrees: {
285
+ total: worktrees.length,
286
+ filtered: filteredWorktrees.length,
287
+ active: filteredWorktrees.map(w => ({
288
+ name: w.name,
289
+ path: w.path,
290
+ branch: w.branch,
291
+ head: w.head,
292
+ isDirty: w.isDirty,
293
+ status_summary: `${w.isDirty ? 'DIRTY' : 'CLEAN'} - ${w.branch}`
294
+ }))
295
+ },
296
+ recommendations: [
297
+ "Clean up unused worktrees to free resources",
298
+ "Avoid multiple worktrees on the same branch",
299
+ "Use git.checkpoint_create for new clean workspaces"
300
+ ]
301
+ };
302
+ return JSON.stringify(result, null, 2);
303
+ }
304
+ catch (error) {
305
+ return JSON.stringify({
306
+ status: "ERROR",
307
+ message: error instanceof Error ? error.message : `${error}`
308
+ }, null, 2);
309
+ }
310
+ }
311
+ }),
312
+ git_worktree_cleanup: tool({
313
+ description: `Clean up worktrees to free resources. ${AGENT_USAGE_POLICY} Removes specified worktrees after ensuring no uncommitted changes will be lost.`,
314
+ args: {
315
+ worktree_names: tool.schema.array(tool.schema.string()).optional().describe("Specific worktree names to clean up (cleans all if not specified)"),
316
+ force: tool.schema.boolean().optional().default(false).describe("Force cleanup even with uncommitted changes (not recommended)")
317
+ },
318
+ async execute({ worktree_names, force }) {
319
+ log("Tool call: git_worktree_cleanup", { worktree_names, force });
320
+ try {
321
+ const worktrees = await discoverWorktrees();
322
+ let targetWorktrees = worktrees;
323
+ if (worktree_names && worktree_names.length > 0) {
324
+ targetWorktrees = worktrees.filter(w => worktree_names.includes(w.name));
325
+ if (targetWorktrees.length === 0) {
326
+ return JSON.stringify({
327
+ status: "WARNING",
328
+ message: "No matching worktrees found",
329
+ requested: worktree_names,
330
+ available: worktrees.map(w => w.name)
331
+ }, null, 2);
332
+ }
333
+ }
334
+ const cleanupResults = [];
335
+ const warnings = [];
336
+ for (const worktree of targetWorktrees) {
337
+ if (worktree.isDirty && !force) {
338
+ warnings.push(`Skipping dirty worktree: ${worktree.name} (${worktree.branch})`);
339
+ continue;
340
+ }
341
+ try {
342
+ // Remove worktree
343
+ const { error: removeError } = await runCmd(`git worktree remove ${worktree.path}`);
344
+ if (removeError) {
345
+ warnings.push(`Failed to remove ${worktree.name}: ${removeError instanceof Error ? removeError.message : `${removeError}`}`);
346
+ continue;
347
+ }
348
+ // Remove directory if it still exists
349
+ if (fsSync.existsSync(worktree.path)) {
350
+ await fs.rm(worktree.path, { recursive: true, force: true });
351
+ }
352
+ cleanupResults.push({
353
+ name: worktree.name,
354
+ path: worktree.path,
355
+ branch: worktree.branch,
356
+ status: "REMOVED"
357
+ });
358
+ }
359
+ catch (error) {
360
+ warnings.push(`Error cleaning up ${worktree.name}: ${error instanceof Error ? error.message : `${error}`}`);
361
+ }
362
+ }
363
+ const result = {
364
+ status: warnings.length > 0 ? "PARTIAL" : "SUCCESS",
365
+ cleaned_up: cleanupResults,
366
+ warnings,
367
+ summary: {
368
+ processed: targetWorktrees.length,
369
+ removed: cleanupResults.length,
370
+ warnings: warnings.length
371
+ }
372
+ };
373
+ return JSON.stringify(result, null, 2);
374
+ }
375
+ catch (error) {
376
+ return JSON.stringify({
377
+ status: "ERROR",
378
+ message: error instanceof Error ? error.message : `${error}`
379
+ }, null, 2);
380
+ }
381
+ }
382
+ })
383
+ };
384
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
- import { type Plugin } from "@opencode-ai/plugin";
2
- export declare const AutognosisPlugin: Plugin;
3
- export default AutognosisPlugin;
1
+ export default function plugin(): {
2
+ tools: {
3
+ [key: string]: any;
4
+ };
5
+ };
package/dist/index.js CHANGED
@@ -1,235 +1,20 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- import { exec } from "node:child_process";
3
- import * as fs from "node:fs/promises";
4
- import * as fsSync from "node:fs";
5
- import * as path from "node:path";
6
- import { promisify } from "node:util";
7
- import * as crypto from "node:crypto";
8
- const execAsync = promisify(exec);
9
- const PROJECT_ROOT = process.cwd();
10
- const OPENCODE_DIR = path.join(PROJECT_ROOT, ".opencode");
11
- const CACHE_DIR = path.join(OPENCODE_DIR, "cache");
12
- async function runCmd(cmd, cwd = PROJECT_ROOT, timeoutMs = 30000) {
13
- try {
14
- const { stdout, stderr } = await execAsync(cmd, {
15
- cwd,
16
- maxBuffer: 10 * 1024 * 1024,
17
- timeout: timeoutMs
18
- });
19
- return { stdout: stdout.trim(), stderr: stderr.trim() };
20
- }
21
- catch (error) {
22
- if (error.signal === 'SIGTERM' && error.code === undefined) {
23
- return { stdout: "", stderr: `Command timed out after ${timeoutMs}ms`, error, timedOut: true };
24
- }
25
- return { stdout: "", stderr: error.message, error };
26
- }
27
- }
28
- async function checkBinary(bin) {
29
- const { error } = await runCmd(`${bin} --version`, PROJECT_ROOT, 5000);
30
- return !error;
31
- }
32
- async function ensureCache() {
33
- await fs.mkdir(CACHE_DIR, { recursive: true });
34
- }
35
- async function cleanCache() {
36
- try {
37
- const files = await fs.readdir(CACHE_DIR);
38
- const now = Date.now();
39
- const MAX_AGE = 7 * 24 * 60 * 60 * 1000;
40
- let deleted = 0;
41
- for (const file of files) {
42
- const filePath = path.join(CACHE_DIR, file);
43
- const stats = await fs.stat(filePath);
44
- if (now - stats.mtimeMs > MAX_AGE) {
45
- await fs.unlink(filePath);
46
- deleted++;
47
- }
48
- }
49
- return deleted;
50
- }
51
- catch (e) {
52
- return 0;
53
- }
54
- }
55
- async function maintainSymbolIndex() {
56
- await ensureCache();
57
- if (!(await checkBinary("ctags"))) {
58
- return { rebuilt: false, status: "unavailable", reason: "ctags binary missing" };
59
- }
60
- const tagsFile = path.join(CACHE_DIR, "tags");
61
- const fingerprintFile = path.join(CACHE_DIR, "tags.fingerprint");
62
- const { stdout: head } = await runCmd("git rev-parse HEAD");
63
- const { stdout: status } = await runCmd("git status --porcelain");
64
- const currentFingerprint = `${head}\n${status}`;
65
- let storedFingerprint = "";
66
- try {
67
- storedFingerprint = await fs.readFile(fingerprintFile, "utf-8");
68
- }
69
- catch (e) { }
70
- if (currentFingerprint !== storedFingerprint || !fsSync.existsSync(tagsFile)) {
71
- const { error, stderr } = await runCmd(`ctags -R -f ${tagsFile} --languages=TypeScript,JavaScript,Python,Go,Rust,C++,C .`, PROJECT_ROOT);
72
- if (error) {
73
- return { rebuilt: false, status: "failed", reason: stderr };
74
- }
75
- await fs.writeFile(fingerprintFile, currentFingerprint);
76
- return { rebuilt: true, status: "ok" };
77
- }
78
- return { rebuilt: false, status: "ok" };
79
- }
80
- export const AutognosisPlugin = async (_ctx) => {
81
- let pendingInitToken = null;
1
+ import { systemTools } from "./system-tools.js";
2
+ import { gitWorktreeTools } from "./git-worktree.js";
3
+ import { testingTools } from "./testing-infrastructure.js";
4
+ import { chunkCardsTools } from "./chunk-cards.js";
5
+ import { activeSetTools } from "./activeset.js";
6
+ import { moduleSummariesTools } from "./module-summaries.js";
7
+ import { performanceTools } from "./performance-optimization.js";
8
+ export default function plugin() {
82
9
  return {
83
- tool: {
84
- autognosis_init: tool({
85
- description: "Initialize the Autognosis environment.",
86
- args: {
87
- mode: tool.schema.enum(["plan", "apply"]).optional().default("plan"),
88
- token: tool.schema.string().optional()
89
- },
90
- async execute({ mode, token }) {
91
- if (mode === "plan") {
92
- const checks = { rg: await checkBinary("rg"), fd: await checkBinary("fd"), sg: await checkBinary("sg"), ctags: await checkBinary("ctags"), git: await checkBinary("git") };
93
- const actions = [];
94
- if (!fsSync.existsSync(CACHE_DIR))
95
- actions.push(`Create cache directory: ${CACHE_DIR}`);
96
- pendingInitToken = crypto.randomBytes(4).toString("hex");
97
- return JSON.stringify({
98
- status: "PLAN_READY",
99
- system_checks: checks,
100
- planned_actions: actions,
101
- confirm_token: pendingInitToken,
102
- instruction: "Call autognosis_init with mode='apply' and the confirm_token."
103
- }, null, 2);
104
- }
105
- else {
106
- if (!pendingInitToken || token !== pendingInitToken) {
107
- return JSON.stringify({ status: "ERROR", message: "Invalid token." });
108
- }
109
- await ensureCache();
110
- pendingInitToken = null;
111
- return JSON.stringify({ status: "SUCCESS", message: "Autognosis initialized." });
112
- }
113
- }
114
- }),
115
- fast_search: tool({
116
- description: "Fast search using rg and fd.",
117
- args: {
118
- query: tool.schema.string(),
119
- mode: tool.schema.enum(["filename", "content"]).optional().default("filename"),
120
- path: tool.schema.string().optional().default(".")
121
- },
122
- async execute({ query, mode, path: searchPath }) {
123
- if (mode === "content") {
124
- const { stdout } = await runCmd(`rg -n --column "${query}" "${searchPath}"`);
125
- return stdout.split('\n').slice(0, 50).join('\n') || "No matches.";
126
- }
127
- else {
128
- const { stdout } = await runCmd(`fd "${query}" "${searchPath}"`);
129
- return stdout.split('\n').slice(0, 50).join('\n') || "No files.";
130
- }
131
- }
132
- }),
133
- read_slice: tool({
134
- description: "Read a specific slice of a file.",
135
- args: {
136
- file: tool.schema.string(),
137
- start_line: tool.schema.number(),
138
- end_line: tool.schema.number()
139
- },
140
- async execute({ file, start_line, end_line }) {
141
- const { stdout, stderr } = await runCmd(`sed -n '${start_line},${end_line}p;${end_line + 1}q' "${file}"`);
142
- if (stderr)
143
- return `Error: ${stderr}`;
144
- return JSON.stringify({ file, start_line, end_line, content: stdout }, null, 2);
145
- }
146
- }),
147
- symbol_query: tool({
148
- description: "Query the symbol index.",
149
- args: {
150
- symbol: tool.schema.string()
151
- },
152
- async execute({ symbol }) {
153
- const maint = await maintainSymbolIndex();
154
- if (maint.status === "unavailable")
155
- return JSON.stringify({ error: maint.reason });
156
- const tagsFile = path.join(CACHE_DIR, "tags");
157
- const { stdout: grepOut } = await runCmd(`grep -P "^${symbol}\t" "${tagsFile}"`);
158
- return JSON.stringify({ matches: grepOut.split('\n').filter(Boolean), metadata: maint }, null, 2);
159
- }
160
- }),
161
- jump_to_symbol: tool({
162
- description: "Jump to a symbol definition.",
163
- args: {
164
- symbol: tool.schema.string()
165
- },
166
- async execute({ symbol }) {
167
- const maint = await maintainSymbolIndex();
168
- if (maint.status !== "ok")
169
- return JSON.stringify({ error: maint.reason });
170
- const tagsFile = path.join(CACHE_DIR, "tags");
171
- const { stdout: tagLine } = await runCmd(`grep -P "^${symbol}\t" "${tagsFile}" | head -n 1`);
172
- if (!tagLine)
173
- return JSON.stringify({ found: false, symbol });
174
- const file = tagLine.split('\t')[1];
175
- const { stdout: grepLine } = await runCmd(`grep -n "${symbol}" "${file}" | head -n 1`);
176
- const line = grepLine ? parseInt(grepLine.split(':')[0], 10) : 1;
177
- const start = Math.max(1, line - 5);
178
- const end = line + 15;
179
- const { stdout: slice } = await runCmd(`sed -n '${start},${end}p;${end + 1}q' "${file}"`);
180
- return JSON.stringify({ symbol, resolved_location: { file, line }, slice: { start, end, content: slice } }, null, 2);
181
- }
182
- }),
183
- brief_fix_loop: tool({
184
- description: "Action Planner.",
185
- args: {
186
- symbol: tool.schema.string(),
187
- intent: tool.schema.string()
188
- },
189
- async execute({ symbol, intent }) {
190
- return JSON.stringify({ plan_id: `plan-${Date.now()}`, symbol, intent }, null, 2);
191
- }
192
- }),
193
- prepare_patch: tool({
194
- description: "Generate a .diff artifact.",
195
- args: {
196
- message: tool.schema.string()
197
- },
198
- async execute({ message }) {
199
- await ensureCache();
200
- const patchPath = path.join(CACHE_DIR, `patch-${Date.now()}.diff`);
201
- const { stdout } = await runCmd("git diff");
202
- if (!stdout)
203
- return "No changes.";
204
- await fs.writeFile(patchPath, `// MSG: ${message}\n\n${stdout}`);
205
- return `Patch saved to ${patchPath}`;
206
- }
207
- }),
208
- validate_patch: tool({
209
- description: "Validate a patch.",
210
- args: {
211
- patch_path: tool.schema.string()
212
- },
213
- async execute({ patch_path }) {
214
- const { error } = await runCmd(`git apply --check "${patch_path}"`);
215
- return error ? `FAILED: ${error.message}` : "SUCCESS.";
216
- }
217
- }),
218
- finalize_plan: tool({
219
- description: "Finalize a plan.",
220
- args: {
221
- plan_id: tool.schema.string(),
222
- outcome: tool.schema.string()
223
- },
224
- async execute({ plan_id, outcome }) {
225
- await ensureCache();
226
- const report = { plan_id, outcome, time: new Date().toISOString() };
227
- await fs.appendFile(path.join(CACHE_DIR, "gaps.jsonl"), JSON.stringify(report) + "\n");
228
- const deleted = await cleanCache();
229
- return `Finalized. Deleted ${deleted} items.`;
230
- }
231
- })
232
- }
10
+ tools: {
11
+ ...systemTools(),
12
+ ...gitWorktreeTools(),
13
+ ...testingTools(),
14
+ ...chunkCardsTools(),
15
+ ...activeSetTools(),
16
+ ...moduleSummariesTools(),
17
+ ...performanceTools(),
18
+ },
233
19
  };
234
- };
235
- export default AutognosisPlugin;
20
+ }
@@ -0,0 +1,3 @@
1
+ export declare function moduleSummariesTools(): {
2
+ [key: string]: any;
3
+ };