workermill 0.1.9 → 0.2.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.
@@ -1,9 +1,16 @@
1
+ // node_modules/tsup/assets/esm_shims.js
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ var getFilename = () => fileURLToPath(import.meta.url);
5
+ var getDirname = () => path.dirname(getFilename());
6
+ var __dirname = /* @__PURE__ */ getDirname();
7
+
1
8
  // src/config.js
2
9
  import fs from "fs";
3
- import path from "path";
10
+ import path2 from "path";
4
11
  import os from "os";
5
- var CONFIG_DIR = path.join(os.homedir(), ".workermill");
6
- var CONFIG_FILE = path.join(CONFIG_DIR, "cli.json");
12
+ var CONFIG_DIR = path2.join(os.homedir(), ".workermill");
13
+ var CONFIG_FILE = path2.join(CONFIG_DIR, "cli.json");
7
14
  function loadConfig() {
8
15
  try {
9
16
  if (!fs.existsSync(CONFIG_FILE))
@@ -30,7 +37,8 @@ function getProviderForPersona(config, persona) {
30
37
  provider: providerName,
31
38
  model: providerConfig.model,
32
39
  apiKey: providerConfig.apiKey?.startsWith("{env:") ? process.env[providerConfig.apiKey.slice(5, -1)] || void 0 : providerConfig.apiKey,
33
- host: providerConfig.host
40
+ host: providerConfig.host,
41
+ contextLength: providerConfig.contextLength
34
42
  };
35
43
  }
36
44
 
@@ -39,7 +47,18 @@ import { anthropic } from "@ai-sdk/anthropic";
39
47
  import { openai } from "@ai-sdk/openai";
40
48
  import { google } from "@ai-sdk/google";
41
49
  import { createOllama } from "ollama-ai-provider-v2";
42
- function createModel(provider, modelName, ollamaHost) {
50
+ function buildOllamaOptions(provider, contextLength) {
51
+ if (provider !== "ollama" || !contextLength)
52
+ return {};
53
+ return {
54
+ providerOptions: {
55
+ ollama: {
56
+ num_ctx: contextLength
57
+ }
58
+ }
59
+ };
60
+ }
61
+ function createModel(provider, modelName, ollamaHost, ollamaContextLength) {
43
62
  switch (provider) {
44
63
  case "anthropic":
45
64
  return anthropic(modelName);
@@ -61,7 +80,7 @@ function createModel(provider, modelName, ollamaHost) {
61
80
  // ../packages/engine/src/tools/index.js
62
81
  import { tool } from "ai";
63
82
  import { z } from "zod";
64
- import path9 from "path";
83
+ import path10 from "path";
65
84
 
66
85
  // ../packages/engine/src/tools/bash.js
67
86
  import { spawn } from "child_process";
@@ -123,10 +142,7 @@ async function execute({ command, cwd, timeout = 12e4 }) {
123
142
  let killed = false;
124
143
  const child = spawn("/bin/bash", ["-c", command], {
125
144
  cwd: cwd || process.cwd(),
126
- env: {
127
- ...process.env,
128
- PATH: "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
129
- },
145
+ env: process.env,
130
146
  stdio: ["pipe", "pipe", "pipe"],
131
147
  detached: true
132
148
  });
@@ -206,11 +222,11 @@ async function execute({ command, cwd, timeout = 12e4 }) {
206
222
 
207
223
  // ../packages/engine/src/tools/read-file.js
208
224
  import fs2 from "fs";
209
- import path2 from "path";
225
+ import path3 from "path";
210
226
  var description2 = "Read the contents of a file. Returns the file content as a string. Supports text files of any type.";
211
227
  async function execute2({ path: filePath, encoding = "utf8", maxLines, startLine }) {
212
228
  try {
213
- const absolutePath = path2.isAbsolute(filePath) ? filePath : path2.resolve(process.cwd(), filePath);
229
+ const absolutePath = path3.isAbsolute(filePath) ? filePath : path3.resolve(process.cwd(), filePath);
214
230
  if (!fs2.existsSync(absolutePath)) {
215
231
  return {
216
232
  success: false,
@@ -263,12 +279,12 @@ async function execute2({ path: filePath, encoding = "utf8", maxLines, startLine
263
279
 
264
280
  // ../packages/engine/src/tools/write-file.js
265
281
  import fs3 from "fs";
266
- import path3 from "path";
282
+ import path4 from "path";
267
283
  var description3 = "Write content to a file. Creates the file if it does not exist, and creates any necessary parent directories. Overwrites existing content.";
268
284
  async function execute3({ path: filePath, content, encoding = "utf8", append = false }) {
269
285
  try {
270
- const absolutePath = path3.isAbsolute(filePath) ? filePath : path3.resolve(process.cwd(), filePath);
271
- const dirPath = path3.dirname(absolutePath);
286
+ const absolutePath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
287
+ const dirPath = path4.dirname(absolutePath);
272
288
  if (!fs3.existsSync(dirPath)) {
273
289
  fs3.mkdirSync(dirPath, { recursive: true });
274
290
  }
@@ -304,11 +320,11 @@ async function execute3({ path: filePath, content, encoding = "utf8", append = f
304
320
 
305
321
  // ../packages/engine/src/tools/edit-file.js
306
322
  import fs4 from "fs";
307
- import path4 from "path";
323
+ import path5 from "path";
308
324
  var description4 = "Edit a file by finding and replacing text. The old_string must be unique in the file (or use replaceAll for multiple occurrences). Use this instead of write_file when making targeted changes to existing files.";
309
325
  async function execute4({ path: filePath, old_string, new_string, replaceAll = false }) {
310
326
  try {
311
- const absolutePath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
327
+ const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
312
328
  if (!fs4.existsSync(absolutePath)) {
313
329
  return {
314
330
  success: false,
@@ -375,7 +391,7 @@ async function execute4({ path: filePath, old_string, new_string, replaceAll = f
375
391
 
376
392
  // ../packages/engine/src/tools/glob.js
377
393
  import fs5 from "fs";
378
- import path5 from "path";
394
+ import path6 from "path";
379
395
  function globToRegex(pattern) {
380
396
  let regex = "";
381
397
  let i = 0;
@@ -451,7 +467,7 @@ function walkDir(dir, files = [], maxDepth = 20, currentDepth = 0) {
451
467
  continue;
452
468
  if (entry.name === ".git")
453
469
  continue;
454
- const fullPath = path5.join(dir, entry.name);
470
+ const fullPath = path6.join(dir, entry.name);
455
471
  if (entry.isDirectory()) {
456
472
  walkDir(fullPath, files, maxDepth, currentDepth + 1);
457
473
  } else if (entry.isFile()) {
@@ -465,7 +481,7 @@ function walkDir(dir, files = [], maxDepth = 20, currentDepth = 0) {
465
481
  var description5 = 'Find files matching a glob pattern. Supports patterns like "**/*.ts", "src/**/*.js", "*.{ts,tsx}". Excludes node_modules and hidden files by default.';
466
482
  async function execute5({ pattern, cwd, maxResults = 1e3, includeHidden = false }) {
467
483
  try {
468
- const searchDir = cwd ? path5.isAbsolute(cwd) ? cwd : path5.resolve(process.cwd(), cwd) : process.cwd();
484
+ const searchDir = cwd ? path6.isAbsolute(cwd) ? cwd : path6.resolve(process.cwd(), cwd) : process.cwd();
469
485
  if (!fs5.existsSync(searchDir)) {
470
486
  return {
471
487
  success: false,
@@ -476,7 +492,7 @@ async function execute5({ pattern, cwd, maxResults = 1e3, includeHidden = false
476
492
  const regex = globToRegex(pattern);
477
493
  const matches = [];
478
494
  for (const file of allFiles) {
479
- const relativePath = path5.relative(searchDir, file).replace(/\\/g, "/");
495
+ const relativePath = path6.relative(searchDir, file).replace(/\\/g, "/");
480
496
  if (!includeHidden && relativePath.split("/").some((part) => part.startsWith("."))) {
481
497
  continue;
482
498
  }
@@ -510,7 +526,7 @@ async function execute5({ pattern, cwd, maxResults = 1e3, includeHidden = false
510
526
 
511
527
  // ../packages/engine/src/tools/grep.js
512
528
  import fs6 from "fs";
513
- import path6 from "path";
529
+ import path7 from "path";
514
530
  function walkDir2(dir, files = [], maxDepth = 20, currentDepth = 0) {
515
531
  if (currentDepth > maxDepth)
516
532
  return files;
@@ -529,13 +545,13 @@ function walkDir2(dir, files = [], maxDepth = 20, currentDepth = 0) {
529
545
  continue;
530
546
  if (entry.name === ".next")
531
547
  continue;
532
- const fullPath = path6.join(dir, entry.name);
548
+ const fullPath = path7.join(dir, entry.name);
533
549
  if (entry.isDirectory()) {
534
550
  if (!entry.name.startsWith(".")) {
535
551
  walkDir2(fullPath, files, maxDepth, currentDepth + 1);
536
552
  }
537
553
  } else if (entry.isFile()) {
538
- const ext = path6.extname(entry.name).toLowerCase();
554
+ const ext = path7.extname(entry.name).toLowerCase();
539
555
  const binaryExts = [
540
556
  ".png",
541
557
  ".jpg",
@@ -609,7 +625,7 @@ async function execute6({ pattern, path: searchPath, filePattern, ignoreCase = f
609
625
  error: `Invalid regex pattern: ${err.message}`
610
626
  };
611
627
  }
612
- const targetPath = searchPath ? path6.isAbsolute(searchPath) ? searchPath : path6.resolve(process.cwd(), searchPath) : process.cwd();
628
+ const targetPath = searchPath ? path7.isAbsolute(searchPath) ? searchPath : path7.resolve(process.cwd(), searchPath) : process.cwd();
613
629
  if (!fs6.existsSync(targetPath)) {
614
630
  return {
615
631
  success: false,
@@ -634,7 +650,7 @@ async function execute6({ pattern, path: searchPath, filePattern, ignoreCase = f
634
650
  break;
635
651
  const matches = searchFile(file, regex, contextLines);
636
652
  if (matches.length > 0) {
637
- const relativePath = path6.relative(targetPath, file).replace(/\\/g, "/") || path6.basename(file);
653
+ const relativePath = path7.relative(targetPath, file).replace(/\\/g, "/") || path7.basename(file);
638
654
  for (const match of matches) {
639
655
  if (totalMatches >= maxResults)
640
656
  break;
@@ -680,7 +696,7 @@ async function execute6({ pattern, path: searchPath, filePattern, ignoreCase = f
680
696
 
681
697
  // ../packages/engine/src/tools/ls.js
682
698
  import fs7 from "fs";
683
- import path7 from "path";
699
+ import path8 from "path";
684
700
  var description7 = "List directory contents in a tree format. Shows file names, types, and sizes. More efficient than bash ls \u2014 no subprocess needed, returns structured output.";
685
701
  var DEFAULT_IGNORE = ["node_modules", ".git", "__pycache__", ".next", ".nuxt"];
686
702
  function shouldIgnore(name, ignorePatterns) {
@@ -722,7 +738,7 @@ function buildTree(dirPath, prefix, depth, maxDepth, ignorePatterns, state) {
722
738
  const isLast = i === entries.length - 1;
723
739
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
724
740
  const childPrefix = isLast ? " " : "\u2502 ";
725
- const fullPath = path7.join(dirPath, entry.name);
741
+ const fullPath = path8.join(dirPath, entry.name);
726
742
  if (entry.isDirectory()) {
727
743
  state.dirs++;
728
744
  state.lines.push(`${prefix}${connector}${entry.name}/`);
@@ -831,7 +847,7 @@ async function execute8({ url, format = "markdown", timeout = 3e4 }) {
831
847
 
832
848
  // ../packages/engine/src/tools/patch.js
833
849
  import fs8 from "fs";
834
- import path8 from "path";
850
+ import path9 from "path";
835
851
  var description9 = "Apply a unified diff patch to one or more files atomically. All hunks are validated before any changes are written. If any hunk fails to apply, no files are modified. Use standard unified diff format (output of `git diff` or `diff -u`).";
836
852
  function parsePatch(patchText) {
837
853
  const patches = [];
@@ -986,7 +1002,7 @@ ${errors.join("\n")}`,
986
1002
  const filesCreated = [];
987
1003
  const filesDeleted = [];
988
1004
  for (const [filePath, content] of pendingWrites) {
989
- const dir = path8.dirname(filePath);
1005
+ const dir = path9.dirname(filePath);
990
1006
  if (!fs8.existsSync(dir)) {
991
1007
  fs8.mkdirSync(dir, { recursive: true });
992
1008
  }
@@ -1018,7 +1034,7 @@ ${errors.join("\n")}`,
1018
1034
  import { streamText, stepCountIs } from "ai";
1019
1035
  var description10 = "Spawn a read-only sub-agent to explore the codebase. The sub-agent can read files, search with glob/grep, and list directories, but cannot write files, edit, or run commands. Use this for parallel codebase exploration \u2014 understanding architecture, finding patterns, or researching how something works before making changes.";
1020
1036
  function createSubAgentExecutor(model, workingDir, readOnlyTools) {
1021
- return async function execute11({ prompt, maxTurns = 20 }) {
1037
+ return async function execute13({ prompt, maxTurns = 20 }) {
1022
1038
  try {
1023
1039
  let turnsUsed = 0;
1024
1040
  const stream = streamText({
@@ -1100,13 +1116,103 @@ async function execute10({ action, args, cwd }) {
1100
1116
  }
1101
1117
  }
1102
1118
 
1119
+ // ../packages/engine/src/tools/web-search.js
1120
+ var description12 = "Search the web for documentation, error messages, library usage, or any information. Returns search results with titles, URLs, and snippets. Use this when you need up-to-date information that might not be in your training data.";
1121
+ async function execute11(input) {
1122
+ const { query, maxResults = 8 } = input;
1123
+ try {
1124
+ const encoded = encodeURIComponent(query);
1125
+ const response = await globalThis.fetch(`https://html.duckduckgo.com/html/?q=${encoded}`, {
1126
+ headers: {
1127
+ "User-Agent": "WorkerMill/1.0 (AI Coding Agent)"
1128
+ },
1129
+ signal: AbortSignal.timeout(15e3)
1130
+ });
1131
+ if (!response.ok) {
1132
+ return { success: false, error: `Search failed: HTTP ${response.status}` };
1133
+ }
1134
+ const html = await response.text();
1135
+ const results = [];
1136
+ const resultPattern = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g;
1137
+ let match;
1138
+ while ((match = resultPattern.exec(html)) !== null && results.length < maxResults) {
1139
+ const rawUrl = match[1];
1140
+ const title = match[2].replace(/<[^>]+>/g, "").trim();
1141
+ const snippet = match[3].replace(/<[^>]+>/g, "").trim();
1142
+ let url = rawUrl;
1143
+ const uddgMatch = rawUrl.match(/uddg=([^&]+)/);
1144
+ if (uddgMatch) {
1145
+ url = decodeURIComponent(uddgMatch[1]);
1146
+ }
1147
+ if (title && url) {
1148
+ results.push({ title, url, snippet });
1149
+ }
1150
+ }
1151
+ if (results.length === 0) {
1152
+ return { success: true, results: [], error: "No results found" };
1153
+ }
1154
+ return { success: true, results };
1155
+ } catch (err) {
1156
+ return {
1157
+ success: false,
1158
+ error: err instanceof Error ? err.message : String(err)
1159
+ };
1160
+ }
1161
+ }
1162
+
1163
+ // ../packages/engine/src/tools/todo.js
1164
+ var description13 = "Track your progress within a task. Create todo items, mark them complete, and list status. Use this to stay organized on multi-step work.";
1165
+ var todos = /* @__PURE__ */ new Map();
1166
+ var nextId = 1;
1167
+ async function execute12(input) {
1168
+ const { action, text, id, status } = input;
1169
+ switch (action) {
1170
+ case "add": {
1171
+ if (!text)
1172
+ return { success: false, error: "Text is required for adding a todo" };
1173
+ const todoId = `todo-${nextId++}`;
1174
+ const item = {
1175
+ id: todoId,
1176
+ text,
1177
+ status: "pending",
1178
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1179
+ };
1180
+ todos.set(todoId, item);
1181
+ return { success: true, item };
1182
+ }
1183
+ case "update": {
1184
+ if (!id)
1185
+ return { success: false, error: "ID is required for updating a todo" };
1186
+ const existing = todos.get(id);
1187
+ if (!existing)
1188
+ return { success: false, error: `Todo not found: ${id}` };
1189
+ if (status)
1190
+ existing.status = status;
1191
+ if (text)
1192
+ existing.text = text;
1193
+ return { success: true, item: existing };
1194
+ }
1195
+ case "list": {
1196
+ const items = Array.from(todos.values());
1197
+ return { success: true, items };
1198
+ }
1199
+ case "clear": {
1200
+ todos.clear();
1201
+ nextId = 1;
1202
+ return { success: true, items: [] };
1203
+ }
1204
+ default:
1205
+ return { success: false, error: `Unknown action: ${action}` };
1206
+ }
1207
+ }
1208
+
1103
1209
  // ../packages/engine/src/tools/index.js
1104
1210
  function assertPathInBounds(resolvedPath, workingDir, sandboxed) {
1105
1211
  if (!sandboxed)
1106
1212
  return resolvedPath;
1107
- const normalized = path9.resolve(resolvedPath);
1108
- const normalizedWorkDir = path9.resolve(workingDir);
1109
- if (!normalized.startsWith(normalizedWorkDir + path9.sep) && normalized !== normalizedWorkDir) {
1213
+ const normalized = path10.resolve(resolvedPath);
1214
+ const normalizedWorkDir = path10.resolve(workingDir);
1215
+ if (!normalized.startsWith(normalizedWorkDir + path10.sep) && normalized !== normalizedWorkDir) {
1110
1216
  throw new Error(`Path "${resolvedPath}" is outside the working directory. Use --full-disk to allow access outside ${workingDir}`);
1111
1217
  }
1112
1218
  return normalized;
@@ -1121,7 +1227,7 @@ function createToolDefinitions(workingDir, model, sandboxed = true) {
1121
1227
  timeout: z.number().optional().describe("Timeout in milliseconds (default: 120000 = 2 minutes)")
1122
1228
  }),
1123
1229
  execute: async ({ command, cwd, timeout }) => {
1124
- const resolvedCwd = cwd ? path9.isAbsolute(cwd) ? cwd : path9.resolve(workingDir, cwd) : workingDir;
1230
+ const resolvedCwd = cwd ? path10.isAbsolute(cwd) ? cwd : path10.resolve(workingDir, cwd) : workingDir;
1125
1231
  assertPathInBounds(resolvedCwd, workingDir, sandboxed);
1126
1232
  const result = await execute({
1127
1233
  command,
@@ -1131,8 +1237,14 @@ function createToolDefinitions(workingDir, model, sandboxed = true) {
1131
1237
  if (result.success) {
1132
1238
  return result.stdout || "(no output)";
1133
1239
  }
1134
- return `Error: ${result.error || result.stderr}
1135
- ${result.stdout || ""}`.trim();
1240
+ const parts = [];
1241
+ if (result.stderr)
1242
+ parts.push(result.stderr);
1243
+ if (result.stdout)
1244
+ parts.push(result.stdout);
1245
+ if (result.error)
1246
+ parts.push(result.error);
1247
+ return `Error: ${parts.join("\n")}`.trim();
1136
1248
  }
1137
1249
  }),
1138
1250
  read_file: tool({
@@ -1144,7 +1256,7 @@ ${result.stdout || ""}`.trim();
1144
1256
  startLine: z.number().optional().describe("Line number to start reading from (1-indexed, optional)")
1145
1257
  }),
1146
1258
  execute: async ({ path: filePath, encoding, maxLines, startLine }) => {
1147
- const resolvedPath = path9.isAbsolute(filePath) ? filePath : path9.resolve(workingDir, filePath);
1259
+ const resolvedPath = path10.isAbsolute(filePath) ? filePath : path10.resolve(workingDir, filePath);
1148
1260
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1149
1261
  const result = await execute2({
1150
1262
  path: resolvedPath,
@@ -1167,7 +1279,7 @@ ${result.stdout || ""}`.trim();
1167
1279
  append: z.boolean().optional().describe("Append to file instead of overwriting (default: false)")
1168
1280
  }),
1169
1281
  execute: async ({ path: filePath, content, encoding, append }) => {
1170
- const resolvedPath = path9.isAbsolute(filePath) ? filePath : path9.resolve(workingDir, filePath);
1282
+ const resolvedPath = path10.isAbsolute(filePath) ? filePath : path10.resolve(workingDir, filePath);
1171
1283
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1172
1284
  const result = await execute3({
1173
1285
  path: resolvedPath,
@@ -1190,7 +1302,7 @@ ${result.stdout || ""}`.trim();
1190
1302
  replaceAll: z.boolean().optional().describe("Replace all occurrences instead of requiring unique match (default: false)")
1191
1303
  }),
1192
1304
  execute: async ({ path: filePath, old_string, new_string, replaceAll }) => {
1193
- const resolvedPath = path9.isAbsolute(filePath) ? filePath : path9.resolve(workingDir, filePath);
1305
+ const resolvedPath = path10.isAbsolute(filePath) ? filePath : path10.resolve(workingDir, filePath);
1194
1306
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1195
1307
  const result = await execute4({
1196
1308
  path: resolvedPath,
@@ -1214,7 +1326,7 @@ Hint: ${result.hint}` : ""}`;
1214
1326
  includeHidden: z.boolean().optional().describe("Include hidden files (starting with .) (default: false)")
1215
1327
  }),
1216
1328
  execute: async ({ pattern, cwd, maxResults, includeHidden }) => {
1217
- const resolvedCwd = cwd ? path9.isAbsolute(cwd) ? cwd : path9.resolve(workingDir, cwd) : workingDir;
1329
+ const resolvedCwd = cwd ? path10.isAbsolute(cwd) ? cwd : path10.resolve(workingDir, cwd) : workingDir;
1218
1330
  assertPathInBounds(resolvedCwd, workingDir, sandboxed);
1219
1331
  const result = await execute5({
1220
1332
  pattern,
@@ -1243,7 +1355,7 @@ ${result.matches.join("\n")}`;
1243
1355
  maxResults: z.number().optional().describe("Maximum number of total matches to return (default: 100)")
1244
1356
  }),
1245
1357
  execute: async ({ pattern, path: searchPath, filePattern, ignoreCase, contextLines, maxResults }) => {
1246
- const resolvedPath = searchPath ? path9.isAbsolute(searchPath) ? searchPath : path9.resolve(workingDir, searchPath) : workingDir;
1358
+ const resolvedPath = searchPath ? path10.isAbsolute(searchPath) ? searchPath : path10.resolve(workingDir, searchPath) : workingDir;
1247
1359
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1248
1360
  const result = await execute6({
1249
1361
  pattern,
@@ -1279,7 +1391,7 @@ ${result.matches.join("\n")}`;
1279
1391
  maxFiles: z.number().optional().describe("Maximum number of entries to return (default: 1000)")
1280
1392
  }),
1281
1393
  execute: async ({ path: dirPath, ignore, maxDepth, maxFiles }) => {
1282
- const resolvedPath = path9.isAbsolute(dirPath) ? dirPath : path9.resolve(workingDir, dirPath);
1394
+ const resolvedPath = path10.isAbsolute(dirPath) ? dirPath : path10.resolve(workingDir, dirPath);
1283
1395
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1284
1396
  const result = await execute7({ path: resolvedPath, ignore, maxDepth, maxFiles });
1285
1397
  if (result.success) {
@@ -1342,6 +1454,49 @@ Hint: ${result.hint}` : ""}`;
1342
1454
  return `Error: ${result.error}`;
1343
1455
  }
1344
1456
  }),
1457
+ web_search: tool({
1458
+ description: description12,
1459
+ inputSchema: z.object({
1460
+ query: z.string().describe("Search query \u2014 be specific, include library names, error messages, etc."),
1461
+ maxResults: z.number().optional().describe("Maximum results to return (default: 8)")
1462
+ }),
1463
+ execute: async ({ query, maxResults }) => {
1464
+ const result = await execute11({ query, maxResults });
1465
+ if (result.success && result.results && result.results.length > 0) {
1466
+ return result.results.map((r, i) => `${i + 1}. ${r.title}
1467
+ ${r.url}
1468
+ ${r.snippet}`).join("\n\n");
1469
+ }
1470
+ return result.error || "No results found";
1471
+ }
1472
+ }),
1473
+ todo: tool({
1474
+ description: description13,
1475
+ inputSchema: z.object({
1476
+ action: z.enum(["add", "update", "list", "clear"]).describe("Action to perform"),
1477
+ text: z.string().optional().describe("Todo text (for add/update)"),
1478
+ id: z.string().optional().describe("Todo ID (for update)"),
1479
+ status: z.enum(["pending", "in_progress", "completed"]).optional().describe("New status (for update)")
1480
+ }),
1481
+ execute: async ({ action, text, id, status }) => {
1482
+ const result = await execute12({ action, text, id, status });
1483
+ if (result.success) {
1484
+ if (result.item) {
1485
+ return `[${result.item.status}] ${result.item.id}: ${result.item.text}`;
1486
+ }
1487
+ if (result.items) {
1488
+ if (result.items.length === 0)
1489
+ return "No todos";
1490
+ const pending = result.items.filter((t) => t.status !== "completed").length;
1491
+ const done = result.items.filter((t) => t.status === "completed").length;
1492
+ return result.items.map((t) => `[${t.status === "completed" ? "\u2713" : t.status === "in_progress" ? "\u2192" : " "}] ${t.id}: ${t.text}`).join("\n") + `
1493
+
1494
+ ${done}/${result.items.length} completed, ${pending} remaining`;
1495
+ }
1496
+ }
1497
+ return result.error || "Unknown error";
1498
+ }
1499
+ }),
1345
1500
  ...model ? {
1346
1501
  sub_agent: tool({
1347
1502
  description: description10,
@@ -1359,7 +1514,7 @@ Hint: ${result.hint}` : ""}`;
1359
1514
  startLine: z.number().optional().describe("Start line (1-indexed)")
1360
1515
  }),
1361
1516
  execute: async ({ path: filePath, maxLines, startLine }) => {
1362
- const resolvedPath = path9.isAbsolute(filePath) ? filePath : path9.resolve(workingDir, filePath);
1517
+ const resolvedPath = path10.isAbsolute(filePath) ? filePath : path10.resolve(workingDir, filePath);
1363
1518
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1364
1519
  const result2 = await execute2({ path: resolvedPath, maxLines, startLine });
1365
1520
  return result2.success ? result2.content || "" : `Error: ${result2.error}`;
@@ -1372,7 +1527,7 @@ Hint: ${result.hint}` : ""}`;
1372
1527
  cwd: z.string().optional().describe("Directory to search in")
1373
1528
  }),
1374
1529
  execute: async ({ pattern, cwd }) => {
1375
- const resolvedCwd = cwd ? path9.isAbsolute(cwd) ? cwd : path9.resolve(workingDir, cwd) : workingDir;
1530
+ const resolvedCwd = cwd ? path10.isAbsolute(cwd) ? cwd : path10.resolve(workingDir, cwd) : workingDir;
1376
1531
  assertPathInBounds(resolvedCwd, workingDir, sandboxed);
1377
1532
  const result2 = await execute5({ pattern, cwd: resolvedCwd });
1378
1533
  return result2.success ? result2.count === 0 ? `No files found matching: ${pattern}` : `Found ${result2.count} file(s):
@@ -1387,7 +1542,7 @@ ${result2.matches.join("\n")}` : `Error: ${result2.error}`;
1387
1542
  filePattern: z.string().optional().describe("Glob to filter files")
1388
1543
  }),
1389
1544
  execute: async ({ pattern, path: searchPath, filePattern }) => {
1390
- const resolvedPath = searchPath ? path9.isAbsolute(searchPath) ? searchPath : path9.resolve(workingDir, searchPath) : workingDir;
1545
+ const resolvedPath = searchPath ? path10.isAbsolute(searchPath) ? searchPath : path10.resolve(workingDir, searchPath) : workingDir;
1391
1546
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1392
1547
  const result2 = await execute6({ pattern, path: resolvedPath, filePattern });
1393
1548
  if (!result2.success)
@@ -1410,7 +1565,7 @@ ${result2.matches.join("\n")}` : `Error: ${result2.error}`;
1410
1565
  maxDepth: z.number().optional().describe("Max depth (default: 3)")
1411
1566
  }),
1412
1567
  execute: async ({ path: dirPath, maxDepth }) => {
1413
- const resolvedPath = path9.isAbsolute(dirPath) ? dirPath : path9.resolve(workingDir, dirPath);
1568
+ const resolvedPath = path10.isAbsolute(dirPath) ? dirPath : path10.resolve(workingDir, dirPath);
1414
1569
  assertPathInBounds(resolvedPath, workingDir, sandboxed);
1415
1570
  const result2 = await execute7({ path: resolvedPath, maxDepth });
1416
1571
  return result2.success ? result2.tree : `Error: ${result2.error}`;
@@ -1431,422 +1586,21 @@ ${result.content}`;
1431
1586
  };
1432
1587
  }
1433
1588
 
1434
- // src/permissions.js
1435
- import readline from "readline";
1436
- import chalk from "chalk";
1437
- var READ_TOOLS = /* @__PURE__ */ new Set(["read_file", "glob", "grep", "ls", "sub_agent"]);
1438
- var DANGEROUS_PATTERNS = [
1439
- { pattern: /rm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i, label: "recursive/forced delete" },
1440
- { pattern: /git\s+reset\s+--hard/i, label: "hard reset" },
1441
- { pattern: /git\s+push\s+.*--force/i, label: "force push" },
1442
- { pattern: /git\s+clean\s+-[a-z]*f/i, label: "git clean" },
1443
- { pattern: /drop\s+table/i, label: "drop table" },
1444
- { pattern: /truncate\s+/i, label: "truncate" },
1445
- { pattern: /DELETE\s+FROM\s+\w+\s*;/i, label: "DELETE without WHERE" },
1446
- { pattern: /chmod\s+777/i, label: "chmod 777" },
1447
- { pattern: />(\/dev\/sda|\/dev\/disk)/i, label: "write to disk device" }
1448
- ];
1449
- function isDangerous(command) {
1450
- for (const { pattern, label } of DANGEROUS_PATTERNS) {
1451
- if (pattern.test(command))
1452
- return label;
1453
- }
1454
- return null;
1455
- }
1456
- var PermissionManager = class {
1457
- sessionAllow = /* @__PURE__ */ new Set();
1458
- trustAll;
1459
- configTrust;
1460
- rl = null;
1461
- cancelCurrentPrompt = null;
1462
- /** True while rl.question() is active — external line handlers must ignore input */
1463
- questionActive = false;
1464
- constructor(trustAll = false, configTrust = []) {
1465
- this.trustAll = trustAll;
1466
- this.configTrust = new Set(configTrust);
1467
- }
1468
- /** Bind to the agent's readline instance so we reuse it for prompts */
1469
- setReadline(rl) {
1470
- this.rl = rl;
1471
- }
1472
- cancelPrompt() {
1473
- if (this.cancelCurrentPrompt) {
1474
- this.cancelCurrentPrompt();
1475
- this.cancelCurrentPrompt = null;
1476
- }
1477
- }
1478
- async checkPermission(toolName, toolInput) {
1479
- if (toolName === "bash") {
1480
- const cmd = String(toolInput.command || "");
1481
- const danger = isDangerous(cmd);
1482
- if (danger) {
1483
- console.log();
1484
- console.log(chalk.red.bold(` \u26A0 DANGEROUS: ${danger}`));
1485
- console.log(chalk.red(` Command: ${cmd}`));
1486
- if (this.trustAll) {
1487
- console.log(chalk.yellow(" (allowed by --trust mode)"));
1488
- return true;
1489
- }
1490
- const answer = await this.askUser(chalk.red(" Are you sure? (yes to confirm): "));
1491
- if (answer.trim().toLowerCase() !== "yes")
1492
- return false;
1493
- return true;
1494
- }
1495
- }
1496
- if (this.trustAll)
1497
- return true;
1498
- if (READ_TOOLS.has(toolName))
1499
- return true;
1500
- if (this.sessionAllow.has(toolName))
1501
- return true;
1502
- if (this.configTrust.has(toolName))
1503
- return true;
1504
- return this.promptUser(toolName, toolInput);
1505
- }
1506
- async promptUser(toolName, toolInput) {
1507
- const display = this.formatToolCall(toolName, toolInput);
1508
- console.log();
1509
- console.log(chalk.cyan(` \u250C\u2500 ${toolName} ${"\u2500".repeat(Math.max(0, 40 - toolName.length))}\u2510`));
1510
- for (const line of display.split("\n")) {
1511
- console.log(chalk.cyan(" \u2502 ") + chalk.white(line));
1512
- }
1513
- console.log(chalk.cyan(` \u2514${"\u2500".repeat(43)}\u2518`));
1514
- const answer = await this.askUser(chalk.dim(" Allow? ") + chalk.white("(y)es / (n)o / (a)lways this tool / (t)rust all: "));
1515
- const choice = answer.trim().toLowerCase();
1516
- if (choice === "t" || choice === "trust") {
1517
- this.trustAll = true;
1518
- return true;
1519
- }
1520
- if (choice === "a" || choice === "always") {
1521
- this.sessionAllow.add(toolName);
1522
- return true;
1523
- }
1524
- return choice === "y" || choice === "yes";
1525
- }
1526
- /**
1527
- * Prompt the user with a question. Sets questionActive flag so the
1528
- * agent's line handler knows to ignore this input.
1529
- */
1530
- askUser(prompt) {
1531
- return new Promise((resolve, reject) => {
1532
- this.cancelCurrentPrompt = () => {
1533
- this.questionActive = false;
1534
- reject(new Error("cancelled"));
1535
- };
1536
- if (this.rl) {
1537
- this.questionActive = true;
1538
- this.rl.resume();
1539
- this.rl.question(prompt, (answer) => {
1540
- this.questionActive = false;
1541
- this.cancelCurrentPrompt = null;
1542
- this.rl.pause();
1543
- resolve(answer);
1544
- });
1545
- } else {
1546
- const questionRl = readline.createInterface({
1547
- input: process.stdin,
1548
- output: process.stdout
1549
- });
1550
- this.questionActive = true;
1551
- questionRl.question(prompt, (answer) => {
1552
- this.questionActive = false;
1553
- this.cancelCurrentPrompt = null;
1554
- questionRl.close();
1555
- resolve(answer);
1556
- });
1557
- }
1558
- });
1559
- }
1560
- formatToolCall(toolName, input) {
1561
- switch (toolName) {
1562
- case "bash":
1563
- return String(input.command || "");
1564
- case "write_file":
1565
- case "edit_file":
1566
- return `${input.path || ""}`;
1567
- case "patch":
1568
- return String(input.patch_text || "").slice(0, 200) + "...";
1569
- case "fetch":
1570
- return String(input.url || "");
1571
- default:
1572
- return JSON.stringify(input, null, 2).slice(0, 200);
1573
- }
1574
- }
1575
- };
1576
-
1577
- // src/tui.js
1578
- import chalk2 from "chalk";
1579
- import { execSync as execSync2 } from "child_process";
1580
- var toolCounts = {};
1581
- var cachedGitBranch = "";
1582
- var lastBranchCheck = 0;
1583
- function getGitBranch() {
1584
- const now = Date.now();
1585
- if (now - lastBranchCheck > 1e4) {
1586
- lastBranchCheck = now;
1587
- try {
1588
- cachedGitBranch = execSync2("git rev-parse --abbrev-ref HEAD 2>/dev/null", { encoding: "utf-8", timeout: 2e3 }).trim();
1589
- } catch {
1590
- cachedGitBranch = "";
1591
- }
1592
- }
1593
- return cachedGitBranch;
1594
- }
1595
- function incrementToolCount(toolName) {
1596
- toolCounts[toolName] = (toolCounts[toolName] || 0) + 1;
1597
- }
1598
- function printHeader(version, provider, model, cwd) {
1599
- console.log();
1600
- console.log(chalk2.bold.white(` WorkerMill`) + chalk2.dim(` v${version}`));
1601
- if (provider && model) {
1602
- console.log(chalk2.dim(` ${provider}/`) + chalk2.white(model));
1603
- }
1604
- if (cwd) {
1605
- console.log(chalk2.dim(` cwd: `) + chalk2.white(cwd));
1606
- }
1607
- console.log(chalk2.dim(` /help`) + chalk2.dim(` for commands, `) + chalk2.dim(`Ctrl+C`) + chalk2.dim(` to cancel`));
1608
- console.log();
1609
- }
1610
- function printToolCall(toolName, toolInput) {
1611
- incrementToolCount(toolName);
1612
- const arrow = chalk2.dim(" \u2193 ");
1613
- const label = chalk2.cyan;
1614
- switch (toolName) {
1615
- case "bash": {
1616
- let cmd = String(toolInput.command || "");
1617
- const heredocIdx = cmd.indexOf("<<");
1618
- if (heredocIdx > 0)
1619
- cmd = cmd.slice(0, heredocIdx).trim() + " << ...";
1620
- if (cmd.length > 120)
1621
- cmd = cmd.slice(0, 117) + "...";
1622
- console.log(arrow + label("Bash ") + chalk2.yellow(cmd));
1623
- break;
1624
- }
1625
- case "read_file":
1626
- console.log(arrow + label("Read ") + chalk2.white(String(toolInput.path || "")));
1627
- break;
1628
- case "write_file":
1629
- console.log(arrow + label("Write ") + chalk2.white(String(toolInput.path || "")));
1630
- break;
1631
- case "edit_file":
1632
- console.log(arrow + label("Edit ") + chalk2.white(String(toolInput.path || "")));
1633
- break;
1634
- case "patch":
1635
- console.log(arrow + label("Patch ") + chalk2.white("(multi-file)"));
1636
- break;
1637
- case "glob":
1638
- console.log(arrow + label("Glob ") + chalk2.white(String(toolInput.pattern || "")));
1639
- break;
1640
- case "grep":
1641
- console.log(arrow + label("Grep ") + chalk2.white(String(toolInput.pattern || "")));
1642
- break;
1643
- case "ls":
1644
- console.log(arrow + label("List ") + chalk2.white(String(toolInput.path || ".")));
1645
- break;
1646
- case "fetch":
1647
- console.log(arrow + label("Fetch ") + chalk2.white(String(toolInput.url || "")));
1648
- break;
1649
- case "sub_agent":
1650
- console.log(arrow + label("Agent ") + chalk2.white(String(toolInput.prompt || "").slice(0, 80)));
1651
- break;
1652
- case "git":
1653
- console.log(arrow + label("Git ") + chalk2.white(`${toolInput.action}${toolInput.args ? " " + toolInput.args : ""}`));
1654
- break;
1655
- default:
1656
- console.log(arrow + label(toolName));
1657
- }
1658
- }
1659
- var PERSONA_EMOJIS = {
1660
- frontend_developer: "\u{1F3A8}",
1661
- // 🎨
1662
- backend_developer: "\u{1F4BB}",
1663
- // 💻
1664
- fullstack_developer: "\u{1F4BB}",
1665
- // 💻 (same as backend)
1666
- devops_engineer: "\u{1F527}",
1667
- // 🔧
1668
- security_engineer: "\u{1F512}",
1669
- // 🔐
1670
- qa_engineer: "\u{1F9EA}",
1671
- // 🧪
1672
- tech_writer: "\u{1F4DD}",
1673
- // 📝
1674
- project_manager: "\u{1F4CB}",
1675
- // 📋
1676
- architect: "\u{1F3D7}\uFE0F",
1677
- // 🏗️
1678
- database_engineer: "\u{1F4CA}",
1679
- // 📊
1680
- data_engineer: "\u{1F4CA}",
1681
- // 📊
1682
- data_ml_engineer: "\u{1F4CA}",
1683
- // 📊
1684
- ml_engineer: "\u{1F4CA}",
1685
- // 📊
1686
- mobile_developer: "\u{1F4F1}",
1687
- // 📱
1688
- tech_lead: "\u{1F451}",
1689
- // 👑
1690
- manager: "\u{1F454}",
1691
- // 👔
1692
- support_agent: "\u{1F4AC}",
1693
- // 💬
1694
- planner: "\u{1F4A1}",
1695
- // 💡 (planning_agent)
1696
- coordinator: "\u{1F3AF}",
1697
- // 🎯
1698
- critic: "\u{1F50D}",
1699
- // 🔍
1700
- reviewer: "\u{1F50D}"
1701
- // 🔍
1702
- };
1703
- function getPersonaEmoji(persona) {
1704
- return PERSONA_EMOJIS[persona] || "\u{1F916}";
1705
- }
1706
- function printToolResult(toolName, result) {
1707
- const isError = result.startsWith("Error:") || result.startsWith("error:");
1708
- const lines = result.split("\n").filter((l) => l.trim());
1709
- if (isError) {
1710
- const errLines = lines.slice(0, 5);
1711
- for (const line of errLines) {
1712
- console.log(chalk2.red(` ${line}`));
1713
- }
1714
- if (lines.length > 5)
1715
- console.log(chalk2.red(` ... ${lines.length - 5} more lines`));
1716
- return;
1717
- }
1718
- switch (toolName) {
1719
- case "read_file":
1720
- console.log(chalk2.dim(` (${lines.length} lines)`));
1721
- break;
1722
- case "write_file":
1723
- case "edit_file":
1724
- case "patch":
1725
- if (lines.length === 1) {
1726
- console.log(chalk2.dim(` ${lines[0]}`));
1727
- } else {
1728
- console.log(chalk2.dim(` (${lines.length} lines changed)`));
1729
- }
1730
- break;
1731
- case "bash": {
1732
- if (lines.length === 0)
1733
- break;
1734
- const shown = lines.slice(0, 5);
1735
- for (const line of shown) {
1736
- console.log(chalk2.dim(` ${line}`));
1737
- }
1738
- if (lines.length > 5)
1739
- console.log(chalk2.dim(` ... ${lines.length - 5} more lines`));
1740
- break;
1741
- }
1742
- case "glob":
1743
- case "grep":
1744
- case "ls":
1745
- const searchShown = lines.slice(0, 8);
1746
- for (const line of searchShown) {
1747
- console.log(chalk2.dim(` ${line}`));
1748
- }
1749
- if (lines.length > 8)
1750
- console.log(chalk2.dim(` ... ${lines.length - 8} more`));
1751
- break;
1752
- default:
1753
- if (lines.length <= 3) {
1754
- for (const line of lines)
1755
- console.log(chalk2.dim(` ${line}`));
1756
- } else {
1757
- console.log(chalk2.dim(` (${lines.length} lines)`));
1758
- }
1759
- }
1760
- }
1761
- function wmLog(persona, message) {
1762
- const emoji = getPersonaEmoji(persona);
1763
- console.log(chalk2.cyan(`[${emoji} ${persona} \u{1F3E0}] `) + chalk2.white(message));
1764
- }
1765
- function wmLogPrefix(persona) {
1766
- const emoji = getPersonaEmoji(persona);
1767
- return chalk2.cyan(`[${emoji} ${persona} \u{1F3E0}] `);
1768
- }
1769
- function wmCoordinatorLog(message) {
1770
- console.log(chalk2.cyan("[coordinator] ") + chalk2.white(message));
1771
- }
1772
- function printError(message) {
1773
- console.log(chalk2.red(`
1774
- \u2717 ${message}
1775
- `));
1776
- }
1777
- var sessionStartTime = Date.now();
1778
- function formatTokens(n) {
1779
- if (n >= 1e6)
1780
- return `${(n / 1e6).toFixed(1)}M`;
1781
- if (n >= 1e3)
1782
- return `${(n / 1e3).toFixed(n >= 1e4 ? 0 : 1)}k`;
1783
- return String(n);
1784
- }
1785
- function formatElapsed() {
1786
- const mins = Math.floor((Date.now() - sessionStartTime) / 6e4);
1787
- if (mins < 1)
1788
- return "<1m";
1789
- return `>${mins}m`;
1790
- }
1791
- function contextBar(tokens, maxContext) {
1792
- const barLen = 8;
1793
- const usage = Math.min(1, tokens / maxContext);
1794
- const filled = Math.round(usage * barLen);
1795
- const empty = barLen - filled;
1796
- const color = usage < 0.5 ? chalk2.green : usage < 0.8 ? chalk2.yellow : chalk2.red;
1797
- return color("\u2588".repeat(filled)) + chalk2.dim("\u2591".repeat(empty));
1798
- }
1799
- function printStatusBar(provider, model, tokens, permissionMode, cost) {
1800
- const width = process.stdout.columns || 80;
1801
- const bg = chalk2.bgRgb(30, 30, 30);
1802
- const modelDisplay = ` ${model} `;
1803
- const maxCtx = 128e3;
1804
- const bar = contextBar(tokens, maxCtx);
1805
- const tokStr = formatTokens(tokens);
1806
- const shortNames = {
1807
- bash: "Bash",
1808
- read_file: "Read",
1809
- write_file: "Write",
1810
- edit_file: "Edit",
1811
- glob: "Glob",
1812
- grep: "Grep",
1813
- ls: "List",
1814
- fetch: "Fetch",
1815
- patch: "Patch",
1816
- sub_agent: "Agent",
1817
- git: "Git"
1818
- };
1819
- const countParts = Object.entries(toolCounts).filter(([_, count]) => count > 0).map(([name, count]) => `${shortNames[name] || name} x${count}`);
1820
- const toolStr = countParts.length > 0 ? countParts.join(" | ") : "";
1821
- const gitBranch = getGitBranch();
1822
- const cwd = process.cwd().split("/").pop() || "";
1823
- const costStr = cost !== void 0 && cost > 0 ? `$${cost.toFixed(4)} | ` : "";
1824
- const elapsed = formatElapsed();
1825
- const modeStr = permissionMode || "ask";
1826
- const left = bg.white(modelDisplay) + " " + bar + " " + bg.white(tokStr);
1827
- const middle = gitBranch ? bg.dim(" | ") + bg.white(cwd) + bg.dim(" git:(") + bg.green(gitBranch) + bg.dim(")") : bg.dim(" | ") + bg.white(cwd);
1828
- const tools = toolStr ? bg.dim(" | ") + bg.dim(toolStr) : "";
1829
- const right = bg.dim(" | ") + bg.dim(costStr) + bg.white(elapsed) + bg.dim(" ") + bg.green(modeStr) + " ";
1830
- const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
1831
- const contentLen = stripAnsi(left).length + stripAnsi(middle).length + stripAnsi(tools).length + stripAnsi(right).length;
1832
- const pad = Math.max(0, width - contentLen);
1833
- return left + middle + tools + bg(" ".repeat(pad)) + right;
1834
- }
1835
-
1836
1589
  // src/cost-tracker.js
1837
1590
  var PRICING = {
1838
- // Anthropic
1591
+ // Anthropic (Claude 4.5/4.6)
1839
1592
  "claude-opus-4-6": { input: 15, output: 75 },
1840
1593
  "claude-sonnet-4-6": { input: 3, output: 15 },
1841
1594
  "claude-haiku-4-5": { input: 0.8, output: 4 },
1842
- // OpenAI
1843
- "gpt-4o": { input: 2.5, output: 10 },
1844
- "gpt-4o-mini": { input: 0.15, output: 0.6 },
1845
- "o3": { input: 10, output: 40 },
1846
- "o3-mini": { input: 1.1, output: 4.4 },
1847
- // Google
1848
- "gemini-2.5-pro": { input: 1.25, output: 10 },
1849
- "gemini-2.5-flash": { input: 0.15, output: 0.6 }
1595
+ // OpenAI (GPT-5.x)
1596
+ "gpt-5.4": { input: 0.75, output: 4.5 },
1597
+ "gpt-5.4-mini": { input: 0.15, output: 0.6 },
1598
+ "gpt-5.4-pro": { input: 5, output: 20 },
1599
+ "gpt-5.3": { input: 1, output: 5 },
1600
+ // Google (Gemini 3.x)
1601
+ "gemini-3.1-pro": { input: 1.25, output: 10 },
1602
+ "gemini-3.1-flash-lite": { input: 0.15, output: 0.6 }
1603
+ // Ollama (free, local)
1850
1604
  };
1851
1605
  var CostTracker = class {
1852
1606
  entries = [];
@@ -1886,21 +1640,13 @@ var CostTracker = class {
1886
1640
  };
1887
1641
 
1888
1642
  export {
1643
+ __dirname,
1889
1644
  loadConfig,
1890
1645
  saveConfig,
1891
1646
  getProviderForPersona,
1647
+ buildOllamaOptions,
1892
1648
  createModel,
1893
1649
  killActiveProcess,
1894
1650
  createToolDefinitions,
1895
- PermissionManager,
1896
- printHeader,
1897
- printToolCall,
1898
- getPersonaEmoji,
1899
- printToolResult,
1900
- wmLog,
1901
- wmLogPrefix,
1902
- wmCoordinatorLog,
1903
- printError,
1904
- printStatusBar,
1905
1651
  CostTracker
1906
1652
  };