@hizliemre/horse-code 0.1.1 → 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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/{app-UGFQKMLX.js → app-2GPGCDX6.js} +136 -329
  3. package/dist/{chunk-2DGO2BUB.js → chunk-23CLQ2KO.js} +366 -600
  4. package/dist/{chunk-JWAEW7AJ.js → chunk-5ZV42XGJ.js} +2 -2
  5. package/dist/{chunk-2SVAHH5N.js → chunk-63E73TGI.js} +2 -4
  6. package/dist/{chunk-DTWKSZXY.js → chunk-6OSEQOYY.js} +2 -2
  7. package/dist/{chunk-SSDLHWSF.js → chunk-6W4UH2BQ.js} +33 -1
  8. package/dist/{chunk-3XVZXTB6.js → chunk-7JMWPTJ5.js} +1818 -384
  9. package/dist/{chunk-YILDXPSI.js → chunk-AE36LLL2.js} +49 -25
  10. package/dist/{chunk-FFYBY2NA.js → chunk-KAGKX2YT.js} +2 -4
  11. package/dist/{chunk-4EWK7HWQ.js → chunk-KKWZBZYK.js} +10 -0
  12. package/dist/{chunk-TOPZL5SU.js → chunk-LLL7QWXB.js} +41 -9
  13. package/dist/{chunk-7TBYMFMG.js → chunk-LNW557IO.js} +2 -2
  14. package/dist/{chunk-IW2KBAVZ.js → chunk-LPQU436C.js} +12 -1
  15. package/dist/chunk-M2RKCIGV.js +11 -0
  16. package/dist/{chunk-NNTIACT4.js → chunk-MRZVA5JB.js} +4 -4
  17. package/dist/{chunk-HBSC2HT2.js → chunk-UGESK765.js} +1 -1
  18. package/dist/{chunk-PGOYDOI4.js → chunk-XEGQT5EN.js} +3 -5
  19. package/dist/{chunk-FGVJFMK5.js → chunk-ZSQ24YDJ.js} +1 -1
  20. package/dist/cli.js +414 -100
  21. package/dist/{discover-5URG7C4J.js → discover-G2Z6XC3O.js} +9 -3
  22. package/dist/fix-JOIXQFVP.js +33 -0
  23. package/dist/git-QZTDZSJY.js +8 -0
  24. package/dist/{ongoing-OV5XROTU.js → ongoing-WHYXPW24.js} +4 -4
  25. package/dist/{project-graph-IOPCSZUA.js → project-graph-5HNPRFQG.js} +2 -3
  26. package/dist/{run-LQOZ5I7Z.js → run-P6ZYL5JL.js} +2 -3
  27. package/dist/{save-skills-NSLBU33X.js → save-skills-X7U3KCPU.js} +1 -1
  28. package/dist/{trace-ZMB7LT7W.js → trace-X6TU3AG6.js} +2 -3
  29. package/dist/{trace-adopt-C6TUWFJL.js → trace-adopt-URECQWJV.js} +2 -3
  30. package/dist/{trace-run-F23MFTY4.js → trace-run-7U4WJZ3V.js} +6 -7
  31. package/dist/{triage-2J3T5PVQ.js → triage-FCYHD2AQ.js} +8 -9
  32. package/dist/{verify-WQ3GHION.js → verify-6SC4I77M.js} +18 -19
  33. package/package.json +1 -1
  34. package/dist/chunk-B67BK5GQ.js +0 -34
  35. package/dist/chunk-F2IALVBU.js +0 -212
  36. package/dist/chunk-O74BDQKS.js +0 -28
  37. package/dist/fix-HBBOTUWM.js +0 -34
  38. package/dist/git-VTSZALSR.js +0 -6
@@ -1,3 +1,7 @@
1
+ import {
2
+ sessionBase
3
+ } from "./chunk-6W4UH2BQ.js";
4
+
1
5
  // src/tui/format.ts
