zelari-code 0.1.3 → 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.
@@ -523,11 +523,11 @@ __export(updater_exports, {
523
523
  });
524
524
  import { createRequire } from "node:module";
525
525
  import { spawn as spawn2 } from "node:child_process";
526
- import path15 from "node:path";
526
+ import path16 from "node:path";
527
527
  import { fileURLToPath } from "node:url";
528
528
  function getCurrentVersion() {
529
529
  try {
530
- const pkgPath = path15.resolve(__dirname2, "..", "..", "package.json");
530
+ const pkgPath = path16.resolve(__dirname2, "..", "..", "package.json");
531
531
  const pkg = require2(pkgPath);
532
532
  return pkg.version;
533
533
  } catch {
@@ -626,7 +626,7 @@ var init_updater = __esm({
626
626
  "src/cli/updater.ts"() {
627
627
  "use strict";
628
628
  require2 = createRequire(import.meta.url);
629
- __dirname2 = path15.dirname(fileURLToPath(import.meta.url));
629
+ __dirname2 = path16.dirname(fileURLToPath(import.meta.url));
630
630
  REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
631
631
  }
632
632
  });
@@ -1849,6 +1849,41 @@ var AgentHarness = class {
1849
1849
  });
1850
1850
  this.emit(initialMsgEnd);
1851
1851
  yield initialMsgEnd;
1852
+ const MAX_TOOL_LOOP_ITERATIONS = 12;
1853
+ let toolLoopTurns = 0;
1854
+ while (!this.cancelled && !hadError && toolLoopTurns < MAX_TOOL_LOOP_ITERATIONS && initialFinishRef.value === "tool_calls") {
1855
+ toolLoopTurns++;
1856
+ const turnMessageId = crypto.randomUUID();
1857
+ const msgStart = createBrainEvent(
1858
+ "message_start",
1859
+ this.sessionId,
1860
+ { messageId: turnMessageId, role: "assistant" }
1861
+ );
1862
+ this.emit(msgStart);
1863
+ yield msgStart;
1864
+ let turnLength = 0;
1865
+ const turnFinishRef = { value: "stop" };
1866
+ const turnUsageRef = { value: null };
1867
+ for await (const ev of this.runSingleTurn(turnMessageId, turnFinishRef, turnUsageRef)) {
1868
+ if (ev.type === "message_delta") {
1869
+ turnLength += ev.delta.length;
1870
+ } else if (ev.type === "error") {
1871
+ if (ev.severity !== "cancelled") hadError = true;
1872
+ }
1873
+ yield ev;
1874
+ }
1875
+ totalLength += turnLength;
1876
+ const msgEnd = createBrainEvent("message_end", this.sessionId, {
1877
+ messageId: turnMessageId,
1878
+ totalLength: turnLength,
1879
+ finishReason: turnFinishRef.value,
1880
+ ...turnUsageRef.value ? { usage: turnUsageRef.value } : {}
1881
+ });
1882
+ this.emit(msgEnd);
1883
+ yield msgEnd;
1884
+ initialFinishRef.value = turnFinishRef.value;
1885
+ if (hadError || this.cancelled) break;
1886
+ }
1852
1887
  let turns = 0;
1853
1888
  while (turns < this.maxQueuedIterations) {
1854
1889
  if (this.cancelled) break;
@@ -1919,6 +1954,8 @@ var AgentHarness = class {
1919
1954
  });
1920
1955
  let toolCallsThisTurn = 0;
1921
1956
  const maxToolCalls = this.config.maxToolCallsPerTurn;
1957
+ let turnText = "";
1958
+ const turnToolCalls = [];
1922
1959
  for await (const delta of stream) {
1923
1960
  if (this.cancelled) {
1924
1961
  const cancelEvent = createBrainEvent("error", this.sessionId, {
@@ -1931,6 +1968,7 @@ var AgentHarness = class {
1931
1968
  break;
1932
1969
  }
1933
1970
  if (delta.kind === "text") {
1971
+ turnText += delta.delta;
1934
1972
  const deltaEvent = createBrainEvent(
1935
1973
  "message_delta",
1936
1974
  this.sessionId,
@@ -1947,6 +1985,7 @@ var AgentHarness = class {
1947
1985
  yield thinkEvent;
1948
1986
  } else if (delta.kind === "tool_call") {
1949
1987
  toolCallsThisTurn++;
1988
+ turnToolCalls.push({ id: delta.toolCallId, name: delta.toolName, args: delta.args });
1950
1989
  const toolStartEvent = createBrainEvent("tool_execution_start", this.sessionId, {
1951
1990
  toolCallId: delta.toolCallId,
1952
1991
  toolName: delta.toolName,
@@ -1966,18 +2005,24 @@ var AgentHarness = class {
1966
2005
  signal: this.activeController?.signal
1967
2006
  }
1968
2007
  );
2008
+ const resultStr = result.ok ? String(result.value) : result.error;
1969
2009
  const endEvent = createBrainEvent(
1970
2010
  "tool_execution_end",
1971
2011
  this.sessionId,
1972
2012
  {
1973
2013
  toolCallId: delta.toolCallId,
1974
- result: result.ok ? String(result.value) : result.error,
2014
+ result: resultStr,
1975
2015
  isError: !result.ok,
1976
2016
  durationMs: Date.now() - startMs
1977
2017
  }
1978
2018
  );
1979
2019
  this.emit(endEvent);
1980
2020
  yield endEvent;
2021
+ this.config.messages.push({
2022
+ role: "tool",
2023
+ toolCallId: delta.toolCallId,
2024
+ content: resultStr
2025
+ });
1981
2026
  } else if (skipped) {
1982
2027
  const endEvent = createBrainEvent(
1983
2028
  "tool_execution_end",
@@ -1991,9 +2036,21 @@ var AgentHarness = class {
1991
2036
  );
1992
2037
  this.emit(endEvent);
1993
2038
  yield endEvent;
2039
+ this.config.messages.push({
2040
+ role: "tool",
2041
+ toolCallId: delta.toolCallId,
2042
+ content: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`
2043
+ });
1994
2044
  }
1995
2045
  } else if (delta.kind === "finish") {
1996
2046
  finishRef.value = delta.reason;
2047
+ if (turnToolCalls.length > 0 || turnText.length > 0) {
2048
+ this.config.messages.push({
2049
+ role: "assistant",
2050
+ content: turnText,
2051
+ ...turnToolCalls.length > 0 ? { toolCalls: turnToolCalls } : {}
2052
+ });
2053
+ }
1997
2054
  break;
1998
2055
  } else if (delta.kind === "error") {
1999
2056
  const errEvent = createBrainEvent("error", this.sessionId, {
@@ -2245,9 +2302,33 @@ function resolveBaseUrl(providerId) {
2245
2302
  }
2246
2303
  function openaiCompatibleProvider(config2) {
2247
2304
  return async function* (params) {
2305
+ const messages = params.messages.map((m) => {
2306
+ if (m.role === "tool") {
2307
+ return {
2308
+ role: "tool",
2309
+ tool_call_id: m.toolCallId,
2310
+ content: m.content
2311
+ };
2312
+ }
2313
+ if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
2314
+ return {
2315
+ role: "assistant",
2316
+ content: m.content ?? "",
2317
+ tool_calls: m.toolCalls.map((tc) => ({
2318
+ id: tc.id,
2319
+ type: "function",
2320
+ function: {
2321
+ name: tc.name,
2322
+ arguments: JSON.stringify(tc.args ?? {})
2323
+ }
2324
+ }))
2325
+ };
2326
+ }
2327
+ return { role: m.role, content: m.content };
2328
+ });
2248
2329
  const body = {
2249
2330
  model: config2.model,
2250
- messages: params.messages.filter((m) => m.role !== "tool").map((m) => ({ role: m.role, content: m.content })),
2331
+ messages,
2251
2332
  stream: true,
2252
2333
  temperature: 0.7,
2253
2334
  // Task G.4.2 — request the provider to send real token usage in
@@ -2257,6 +2338,17 @@ function openaiCompatibleProvider(config2) {
2257
2338
  // the harness will fall back to the ~4-char/token approximation.
2258
2339
  stream_options: { include_usage: true }
2259
2340
  };
2341
+ if (params.tools && params.tools.length > 0) {
2342
+ body.tools = params.tools.map((t) => ({
2343
+ type: "function",
2344
+ function: {
2345
+ name: t.name,
2346
+ description: t.description,
2347
+ parameters: t.parameters
2348
+ }
2349
+ }));
2350
+ body.tool_choice = "auto";
2351
+ }
2260
2352
  let response;
2261
2353
  try {
2262
2354
  response = await fetch(`${config2.baseUrl}/chat/completions`, {
@@ -2339,6 +2431,9 @@ function openaiCompatibleProvider(config2) {
2339
2431
  }
2340
2432
  }
2341
2433
  }
2434
+ if (choice?.finish_reason) {
2435
+ yield { kind: "finish", reason: choice.finish_reason };
2436
+ }
2342
2437
  } catch {
2343
2438
  }
2344
2439
  }
@@ -2840,11 +2935,11 @@ var TOOL_DEFINITIONS = [
2840
2935
  execute: (args, ctx) => {
2841
2936
  if (!ctx.addDocument) return "Knowledge vault tool not available.";
2842
2937
  const title = args["title"] || "New Document";
2843
- const path17 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
2938
+ const path18 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
2844
2939
  const content = args["content"] || "";
2845
2940
  const tags = args["tags"] || [];
2846
2941
  ctx.addDocument({
2847
- path: path17,
2942
+ path: path18,
2848
2943
  title,
2849
2944
  content,
2850
2945
  format: "markdown",
@@ -2853,7 +2948,7 @@ var TOOL_DEFINITIONS = [
2853
2948
  workspaceId: ctx.workspaceId
2854
2949
  });
2855
2950
  ctx.addActivity("vault", "created document", title);
2856
- return `Document "${title}" created at "${path17}".`;
2951
+ return `Document "${title}" created at "${path18}".`;
2857
2952
  }
2858
2953
  }
2859
2954
  ];
@@ -5120,10 +5215,10 @@ function mergeDefs(...defs) {
5120
5215
  function cloneDef(schema) {
5121
5216
  return mergeDefs(schema._zod.def);
5122
5217
  }
5123
- function getElementAtPath(obj, path17) {
5124
- if (!path17)
5218
+ function getElementAtPath(obj, path18) {
5219
+ if (!path18)
5125
5220
  return obj;
5126
- return path17.reduce((acc, key) => acc?.[key], obj);
5221
+ return path18.reduce((acc, key) => acc?.[key], obj);
5127
5222
  }
5128
5223
  function promiseAllObject(promisesObj) {
5129
5224
  const keys = Object.keys(promisesObj);
@@ -5532,11 +5627,11 @@ function explicitlyAborted(x, startIndex = 0) {
5532
5627
  }
5533
5628
  return false;
5534
5629
  }
5535
- function prefixIssues(path17, issues) {
5630
+ function prefixIssues(path18, issues) {
5536
5631
  return issues.map((iss) => {
5537
5632
  var _a3;
5538
5633
  (_a3 = iss).path ?? (_a3.path = []);
5539
- iss.path.unshift(path17);
5634
+ iss.path.unshift(path18);
5540
5635
  return iss;
5541
5636
  });
5542
5637
  }
@@ -5683,16 +5778,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
5683
5778
  }
5684
5779
  function formatError(error51, mapper = (issue2) => issue2.message) {
5685
5780
  const fieldErrors = { _errors: [] };
5686
- const processError = (error52, path17 = []) => {
5781
+ const processError = (error52, path18 = []) => {
5687
5782
  for (const issue2 of error52.issues) {
5688
5783
  if (issue2.code === "invalid_union" && issue2.errors.length) {
5689
- issue2.errors.map((issues) => processError({ issues }, [...path17, ...issue2.path]));
5784
+ issue2.errors.map((issues) => processError({ issues }, [...path18, ...issue2.path]));
5690
5785
  } else if (issue2.code === "invalid_key") {
5691
- processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
5786
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
5692
5787
  } else if (issue2.code === "invalid_element") {
5693
- processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
5788
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
5694
5789
  } else {
5695
- const fullpath = [...path17, ...issue2.path];
5790
+ const fullpath = [...path18, ...issue2.path];
5696
5791
  if (fullpath.length === 0) {
5697
5792
  fieldErrors._errors.push(mapper(issue2));
5698
5793
  } else {
@@ -5719,17 +5814,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
5719
5814
  }
5720
5815
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
5721
5816
  const result = { errors: [] };
5722
- const processError = (error52, path17 = []) => {
5817
+ const processError = (error52, path18 = []) => {
5723
5818
  var _a3, _b;
5724
5819
  for (const issue2 of error52.issues) {
5725
5820
  if (issue2.code === "invalid_union" && issue2.errors.length) {
5726
- issue2.errors.map((issues) => processError({ issues }, [...path17, ...issue2.path]));
5821
+ issue2.errors.map((issues) => processError({ issues }, [...path18, ...issue2.path]));
5727
5822
  } else if (issue2.code === "invalid_key") {
5728
- processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
5823
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
5729
5824
  } else if (issue2.code === "invalid_element") {
5730
- processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
5825
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
5731
5826
  } else {
5732
- const fullpath = [...path17, ...issue2.path];
5827
+ const fullpath = [...path18, ...issue2.path];
5733
5828
  if (fullpath.length === 0) {
5734
5829
  result.errors.push(mapper(issue2));
5735
5830
  continue;
@@ -5761,8 +5856,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
5761
5856
  }
5762
5857
  function toDotPath(_path) {
5763
5858
  const segs = [];
5764
- const path17 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5765
- for (const seg of path17) {
5859
+ const path18 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5860
+ for (const seg of path18) {
5766
5861
  if (typeof seg === "number")
5767
5862
  segs.push(`[${seg}]`);
5768
5863
  else if (typeof seg === "symbol")
@@ -18454,13 +18549,13 @@ function resolveRef(ref, ctx) {
18454
18549
  if (!ref.startsWith("#")) {
18455
18550
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
18456
18551
  }
18457
- const path17 = ref.slice(1).split("/").filter(Boolean);
18458
- if (path17.length === 0) {
18552
+ const path18 = ref.slice(1).split("/").filter(Boolean);
18553
+ if (path18.length === 0) {
18459
18554
  return ctx.rootSchema;
18460
18555
  }
18461
18556
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
18462
- if (path17[0] === defsKey) {
18463
- const key = path17[1];
18557
+ if (path18[0] === defsKey) {
18558
+ const key = path18[1];
18464
18559
  if (!key || !ctx.defs[key]) {
18465
18560
  throw new Error(`Reference not found: ${ref}`);
18466
18561
  }
@@ -19073,8 +19168,83 @@ var grepContentTool = {
19073
19168
  }
19074
19169
  };
19075
19170
 
19076
- // src/cli/safety/sandboxPath.ts
19171
+ // src/main/core/tools/builtin/listFiles.ts
19172
+ import { promises as fs11 } from "node:fs";
19077
19173
  import path10 from "node:path";
19174
+ var ListFilesArgsSchema = external_exports.object({
19175
+ /** Directory to list (relative to cwd or absolute). Defaults to cwd. */
19176
+ path: external_exports.string().optional(),
19177
+ /** Max traversal depth. 1 = immediate children only (default). */
19178
+ maxDepth: external_exports.number().int().positive().max(10).default(1),
19179
+ /** Glob-style patterns to exclude (matched against each entry name). */
19180
+ exclude: external_exports.array(external_exports.string()).default([
19181
+ "node_modules",
19182
+ ".git",
19183
+ "dist",
19184
+ "build",
19185
+ ".next",
19186
+ "__pycache__",
19187
+ ".cache"
19188
+ ])
19189
+ });
19190
+ function matchesAny(name, patterns) {
19191
+ return patterns.some((p3) => {
19192
+ if (p3.includes("*")) {
19193
+ const regex = new RegExp("^" + p3.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$");
19194
+ return regex.test(name);
19195
+ }
19196
+ return name === p3;
19197
+ });
19198
+ }
19199
+ async function walk(dir, baseRel, depth, maxDepth, exclude, entries, signal) {
19200
+ if (depth > maxDepth) return;
19201
+ let dirents;
19202
+ try {
19203
+ dirents = await fs11.readdir(dir, { withFileTypes: true });
19204
+ } catch {
19205
+ return;
19206
+ }
19207
+ if (signal?.aborted) return;
19208
+ for (const dirent of dirents) {
19209
+ if (matchesAny(dirent.name, exclude)) continue;
19210
+ const rel = baseRel ? `${baseRel}/${dirent.name}` : dirent.name;
19211
+ const isDir = dirent.isDirectory();
19212
+ entries.push({
19213
+ name: rel,
19214
+ type: isDir ? "directory" : dirent.isFile() ? "file" : "other"
19215
+ });
19216
+ if (isDir && depth < maxDepth) {
19217
+ await walk(path10.join(dir, dirent.name), rel, depth + 1, maxDepth, exclude, entries, signal);
19218
+ }
19219
+ }
19220
+ }
19221
+ var listFilesTool = {
19222
+ name: "list_files",
19223
+ description: "List files and directories in the given path (defaults to the working directory). Returns names with types (file/directory). Use this to discover the project structure before reading specific files. Supports a maxDepth for recursive listing and excludes common dependency/build directories by default.",
19224
+ permissions: ["read"],
19225
+ timeoutMs: 15e3,
19226
+ inputSchema: ListFilesArgsSchema,
19227
+ execute: async (args, ctx) => {
19228
+ try {
19229
+ const target = args.path ? path10.isAbsolute(args.path) ? args.path : path10.join(ctx.cwd, args.path) : ctx.cwd;
19230
+ const entries = [];
19231
+ await walk(target, "", 1, args.maxDepth, args.exclude, entries, ctx.signal);
19232
+ entries.sort((a, b) => {
19233
+ if (a.type === "directory" && b.type !== "directory") return -1;
19234
+ if (a.type !== "directory" && b.type === "directory") return 1;
19235
+ return a.name.localeCompare(b.name);
19236
+ });
19237
+ const MAX_ENTRIES = 500;
19238
+ const truncated = entries.length > MAX_ENTRIES;
19239
+ return typedOk({ dir: target, entries: truncated ? entries.slice(0, MAX_ENTRIES) : entries, truncated });
19240
+ } catch (err) {
19241
+ return typedErr(err instanceof Error ? err.message : String(err));
19242
+ }
19243
+ }
19244
+ };
19245
+
19246
+ // src/cli/safety/sandboxPath.ts
19247
+ import path11 from "node:path";
19078
19248
  var SandboxViolationError = class extends Error {
19079
19249
  constructor(message, attemptedPath, resolvedPath) {
19080
19250
  super(message);
@@ -19087,9 +19257,9 @@ function resolveSandboxedPath(userPath, options = {}) {
19087
19257
  if (typeof userPath !== "string" || userPath.length === 0) {
19088
19258
  throw new SandboxViolationError("Empty path", userPath, "");
19089
19259
  }
19090
- const root = path10.resolve(options.root ?? process.cwd());
19091
- const resolved = path10.isAbsolute(userPath) ? path10.resolve(userPath) : path10.resolve(root, userPath);
19092
- const rootWithSep = root.endsWith(path10.sep) ? root : root + path10.sep;
19260
+ const root = path11.resolve(options.root ?? process.cwd());
19261
+ const resolved = path11.isAbsolute(userPath) ? path11.resolve(userPath) : path11.resolve(root, userPath);
19262
+ const rootWithSep = root.endsWith(path11.sep) ? root : root + path11.sep;
19093
19263
  if (resolved !== root && !resolved.startsWith(rootWithSep)) {
19094
19264
  throw new SandboxViolationError(
19095
19265
  `Path escapes sandbox root: ${userPath} \u2192 ${resolved} (root: ${root})`,
@@ -19152,8 +19322,8 @@ function assertShellAllowed(command) {
19152
19322
  }
19153
19323
 
19154
19324
  // src/cli/safety/auditLogger.ts
19155
- import { promises as fs11 } from "node:fs";
19156
- import path11 from "node:path";
19325
+ import { promises as fs12 } from "node:fs";
19326
+ import path12 from "node:path";
19157
19327
  import os7 from "node:os";
19158
19328
  var AuditLogger = class {
19159
19329
  logPath;
@@ -19172,8 +19342,8 @@ var AuditLogger = class {
19172
19342
  async append(entry) {
19173
19343
  const line = JSON.stringify(entry) + "\n";
19174
19344
  this.writeQueue = this.writeQueue.then(async () => {
19175
- await fs11.mkdir(path11.dirname(this.logPath), { recursive: true });
19176
- await fs11.appendFile(this.logPath, line, "utf-8");
19345
+ await fs12.mkdir(path12.dirname(this.logPath), { recursive: true });
19346
+ await fs12.appendFile(this.logPath, line, "utf-8");
19177
19347
  });
19178
19348
  return this.writeQueue;
19179
19349
  }
@@ -19216,7 +19386,7 @@ var AuditLogger = class {
19216
19386
  function defaultAuditPath() {
19217
19387
  const override = process.env.ANATHEMA_AUDIT_LOG;
19218
19388
  if (override && override.trim().length > 0) return override;
19219
- return path11.join(os7.tmpdir(), "zelari-code", "audit.jsonl");
19389
+ return path12.join(os7.tmpdir(), "zelari-code", "audit.jsonl");
19220
19390
  }
19221
19391
  function redactArgs(args) {
19222
19392
  const redacted = {};
@@ -19248,6 +19418,7 @@ function createBuiltinToolRegistry(options = {}) {
19248
19418
  const safeWriteFile = wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId);
19249
19419
  const safeEditFile = wrapWithSandbox(editFileTool, ["path"], root, audit, sessionId);
19250
19420
  const safeGrepContent = wrapWithSandbox(grepContentTool, ["path"], root, audit, sessionId);
19421
+ const safeListFiles = wrapWithSandbox(listFilesTool, ["path"], root, audit, sessionId);
19251
19422
  const safeBash = wrapWithShellSafety(bashTool, audit, sessionId);
19252
19423
  const registry3 = new ToolRegistry();
19253
19424
  registry3.register(safeReadFile);
@@ -19255,12 +19426,14 @@ function createBuiltinToolRegistry(options = {}) {
19255
19426
  registry3.register(safeEditFile);
19256
19427
  registry3.register(safeBash);
19257
19428
  registry3.register(safeGrepContent);
19429
+ registry3.register(safeListFiles);
19258
19430
  const tools = [
19259
19431
  safeReadFile,
19260
19432
  safeWriteFile,
19261
19433
  safeEditFile,
19262
19434
  safeBash,
19263
- safeGrepContent
19435
+ safeGrepContent,
19436
+ safeListFiles
19264
19437
  ].map((t) => ({
19265
19438
  name: t.name,
19266
19439
  description: t.description,
@@ -19377,7 +19550,7 @@ function redactForAudit(args) {
19377
19550
  // src/cli/gitOps.ts
19378
19551
  import { execFile } from "node:child_process";
19379
19552
  import { promisify } from "node:util";
19380
- import path12 from "node:path";
19553
+ import path13 from "node:path";
19381
19554
  var execFileAsync = promisify(execFile);
19382
19555
  async function git(cwd, args) {
19383
19556
  try {
@@ -19423,7 +19596,7 @@ async function undoWorkingChanges(opts = {}) {
19423
19596
  };
19424
19597
  }
19425
19598
  function defaultProjectRoot() {
19426
- return path12.resolve(__dirname, "..", "..", "..");
19599
+ return path13.resolve(__dirname, "..", "..", "..");
19427
19600
  }
19428
19601
 
19429
19602
  // src/cli/compaction.ts
@@ -19464,16 +19637,16 @@ function formatCompactionSummary(result) {
19464
19637
  }
19465
19638
 
19466
19639
  // src/cli/metrics.ts
19467
- import { promises as fs12, existsSync as existsSync7, statSync as statSync3, renameSync, appendFileSync, mkdirSync as mkdirSync7 } from "node:fs";
19468
- import path13 from "node:path";
19640
+ import { promises as fs13, existsSync as existsSync7, statSync as statSync3, renameSync, appendFileSync, mkdirSync as mkdirSync7 } from "node:fs";
19641
+ import path14 from "node:path";
19469
19642
  import os8 from "node:os";
19470
19643
  var METRICS_ROTATE_BYTES = 10 * 1024 * 1024;
19471
19644
  var MetricsLogger = class {
19472
19645
  file;
19473
19646
  writeQueue = Promise.resolve();
19474
19647
  constructor(file2) {
19475
- this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path13.join(os8.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
19476
- mkdirSync7(path13.dirname(this.file), { recursive: true });
19648
+ this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path14.join(os8.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
19649
+ mkdirSync7(path14.dirname(this.file), { recursive: true });
19477
19650
  }
19478
19651
  /** Fire-and-forget record append. */
19479
19652
  record(rec) {
@@ -19524,8 +19697,8 @@ function getMetricsLogger() {
19524
19697
  }
19525
19698
 
19526
19699
  // src/cli/skillHistory.ts
19527
- import { promises as fs13, existsSync as existsSync8, statSync as statSync4, renameSync as renameSync2, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "node:fs";
19528
- import path14 from "node:path";
19700
+ import { promises as fs14, existsSync as existsSync8, statSync as statSync4, renameSync as renameSync2, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "node:fs";
19701
+ import path15 from "node:path";
19529
19702
  import os9 from "node:os";
19530
19703
  import { randomUUID as randomUUID2 } from "node:crypto";
19531
19704
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
@@ -19534,8 +19707,8 @@ var SkillHistoryLogger = class {
19534
19707
  writeQueue = Promise.resolve();
19535
19708
  inflight = /* @__PURE__ */ new Map();
19536
19709
  constructor(file2) {
19537
- this.file = file2 ?? process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path14.join(os9.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
19538
- mkdirSync8(path14.dirname(this.file), { recursive: true });
19710
+ this.file = file2 ?? process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path15.join(os9.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
19711
+ mkdirSync8(path15.dirname(this.file), { recursive: true });
19539
19712
  }
19540
19713
  /**
19541
19714
  * Mark the start of a skill invocation. Returns a unique invocationId
@@ -19613,7 +19786,7 @@ var SkillHistoryLogger = class {
19613
19786
  async function readSkillHistory(file2) {
19614
19787
  let raw = "";
19615
19788
  try {
19616
- raw = await fs13.readFile(file2, "utf-8");
19789
+ raw = await fs14.readFile(file2, "utf-8");
19617
19790
  } catch {
19618
19791
  return [];
19619
19792
  }
@@ -19649,7 +19822,7 @@ function getSkillStats(records, skillId, sinceTs) {
19649
19822
  }
19650
19823
 
19651
19824
  // src/cli/app.tsx
19652
- import path16 from "node:path";
19825
+ import path17 from "node:path";
19653
19826
  import os10 from "node:os";
19654
19827
  var MODEL = process.env.OPENAI_MODEL ?? "grok-4";
19655
19828
  var PROVIDER = "openai-compatible";
@@ -19887,13 +20060,38 @@ function App() {
19887
20060
  primary: baseProviderStream,
19888
20061
  fallback: failoverResolution.fallback
19889
20062
  });
20063
+ const cwd = process.cwd();
20064
+ const toolList = toolRegistry.toOpenAITools().map((t) => `- ${t.name}: ${t.description}`).join("\n");
20065
+ const systemPrompt = [
20066
+ "You are Zelari Code, an interactive AI coding agent operating directly in the user's terminal.",
20067
+ "",
20068
+ "You ARE connected to this machine and have real tools to read, modify, and explore the codebase.",
20069
+ "Never claim you lack filesystem or shell access \u2014 you have it. Use your tools instead of asking the user to paste file contents.",
20070
+ "",
20071
+ `# Working Directory`,
20072
+ `You are running in: ${cwd}`,
20073
+ "All relative file paths are resolved against this directory. Always work with real files here.",
20074
+ "",
20075
+ "# Available Tools",
20076
+ "You can call these tools. Use them to take action and gather information autonomously:",
20077
+ toolList,
20078
+ "",
20079
+ "# Guidelines",
20080
+ "- To understand a project, list files (bash: ls) and read key files (read_file), don't ask the user to paste them.",
20081
+ "- Be proactive: explore, read, and act. Prefer doing over asking.",
20082
+ "- When you finish a task, briefly summarize what you did."
20083
+ ].join("\n");
19890
20084
  const harness = new AgentHarness({
19891
20085
  model: envConfig.model,
19892
20086
  provider: PROVIDER,
19893
- messages: [{ role: "user", content: userText }],
20087
+ messages: [
20088
+ { role: "system", content: systemPrompt },
20089
+ { role: "user", content: userText }
20090
+ ],
19894
20091
  tools: toolRegistry.toOpenAITools(),
19895
20092
  toolRegistry,
19896
- providerStream
20093
+ providerStream,
20094
+ cwd
19897
20095
  });
19898
20096
  harnessRef.current = harness;
19899
20097
  setQueueCount(harness.queueLength);
@@ -20418,9 +20616,9 @@ ${lines.join("\n")}`;
20418
20616
  if (result.kind === "promote_member" && result.promoteMemberId) {
20419
20617
  try {
20420
20618
  const { skill, markdown } = promoteMember(result.promoteMemberId);
20421
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path16.join(os10.homedir(), ".tmp", "zelari-code", "skills");
20619
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path17.join(os10.homedir(), ".tmp", "zelari-code", "skills");
20422
20620
  await fs.promises.mkdir(skillDir, { recursive: true });
20423
- const filePath = path16.join(skillDir, `${skill.id}.md`);
20621
+ const filePath = path17.join(skillDir, `${skill.id}.md`);
20424
20622
  await fs.promises.writeFile(filePath, markdown, "utf8");
20425
20623
  setMessages((prev) => [
20426
20624
  ...prev,
@@ -21150,7 +21348,7 @@ ${lines.join("\n")}`,
21150
21348
  return;
21151
21349
  }
21152
21350
  if (result.kind === "skill_stats") {
21153
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path16.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
21351
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path17.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
21154
21352
  try {
21155
21353
  const records = await readSkillHistory(historyFile);
21156
21354
  const stats = getSkillStats(records, result.skillStatsSkillId);
@@ -21179,7 +21377,7 @@ ${lines.join("\n")}`,
21179
21377
  setInput("");
21180
21378
  return;
21181
21379
  }
21182
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path16.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
21380
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path17.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
21183
21381
  try {
21184
21382
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
21185
21383
  setMessages((prev) => [