ak-gemini 2.1.3 → 2.1.5

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/code-agent.js CHANGED
@@ -8,7 +8,7 @@ import BaseGemini from './base.js';
8
8
  import log from './logger.js';
9
9
  import { execFile } from 'node:child_process';
10
10
  import { writeFile, unlink, readdir, readFile, mkdir } from 'node:fs/promises';
11
- import { join, sep, basename } from 'node:path';
11
+ import { join, sep, basename, isAbsolute } from 'node:path';
12
12
  import { randomUUID } from 'node:crypto';
13
13
 
14
14
  /**
@@ -49,6 +49,17 @@ class CodeAgent extends BaseGemini {
49
49
  this.skills = options.skills || [];
50
50
  this.envOverview = options.envOverview || '';
51
51
 
52
+ // ── Custom tools ──
53
+ this.customTools = (options.tools || []).map(t => ({
54
+ name: t.name,
55
+ description: t.description,
56
+ parametersJsonSchema: t.parametersJsonSchema || t.parameters || t.input_schema || t.inputSchema
57
+ }));
58
+ this.toolExecutor = options.toolExecutor || null;
59
+ if (this.customTools.length > 0 && !this.toolExecutor) {
60
+ throw new Error('CodeAgent: tools provided without a toolExecutor.');
61
+ }
62
+
52
63
  // ── Internal state ──
53
64
  this._codebaseContext = null;
54
65
  this._contextGathered = false;
@@ -156,6 +167,11 @@ class CodeAgent extends BaseGemini {
156
167
  });
157
168
  }
158
169
 
170
+ // Append custom tools
171
+ for (const t of this.customTools) {
172
+ declarations.push({ name: t.name, description: t.description, parametersJsonSchema: t.parametersJsonSchema });
173
+ }
174
+
159
175
  return { functionDeclarations: declarations };
160
176
  }
161
177
 
@@ -252,7 +268,7 @@ class CodeAgent extends BaseGemini {
252
268
  continue;
253
269
  }
254
270
  try {
255
- const fullPath = join(this.workingDirectory, resolved);
271
+ const fullPath = isAbsolute(resolved) ? resolved : join(this.workingDirectory, resolved);
256
272
  const content = await readFile(fullPath, 'utf-8');
257
273
  importantFileContents.push({ path: resolved, content });
258
274
  } catch (e) {
@@ -269,6 +285,8 @@ class CodeAgent extends BaseGemini {
269
285
  * @private
270
286
  */
271
287
  _resolveImportantFile(filename, fileTreeLines) {
288
+ if (isAbsolute(filename)) return filename;
289
+
272
290
  const exact = fileTreeLines.find(line => line === filename);
273
291
  if (exact) return exact;
274
292
 
@@ -651,12 +669,30 @@ These rules apply when using execute_code, write_and_run_code, or fix_code (with
651
669
  data: { tool: 'use_skill', skillName: skill.name, content: skill.content, found: true }
652
670
  };
653
671
  }
654
- default:
672
+ default: {
673
+ if (this.toolExecutor) {
674
+ try {
675
+ const result = await this.toolExecutor(name, input);
676
+ const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
677
+ return {
678
+ output: resultStr,
679
+ type: 'tool',
680
+ data: { tool: name, args: input, result }
681
+ };
682
+ } catch (err) {
683
+ return {
684
+ output: `Tool "${name}" failed: ${err.message}`,
685
+ type: 'tool',
686
+ data: { tool: name, args: input, error: err.message }
687
+ };
688
+ }
689
+ }
655
690
  return {
656
691
  output: `Unknown tool: ${name}`,
657
692
  type: 'unknown',
658
693
  data: { tool: name }
659
694
  };
695
+ }
660
696
  }
661
697
  }
662
698
 
@@ -859,6 +895,11 @@ These rules apply when using execute_code, write_and_run_code, or fix_code (with
859
895
  yield { type: 'skill', skillName: data.skillName, content: data.content, found: data.found };
860
896
  }
861
897
 