2
6
  function fmtTokens(n) {
3
7
  if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B`;
@@ -443,22 +447,6 @@ function recallNote(tool, subject, turn, authored = false) {
443
447
  }
444
448
 
445
449
  // src/core/surrogates.ts
446
- var LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
447
- var REPLACEMENT = "\uFFFD";
448
- function stripLoneSurrogates(text) {
449
- LONE_SURROGATE.lastIndex = 0;
450
- return LONE_SURROGATE.test(text) ? text.replace(LONE_SURROGATE, REPLACEMENT) : text;
451
- }
452
- function sanitizeForJson(value) {
453
- if (typeof value === "string") return stripLoneSurrogates(value);
454
- if (Array.isArray(value)) return value.map((v) => sanitizeForJson(v));
455
- if (value && typeof value === "object") {
456
- const out = {};
457
- for (const [k, v] of Object.entries(value)) out[k] = sanitizeForJson(v);
458
- return out;
459
- }
460
- return value;
461
- }
462
450
  function truncateSafe(text, max) {
463
451
  if (text.length <= max || max <= 0) return text;
464
452
  const code = text.charCodeAt(max - 1);
@@ -983,15 +971,19 @@ function shieldToolOutput(text) {
983
971
  }
984
972
 
985
973
  // src/agent/loop.ts
974
+ var DELEGATED_TOOL_NAMES = { file_change: "Write", command_execution: "Bash" };
986
975
  function fmtChars(n) {
987
976
  return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k chars` : `${n} chars`;
988
977
  }
989
978
  function workingDirectoryNote(cwd) {
990
- return `
979
+ const here = `
991
980
 
992
981
  # Working directory
993
982
 
994
983
  You are already in \`${cwd}\`. Every relative path resolves from here, and every tool runs here \u2014 do not \`cd\` elsewhere, and do not go looking for the repository.`;
984
+ return sessionBase(cwd) === void 0 ? here : `${here}
985
+
986
+ This is a fresh git worktree, so everything git ignores is absent: no \`node_modules\`, no build output, no package caches. Source, tests and configuration are all here and current.`;
995
987
  }
996
988
  var COMPACT_KEYS_LOGGED = 24;
997
989
  var agentSeq = 0;
@@ -1067,6 +1059,16 @@ async function* runRoleAgent(opts) {
1067
1059
  toolCalls.push(ev.toolCall);
1068
1060
  } else if (ev.type === "tool-progress") {
1069
1061
  if (ev.path) opts.onLiveActivity?.(`writing ${ev.path.split("/").pop()} \xB7 ${fmtChars(ev.chars)}`);
1062
+ } else if (ev.type === "activity") {
1063
+ const what = ev.target ? ev.target.split("/").pop() ?? ev.target : "";
1064
+ opts.onActivity?.({
1065
+ tool: DELEGATED_TOOL_NAMES[ev.tool] ?? ev.tool,
1066
+ target: what,
1067
+ lines: 0,
1068
+ // Only a failure is worth a suffix. "done" on every row is the bullet said twice.
1069
+ summary: ev.ok === false ? "failed" : "",
1070
+ ok: ev.ok !== false
1071
+ });
1070
1072
  } else if (ev.type === "usage") {
1071
1073
  yield { type: "usage", promptTokens: ev.promptTokens, completionTokens: ev.completionTokens };
1072
1074
  opts.onUsage?.({ promptTokens: ev.promptTokens, completionTokens: ev.completionTokens, model: activeModel });
@@ -1082,7 +1084,7 @@ async function* runRoleAgent(opts) {
1082
1084
  break;
1083
1085
  }
1084
1086
  if (errored.retryable && !errored.capability && !errored.noBench) opts.onExhausted?.(activeModel, errored.message);
1085
- if (errored.retryable && !streamed && chainIdx < chain.length - 1) {
1087
+ if (errored.retryable && chainIdx < chain.length - 1) {
1086
1088
  const next = chain[chainIdx + 1];
1087
1089
  opts.onFallback?.(activeModel, next, errored.message);
1088
1090
  chainIdx++;
@@ -1240,7 +1242,33 @@ var ToolRegistry = class {
1240
1242
  }
1241
1243
  };
1242
1244
 
1245
+ // src/core/types.ts
1246
+ var DEADLINE_MESSAGE = "the model did not answer within its deadline";
1247
+ var CHAIN_BUDGET_MESSAGE = "the chain's total budget ran out before this model was given a fair turn";
1248
+
1243
1249
  // src/agent/structured.ts
1250
+ function valueAt(args, path) {
1251
+ let cur = args;
1252
+ for (const key of path) {
1253
+ if (typeof cur !== "object" || cur === null) return void 0;
1254
+ cur = cur[key];
1255
+ }
1256
+ return cur;
1257
+ }
1258
+ function whatWasWrong(issues, args) {
1259
+ return issues.map((i) => {
1260
+ const where = i.path.length ? i.path.join(".") : void 0;
1261
+ const got = valueAt(args, i.path);
1262
+ const shown = got === void 0 ? "nothing" : JSON.stringify(got);
1263
+ const head = where ? `${where}: ${i.message}` : i.message;
1264
+ if (got === void 0 && /received\s+(undefined|null|nothing)/i.test(i.message)) {
1265
+ const parent = i.path.length > 1 ? valueAt(args, i.path.slice(0, -1)) : args;
1266
+ const sent = parent && typeof parent === "object" ? Object.keys(parent) : [];
1267
+ return sent.length ? `${head} \u2014 you sent only ${sent.map((k) => `\`${k}\``).join(", ")}` : head;
1268
+ }
1269
+ return `${head} \u2014 got ${shown.length > 120 ? `${shown.slice(0, 120)}\u2026` : shown}`;
1270
+ }).join("; ");
1271
+ }
1244
1272
  function buildSubmitTool(schema) {
1245
1273
  let box;
1246
1274
  const tool = {
@@ -1251,10 +1279,7 @@ function buildSubmitTool(schema) {
1251
1279
  run: async (rawArgs) => {
1252
1280
  const parsed = schema.safeParse(rawArgs);
1253
1281
  if (!parsed.success) {
1254
- return {
1255
- content: `submit: invalid output: ${parsed.error.issues.map((i) => i.message).join("; ")}`,
1256
- isError: true
1257
- };
1282
+ return { content: `submit: invalid output: ${whatWasWrong(parsed.error.issues, rawArgs)}`, isError: true };
1258
1283
  }
1259
1284
  box = { value: parsed.data };
1260
1285
  return { content: "received", isError: false };
@@ -1309,7 +1334,7 @@ async function runStructuredRole(opts, schema, maxAttempts = 2) {
1309
1334
  }
1310
1335
  if (ev.type === "abort") {
1311
1336
  if (opts.signal.aborted) throw new Error("cancelled");
1312
- errored = total?.aborted ? "the chain's total budget ran out before this model was given a fair turn" : "the model did not answer within its deadline";
1337
+ errored = total?.aborted ? CHAIN_BUDGET_MESSAGE : DEADLINE_MESSAGE;
1313
1338
  break;
1314
1339
  }
1315
1340
  if (ev.type === "message.done") lastText = ev.message.content ?? lastText;
@@ -1342,13 +1367,12 @@ async function runStructuredRole(opts, schema, maxAttempts = 2) {
1342
1367
  }
1343
1368
 
1344
1369
  export {
1345
- sanitizeForJson,
1346
- truncateSafe,
1347
1370
  fmtTokens,
1348
1371
  fmtDuration,
1349
1372
  relTime,
1350
1373
  stripThinking,
1351
1374
  handedOver,
1375
+ truncateSafe,
1352
1376
  Telemetry,
1353
1377
  sampleMemory,
1354
1378
  estimateFreezeSeconds,
@@ -1,9 +1,7 @@
1
1
  import {
2
- stateRoot
3
- } from "./chunk-SSDLHWSF.js";
4
- import {
2
+ stateRoot,
5
3
  writeAtomic
6
- } from "./chunk-B67BK5GQ.js";
4
+ } from "./chunk-6W4UH2BQ.js";
7
5
 
8
6
  // src/engine/trace.ts
9
7
  import { readFile, writeFile, mkdir, rm } from "fs/promises";
@@ -18,6 +18,7 @@ var DEFAULT_CONFIG = {
18
18
  specKit: { version: "v0.13.2" },
19
19
  mcp: {},
20
20
  modelSources: [],
21
+ accounts: [],
21
22
  traceDir: "",
22
23
  /**
23
24
  * Shipped as a REFERENCE, not a copy, for the reasons skills/README.md gives for exactly this shape: it is
@@ -60,6 +61,14 @@ var fileSchema = z.object({
60
61
  council: z.object({ members: z.array(reviewerSchema) }).optional(),
61
62
  specKit: z.object({ version: z.string() }).optional(),
62
63
  modelSources: z.array(z.string()).optional(),
64
+ // Logged-in profile directories, in spill order. A path each, never a credential.
65
+ accounts: z.array(z.object({
66
+ kind: z.enum(["claude", "codex"]),
67
+ name: z.string(),
68
+ configDir: z.string(),
69
+ email: z.string().optional(),
70
+ plan: z.string().optional()
71
+ })).optional(),
63
72
  traceDir: z.string().optional(),
64
73
  // where /graph trace writes; empty = .horsecode/traces
65
74
  mainBranch: z.string().optional(),
@@ -103,6 +112,7 @@ function loadConfig(opts) {
103
112
  merged.roles = { ...global.roles ?? {}, ...projectSafe.roles ?? {} };
104
113
  merged.mcp = { ...global.mcp ?? {}, ...projectSafe.mcp ?? {} };
105
114
  merged.modelSources = projectSafe.modelSources ?? global.modelSources ?? [];
115
+ merged.accounts = global.accounts ?? [];
106
116
  merged.maxParallel = projectSafe.maxParallel ?? global.maxParallel ?? DEFAULT_CONFIG.maxParallel;
107
117
  merged.telemetry = projectSafe.telemetry ?? global.telemetry ?? DEFAULT_CONFIG.telemetry;
108
118
  const spoken = global.skillSources !== void 0 || projectSafe.skillSources !== void 0;
@@ -2,21 +2,21 @@ import {
2
2
  isMigrated,
3
3
  loadMigratedSync,
4
4
  migratedNotice
5
- } from "./chunk-2SVAHH5N.js";
5
+ } from "./chunk-63E73TGI.js";
6
6
  import {
7
7
  telemetry
8
- } from "./chunk-YILDXPSI.js";
8
+ } from "./chunk-AE36LLL2.js";
9
9
  import {
10
10
  readBriefSync
11
- } from "./chunk-DTWKSZXY.js";
11
+ } from "./chunk-6OSEQOYY.js";
12
12
  import {
13
13
  everTraceable,
14
14
  readTraceSync
15
- } from "./chunk-FFYBY2NA.js";
15
+ } from "./chunk-KAGKX2YT.js";
16
16
  import {
17
17
  areaOf,
18
18
  loadGraphSync
19
- } from "./chunk-PGOYDOI4.js";
19
+ } from "./chunk-XEGQT5EN.js";
20
20
 
21
21
  // src/tools/read.ts
22
22
  import { readFile } from "fs/promises";
@@ -83,6 +83,23 @@ async function sameNameElsewhere(cwd, asked) {
83
83
  if (!hits.length) return "";
84
84
  return hits.length === 1 ? ` There is one file named \`${base}\` in this project, at \`${hits[0]}\` \u2014 read that if it is what you meant.` : ` Files named \`${base}\` in this project: ${hits.map((h) => `\`${h}\``).join(", ")}.`;
85
85
  }
86
+ var MAX_SIBLINGS = 12;
87
+ async function whatIsInThatDirectory(cwd, asked) {
88
+ const slash = asked.lastIndexOf("/");
89
+ if (slash <= 0) return "";
90
+ const dir = asked.slice(0, slash);
91
+ try {
92
+ const { readdir: readdir2 } = await import("fs/promises");
93
+ const entries = await readdir2(resolve(cwd, dir), { withFileTypes: true });
94
+ const names = entries.filter((e) => !e.name.startsWith(".")).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
95
+ if (!names.length) return ` \`${dir}/\` exists but is empty.`;
96
+ const shown = names.slice(0, MAX_SIBLINGS).map((n) => `\`${n}\``).join(", ");
97
+ const rest = names.length - Math.min(names.length, MAX_SIBLINGS);
98
+ return ` \`${dir}/\` does exist, and holds: ${shown}${rest > 0 ? `, and ${rest} more` : ""}.`;
99
+ } catch {
100
+ return "";
101
+ }
102
+ }
86
103
  function numbered(lines, startLine) {
87
104
  const width = String(startLine + lines.length - 1).length;
88
105
  return lines.map((l, i) => `${String(startLine + i).padStart(width, " ")} ${l}`).join("\n");
@@ -132,8 +149,12 @@ var readFileTool = {
132
149
  isError: true
133
150
  };
134
151
  }