898
+ // Emit custom tool event
899
+ if (type === 'tool') {
900
+ yield { type: 'tool', toolName, args: data.args, result: data.result, error: data.error };
901
+ }
902
+
862
903
  // Track consecutive failures
863
904
  const isExecutingTool = EXECUTING_TOOLS.has(toolName) || (toolName === 'fix_code' && toolInput.execute);
864
905
  if (isExecutingTool) {
package/index.cjs CHANGED
@@ -1649,6 +1649,15 @@ var CodeAgent = class extends base_default {
1649
1649
  this.maxRetries = options.maxRetries ?? 3;
1650
1650
  this.skills = options.skills || [];
1651
1651
  this.envOverview = options.envOverview || "";
1652
+ this.customTools = (options.tools || []).map((t) => ({
1653
+ name: t.name,
1654
+ description: t.description,
1655
+ parametersJsonSchema: t.parametersJsonSchema || t.parameters || t.input_schema || t.inputSchema
1656
+ }));
1657
+ this.toolExecutor = options.toolExecutor || null;
1658
+ if (this.customTools.length > 0 && !this.toolExecutor) {
1659
+ throw new Error("CodeAgent: tools provided without a toolExecutor.");
1660
+ }
1652
1661
  this._codebaseContext = null;
1653
1662
  this._contextGathered = false;
1654
1663
  this._stopped = false;
@@ -1746,6 +1755,9 @@ var CodeAgent = class extends base_default {
1746
1755
  }
1747
1756
  });
1748
1757
  }
1758
+ for (const t of this.customTools) {
1759
+ declarations.push({ name: t.name, description: t.description, parametersJsonSchema: t.parametersJsonSchema });
1760
+ }
1749
1761
  return { functionDeclarations: declarations };
1750
1762
  }
1751
1763
  // ── Init ─────────────────────────────────────────────────────────────────
@@ -1823,7 +1835,7 @@ var CodeAgent = class extends base_default {
1823
1835
  continue;
1824
1836
  }