135
- const elsewhere = said.includes("ENOENT") ? await sameNameElsewhere(ctx.cwd, args.path) : "";
136
- return { content: `read_file error: ${said}${elsewhere}`, isError: true };
152
+ let hint = "";
153
+ if (said.includes("ENOENT")) {
154
+ hint = await sameNameElsewhere(ctx.cwd, args.path);
155
+ if (!hint) hint = await whatIsInThatDirectory(ctx.cwd, args.path);
156
+ }
157
+ return { content: `read_file error: ${said}${hint}`, isError: true };
137
158
  }
138
159
  const all = raw.split("\n");
139
160
  if (args.offset === void 0 && args.limit === void 0 && raw.length <= MAX_READ_CHARS) {
@@ -927,9 +948,20 @@ ${core.join("\n")}`;
927
948
  );
928
949
  var graphTraceTool = {
929
950
  name: "graph_trace",
930
- description: `What a source file is responsible for and what to be careful of when changing it, in the product's terms. Far cheaper than reading the file. Use it to orient before opening unfamiliar code. Pass "project" instead of a path to get the project brief: what the product is, its domain vocabulary, and the business rules the code must not violate. Read that FIRST in an unfamiliar codebase.`,
951
+ /**
952
+ * The limit is stated HERE because this is the only place an agent reads before calling.
953
+ *
954
+ * Measured live in one run: four different lenses — `risk-judge`, `plan-observability`,
955
+ * `plan-architecture` and one more — each asked for a trace of `spec.md` or `plan.md`. The error they got
956
+ * back is a good one and says the failure is permanent, so none of them asked twice; but each learned it
957
+ * separately, at a turn apiece, because a lens has its own memo and cannot be told by the last one. An
958
+ * error teaches one agent after the fact. A description tells every agent before.
959
+ */
960
+ description: `What a source file is responsible for and what to be careful of when changing it, in the product's terms. Far cheaper than reading the file. Use it to orient before opening unfamiliar code. SOURCE CODE ONLY (.ts, .cs, .py, .go, \u2026): a .md, .json or .txt path has no trace and never will \u2014 read documents with read_file. Pass "project" instead of a path to get the project brief: what the product is, its domain vocabulary, and the business rules the code must not violate. Read that FIRST in an unfamiliar codebase.`,
931
961
  permissionLevel: "safe",
932
- parameters: z4.object({ file: z4.string().describe('Repo-relative path, e.g. "src/config/config.ts"') }),
962
+ parameters: z4.object({
963
+ file: z4.string().describe('Repo-relative path to SOURCE code, e.g. "src/config/config.ts". Not a document.')
964
+ }),
933
965
  describe: (args) => ({ allowKey: "graph:trace", preview: `graph_trace ${JSON.stringify(args)}`.slice(0, 120) }),
934
966
  async run(args, ctx) {
935
967
  const file = String(args.file ?? "");
@@ -5,11 +5,11 @@ import {
5
5
  memoryHints,
6
6
  readFileTool,
7
7
  reinforceUsed
8
- } from "./chunk-TOPZL5SU.js";
8
+ } from "./chunk-LLL7QWXB.js";
9
9
  import {
10
10
  ToolRegistry,
11
11
  runStructuredRole
12
- } from "./chunk-YILDXPSI.js";
12
+ } from "./chunk-AE36LLL2.js";
13
13
 
14
14
  // src/engine/triage.ts
15
15
  import { z } from "zod";
@@ -15,7 +15,18 @@ var defaultGitRunner = (args, cwd) => new Promise((resolve) => {
15
15
  child.on("error", (e) => resolve({ stdout, stderr: stderr + e.message, code: -1 }));
16
16
  child.on("close", (code) => resolve({ stdout, stderr, code: code ?? -1 }));
17
17
  });
18
+ var TAKES_A_VALUE = /* @__PURE__ */ new Set(["-c", "-C", "--git-dir", "--work-tree", "--namespace", "--exec-path", "--config-env"]);
19
+ function gitVerb(args) {
20
+ for (let i = 0; i < args.length; i++) {
21
+ const a = args[i];
22
+ if (a === void 0) continue;
23
+ if (!a.startsWith("-")) return a;
24
+ if (TAKES_A_VALUE.has(a)) i++;
25
+ }
26
+ return void 0;
27
+ }
18
28
 
19
29
  export {
20
- defaultGitRunner
30
+ defaultGitRunner,
31
+ gitVerb
21
32
  };
@@ -0,0 +1,11 @@
1
+ // src/engine/language.ts
2
+ function respondIn(language) {
3
+ if (!language || /^english$/i.test(language)) return "";
4
+ return `
5
+
6
+ Respond to the user in ${language}: everything you say to them, and every question you ask, is in ${language}. Code, identifiers, logs and commit messages keep whatever language the project uses.`;
7
+ }
8
+
9
+ export {
10
+ respondIn
11
+ };
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  defaultGitRunner
3
- } from "./chunk-IW2KBAVZ.js";
3
+ } from "./chunk-LPQU436C.js";
4
4
  import {
5
5
  briefForPrompt,
6
6
  briefPrompt,
7
7
  briefStatus,
8
8
  gatherBriefInput,
9
9
  saveBrief
10
- } from "./chunk-DTWKSZXY.js";
10
+ } from "./chunk-6OSEQOYY.js";
11
11
  import {
12
12
  ensureGitignore,
13
13
  loadTraceIndex,
@@ -19,10 +19,10 @@ import {
19
19
  tracePrompt,
20
20
  traceRootRel,
21
21
  traceable
22
- } from "./chunk-FFYBY2NA.js";
22
+ } from "./chunk-KAGKX2YT.js";
23
23
  import {
24
24
  loadGraph
25
- } from "./chunk-PGOYDOI4.js";
25
+ } from "./chunk-XEGQT5EN.js";
26
26
 