1825
1837
  try {
1826
- const fullPath = (0, import_node_path.join)(this.workingDirectory, resolved);
1838
+ const fullPath = (0, import_node_path.isAbsolute)(resolved) ? resolved : (0, import_node_path.join)(this.workingDirectory, resolved);
1827
1839
  const content = await (0, import_promises2.readFile)(fullPath, "utf-8");
1828
1840
  importantFileContents.push({ path: resolved, content });
1829
1841
  } catch (e) {
@@ -1838,6 +1850,7 @@ var CodeAgent = class extends base_default {
1838
1850
  * @private
1839
1851
  */
1840
1852
  _resolveImportantFile(filename, fileTreeLines) {
1853
+ if ((0, import_node_path.isAbsolute)(filename)) return filename;
1841
1854
  const exact = fileTreeLines.find((line) => line === filename);
1842
1855
  if (exact) return exact;
1843
1856
  const partial = fileTreeLines.find(
@@ -2234,12 +2247,30 @@ ${this.envOverview}`;
2234
2247
  data: { tool: "use_skill", skillName: skill.name, content: skill.content, found: true }
2235
2248
  };
2236
2249
  }
2237
- default:
2250
+ default: {
2251
+ if (this.toolExecutor) {
2252
+ try {
2253
+ const result = await this.toolExecutor(name, input);
2254
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result);
2255
+ return {
2256
+ output: resultStr,
2257
+ type: "tool",
2258
+ data: { tool: name, args: input, result }
2259
+ };
2260
+ } catch (err) {
2261
+ return {
2262
+ output: `Tool "${name}" failed: ${err.message}`,
2263
+ type: "tool",
2264
+ data: { tool: name, args: input, error: err.message }
2265
+ };
2266
+ }
2267
+ }
2238
2268
  return {
2239
2269
  output: `Unknown tool: ${name}`,
2240
2270
  type: "unknown",
2241
2271
  data: { tool: name }
2242
2272
  };
2273
+ }
2243
2274
  }
2244
2275
  }
2245
2276
  // ── Non-Streaming Chat ───────────────────────────────────────────────────
@@ -2396,6 +2427,9 @@ ${this.envOverview}`;
2396
2427
  if (toolName === "use_skill") {
2397
2428
  yield { type: "skill", skillName: data.skillName, content: data.content, found: data.found };
2398
2429
  }
2430
+ if (type === "tool") {
2431
+ yield { type: "tool", toolName, args: data.args, result: data.result, error: data.error };
2432
+ }
2399
2433
  const isExecutingTool = EXECUTING_TOOLS.has(toolName) || toolName === "fix_code" && toolInput.execute;
2400
2434
  if (isExecutingTool) {
2401
2435
  if (data.exitCode !== 0 && !data.denied) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "ak-gemini",
3
3
  "author": "ak@mixpanel.com",
4
4
  "description": "AK's Generative AI Helper for doing... everything",
5
- "version": "2.1.3",
5
+ "version": "2.1.5",
6
6
  "main": "index.js",
7
7
  "files": [
8
8
  "index.js",
package/types.d.ts CHANGED
@@ -324,6 +324,17 @@ export interface CodeAgentOptions extends BaseGeminiOptions {
324
324
  skills?: string[];
325
325
  /** Plain text environment overview appended to the system prompt — describe the project, stack, conventions, etc. */
326
326
  envOverview?: string;
327
+ /** Custom tool declarations to add alongside built-in CodeAgent tools. Accepts Gemini, Claude, or OpenAI tool formats (auto-mapped). */
328
+ tools?: Array<{
329
+ name: string;
330
+ description: string;
331
+ parametersJsonSchema?: any;
332
+ parameters?: any;
333
+ input_schema?: any;
334
+ inputSchema?: any;
335
+ }>;
336
+ /** Function to execute custom tool calls: (toolName, args) => result */
337
+ toolExecutor?: (toolName: string, args: Record<string, any>) => Promise<any>;
327
338
  }
328
339
 
329
340
  export interface CodeExecution {
@@ -340,7 +351,7 @@ export interface CodeExecution {
340
351
  }
341
352
 
342
353
  export interface ToolCallResult {
343
- tool: 'write_code' | 'execute_code' | 'write_and_run_code' | 'fix_code' | 'run_bash' | 'use_skill';
354
+ tool: 'write_code' | 'execute_code' | 'write_and_run_code' | 'fix_code' | 'run_bash' | 'use_skill' | string;
344
355
  code?: string;
345
356
  purpose?: string;
346
357
  language?: string;
@@ -370,7 +381,7 @@ export interface CodeAgentResponse {
370
381
  }
371
382
 
372
383
  export interface CodeAgentStreamEvent {
373
- type: 'text' | 'code' | 'output' | 'write' | 'fix' | 'bash' | 'skill' | 'done';
384
+ type: 'text' | 'code' | 'output' | 'write' | 'fix' | 'bash' | 'skill' | 'tool' | 'done';
374
385
  text?: string;
375
386
  code?: string;
376
387
  stdout?: string;
@@ -390,6 +401,14 @@ export interface CodeAgentStreamEvent {
390
401
  skillName?: string;
391
402
  content?: string;
392
403
  found?: boolean;
404
+ /** custom tool: tool name */
405
+ toolName?: string;
406
+ /** custom tool: arguments passed */
407
+ args?: Record<string, any>;
408
+ /** custom tool: result returned */
409
+ result?: any;
410
+ /** custom tool: error message (if failed) */
411
+ error?: string;
393
412
  }
394
413
 
395
414
  // ── Per-Message Options ──────────────────────────────────────────────────────
@@ -625,6 +644,8 @@ export declare class CodeAgent extends BaseGemini {
625
644
  maxRetries: number;
626
645
  skills: string[];
627
646
  envOverview: string;
647
+ customTools: Array<{ name: string; description: string; parametersJsonSchema: any }>;
648
+ toolExecutor: ((toolName: string, args: Record<string, any>) => Promise<any>) | null;
628
649
 
629
650
  init(force?: boolean): Promise<void>;
630
651
  chat(message: string, opts?: { labels?: Record<string, string> }): Promise<CodeAgentResponse>;