27
27
  // src/engine/trace-run.ts
28
28
  async function traceableFiles(cwd, opts) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ToolRegistry,
3
3
  runStructuredRole
4
- } from "./chunk-YILDXPSI.js";
4
+ } from "./chunk-AE36LLL2.js";
5
5
 
6
6
  // src/engine/user-language.ts
7
7
  import { z } from "zod";
@@ -1,10 +1,8 @@
1
1
  import {
2
2
  stateRoot,
3
- writableStateRoot
4
- } from "./chunk-SSDLHWSF.js";
5
- import {
3
+ writableStateRoot,
6
4
  writeAtomic
7
- } from "./chunk-B67BK5GQ.js";
5
+ } from "./chunk-6W4UH2BQ.js";
8
6
 
9
7
  // src/engine/project-graph.ts
10
8
  import { readFile, stat } from "fs/promises";
@@ -302,7 +300,7 @@ function pruneTooling(doc, alsoDrop = /* @__PURE__ */ new Set()) {
302
300
  async function writtenTracePaths(cwd) {
303
301
  const out = /* @__PURE__ */ new Set();
304
302
  try {
305
- const { loadTraceIndex, traceRootRel } = await import("./trace-ZMB7LT7W.js");
303
+ const { loadTraceIndex, traceRootRel } = await import("./trace-X6TU3AG6.js");
306
304
  const index = await loadTraceIndex(cwd);
307
305
  const root = traceRootRel().replace(/\\/g, "/");
308
306
  for (const [file, rec] of Object.entries(index.traces)) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  writeAtomicSync
3
- } from "./chunk-B67BK5GQ.js";
3
+ } from "./chunk-6W4UH2BQ.js";
4
4
 
5
5
  // src/engine/checkpoint.ts
6
6
  import { existsSync, readFileSync, rmSync, statSync } from "fs";