@hizliemre/horse-code 0.2.0 → 0.3.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.
@@ -4,6 +4,11 @@ import {
4
4
  isContinuePrompt,
5
5
  readCheckpoint
6
6
  } from "./chunk-ZSQ24YDJ.js";
7
+ import {
8
+ CLI_KINDS,
9
+ SYNTHETIC,
10
+ runCliAgent
11
+ } from "./chunk-G45RWL7S.js";
7
12
  import {
8
13
  planFor,
9
14
  runTraces
@@ -49,70 +54,34 @@ import {
49
54
  writableStateRoot
50
55
  } from "./chunk-6W4UH2BQ.js";
51
56
 
52
- // src/agents/cli-auth.ts
53
- import { spawnSync } from "child_process";
54
- function profileEnv(kind, configDir) {
55
- if (!configDir) return {};
56
- return kind === "claude" ? { CLAUDE_CONFIG_DIR: configDir } : { CODEX_HOME: configDir };
57
- }
58
- function readAuthStatus(kind, out) {
59
- if (kind === "codex") {
60
- const m = /logged in(?: using (.+))?/i.exec(out);
61
- if (!m || /not logged in/i.test(out)) return { loggedIn: false };
62
- const plan = m[1]?.trim();
63
- return { loggedIn: true, ...plan ? { plan } : {} };
64
- }
65
- try {
66
- const j = JSON.parse(out);
67
- if (!j.loggedIn) return { loggedIn: false };
68
- return {
69
- loggedIn: true,
70
- ...j.email ? { email: j.email } : {},
71
- ...j.subscriptionType ? { plan: j.subscriptionType } : {}
72
- };
73
- } catch {
74
- return { loggedIn: false };
75
- }
76
- }
77
- function statusArgs(kind) {
78
- return kind === "claude" ? ["auth", "status"] : ["login", "status"];
79
- }
80
- function loginArgs(kind) {
81
- return kind === "claude" ? ["auth", "login"] : ["login"];
82
- }
83
- function checkProfile(kind, configDir) {
84
- const r = spawnSync(kind, statusArgs(kind), {
85
- env: { ...process.env, ...profileEnv(kind, configDir) },
86
- encoding: "utf8",
87
- // A status check that hangs must not hang the startup summary with it.
88
- timeout: 2e4
89
- });
90
- if (r.error) return { loggedIn: false };
91
- return readAuthStatus(kind, `${r.stdout ?? ""}${r.stderr ?? ""}`);
92
- }
93
- function runLogin(kind, configDir) {
94
- const r = spawnSync(kind, loginArgs(kind), {
95
- env: { ...process.env, ...profileEnv(kind, configDir) },
96
- stdio: "inherit"
97
- });
98
- if (r.error) {
99
- const e = r.error;
100
- return e.code === "ENOENT" ? { ok: false, error: `\`${kind}\` is not installed, or not on PATH` } : { ok: false, error: e.message };
101
- }
102
- return { ok: r.status === 0 };
103
- }
104
-
105
57
  // src/agents/cli-models.ts
106
58
  var CLAUDE_MODELS = ["fable", "opus", "sonnet", "haiku"];
107
59
  var CODEX_MODELS = ["gpt-5.6-terra", "gpt-5.6-sol", "gpt-5.6-luna"];
108
60
  var CODEX_DEFAULT = "gpt-5.6-terra";
61
+ var GROK_MODELS = ["grok-4.6", "grok-4.5"];
62
+ function grokEffort(effort) {
63
+ const e = effort.toLowerCase();
64
+ if (e === "xhigh" || e === "high" || e === "medium" || e === "low") return e;
65
+ if (e === "max" || e === "ultra") return "xhigh";
66
+ if (e === "minimal") return "low";
67
+ return void 0;
68
+ }
69
+ var ZAI_MODELS = ["glm-5.3", "glm-5.3-flash"];
70
+ function modelsFor(kind) {
71
+ if (kind === "claude") return CLAUDE_MODELS;
72
+ if (kind === "codex") return CODEX_MODELS;
73
+ if (kind === "grok") return GROK_MODELS;
74
+ return ZAI_MODELS;
75
+ }
109
76
  function cliCatalog() {
110
- return [...CLAUDE_MODELS, ...CODEX_MODELS];
77
+ return CLI_KINDS.flatMap((k) => [...modelsFor(k)]);
111
78
  }
112
79
  function cliFor(model) {
113
80
  const m = model.toLowerCase().replace(/^no-think\//, "").replace(/^(cc|claude|cx|codex)\//, "");
114
81
  if (/^(fable|opus|sonnet|haiku)\b/.test(m) || m.startsWith("claude")) return "claude";
115
82
  if (/^(codex|gpt|o[0-9])\b/.test(m)) return "codex";
83
+ if (/^grok(-|$)/.test(m)) return "grok";
84
+ if (/^glm(-|$)/.test(m)) return "zai";
116
85
  return void 0;
117
86
  }
118
87
  function cliInvocation(model) {
@@ -126,204 +95,6 @@ function cliInvocation(model) {
126
95
  };
127
96
  }
128
97
 
129
- // src/agents/cli-agent.ts
130
- import { spawn } from "child_process";
131
- function cliArgs(kind, prompt, extra = []) {
132
- return kind === "claude" ? ["--output-format", "stream-json", "--verbose", ...extra, "-p", "--", prompt] : ["exec", "--json", "--skip-git-repo-check", ...extra, "--", prompt];
133
- }
134
- function decodeClaudeEvent(line) {
135
- let e;
136
- try {
137
- e = JSON.parse(line);
138
- } catch {
139
- return void 0;
140
- }
141
- const type = e.type;
142
- if (type === "rate_limit_event") {
143
- const info = e.rate_limit_info ?? {};
144
- const status = String(info.status ?? "unknown");
145
- const raw = info.unifiedWindows ?? {};
146
- const windows = {};
147
- for (const [name, w] of Object.entries(raw)) windows[name] = w?.utilization ?? 0;
148
- const quota = {
149
- status,
150
- windows,
151
- ...typeof info.resetsAt === "number" ? { resetsAt: info.resetsAt } : {}
152
- };
153
- return status.startsWith("allowed") ? { quota } : {
154
- quota,
155
- /**
156
- * The reset time rides along, as an ISO instant rather than prose.
157
- *
158
- * A spent five-hour window reopens; without saying when, the only safe bench is "the rest of the
159
- * run", which on a ten-hour board writes off a subscription for hours after it recovered. The
160
- * gateway's wordings said "reset after 4h" and nothing ever parsed them — see `quotaResetAt`.
161
- */
162
- rateLimited: `${status} \u2014 ${describeWindows(windows)}` + (quota.resetsAt ? ` (resets ${new Date(quota.resetsAt * 1e3).toISOString()})` : "")
163
- };
164
- }
165
- if (type === "assistant") {
166
- const msg = e.message;
167
- const parts = Array.isArray(msg?.content) ? msg.content : [];
168
- const text = parts.filter((b) => typeof b === "object" && b !== null && b.type === "text").map((b) => b.text).join("");
169
- const tool = parts.find((b) => typeof b === "object" && b !== null && b.type === "tool_use");
170
- return {
171
- ...text ? { text } : {},
172
- ...msg?.model ? { served: msg.model } : {},
173
- ...tool ? { tool: { name: tool.name, ...targetOf(tool.input) ? { target: targetOf(tool.input) } : {} } } : {}
174
- };
175
- }
176
- if (type === "user") {
177
- const parts = e.message?.content;
178
- const failed = (Array.isArray(parts) ? parts : []).find(
179
- (b) => typeof b === "object" && b !== null && b.type === "tool_result" && b.is_error === true
180
- );
181
- return failed ? { tool: { name: "tool", ok: false } } : void 0;
182
- }
183
- if (type === "result") {
184
- const u = e.usage ?? {};
185
- const cost = e.total_cost_usd;
186
- return {
187
- usage: {
188
- freshTokens: u.input_tokens ?? 0,
189
- cachedTokens: u.cache_read_input_tokens ?? 0,
190
- cacheWriteTokens: u.cache_creation_input_tokens ?? 0,
191
- outputTokens: u.output_tokens ?? 0,
192
- ...cost !== void 0 ? { costUsd: cost } : {}
193
- },
194
- ...e.subtype === "error_during_execution" ? { error: String(e.result ?? "the CLI reported an error") } : {}
195
- };
196
- }
197
- return void 0;
198
- }
199
- function decodeCodexEvent(line) {
200
- let e;
201
- try {
202
- e = JSON.parse(line);
203
- } catch {
204
- return void 0;
205
- }
206
- const type = String(e.type ?? "");
207
- if (/rate.?limit/i.test(type)) return { rateLimited: String(e.message ?? "rate limited by the CLI") };
208
- if (type === "item.completed") {
209
- const item = e.item;
210
- if (item?.type === "agent_message" && item.text) return { text: item.text };
211
- if (item?.type && item.type !== "agent_message") {
212
- const changes = item.changes;
213
- const first = Array.isArray(changes) ? changes.find((c) => typeof c?.path === "string")?.path : void 0;
214
- const more = Array.isArray(changes) && changes.length > 1 ? ` +${changes.length - 1}` : "";
215
- return {
216
- tool: {
217
- name: item.name ?? item.type,
218
- ...first ? { target: `${first}${more}` } : {}
219
- }
220
- };
221
- }
222
- return void 0;
223
- }
224
- if (type === "turn.completed") {
225
- const u = e.usage ?? {};
226
- return {
227
- usage: {
228
- freshTokens: u.input_tokens ?? 0,
229
- cachedTokens: u.cached_input_tokens ?? 0,
230
- cacheWriteTokens: u.cache_write_input_tokens ?? 0,
231
- outputTokens: u.output_tokens ?? 0
232
- }
233
- };
234
- }
235
- if (type === "turn.failed" || type === "error") {
236
- return { error: String(e.message ?? "codex reported an error") };
237
- }
238
- return void 0;
239
- }
240
- function describeWindows(windows) {
241
- const parts = Object.entries(windows).map(([k, v]) => `${k} ${Math.round(v * 100)}%`);
242
- return parts.length ? parts.join(", ") : "no window reported";
243
- }
244
- var SYNTHETIC = "<synthetic>";
245
- function targetOf(input) {
246
- for (const k of ["file_path", "path", "filePath", "notebook_path"]) {
247
- const v = input?.[k];
248
- if (typeof v === "string" && v) return v;
249
- }
250
- return void 0;
251
- }
252
- function makeStreamReader(decode, onEvent) {
253
- let pending = "";
254
- const drain = (upToNewline) => {
255
- const lines = pending.split("\n");
256
- pending = upToNewline ? lines.pop() ?? "" : "";
257
- for (const line of lines) {
258
- if (!line.trim()) continue;
259
- const ev = decode(line);
260
- if (ev) onEvent(ev);
261
- }
262
- };
263
- return {
264
- push(chunk) {
265
- pending += chunk;
266
- drain(true);
267
- },
268
- end() {
269
- if (pending.trim()) drain(false);
270
- }
271
- };
272
- }
273
- async function runCliAgent(run) {
274
- const decode = run.kind === "claude" ? decodeClaudeEvent : decodeCodexEvent;
275
- const args = cliArgs(run.kind, run.prompt, run.args ?? []);
276
- return new Promise((resolve6) => {
277
- let child;
278
- try {
279
- child = spawn(run.kind, args, {
280
- cwd: run.cwd,
281
- signal: run.signal,
282
- stdio: ["ignore", "pipe", "pipe"],
283
- ...run.configDir ? { env: { ...process.env, ...profileEnv(run.kind, run.configDir) } } : {}
284
- });
285
- } catch (e) {
286
- resolve6({ text: "", error: e instanceof Error ? e.message : String(e), exitCode: -1 });
287
- return;
288
- }
289
- let text = "";
290
- let usage;
291
- let rateLimited;
292
- let served;
293
- let quota;
294
- let error;
295
- let stderr = "";
296
- const reader = makeStreamReader(decode, (ev) => {
297
- if (ev.text) text += ev.text;
298
- if (ev.usage) usage = ev.usage;
299
- if (ev.rateLimited) rateLimited = ev.rateLimited;
300
- if (ev.served) served = ev.served;
301
- if (ev.quota) quota = ev.quota;
302
- if (ev.error) error = ev.error;
303
- run.onEvent?.(ev);
304
- });
305
- child.stdout?.on("data", (d) => reader.push(d.toString()));
306
- child.stderr?.on("data", (d) => {
307
- stderr += d.toString();
308
- });
309
- child.on("error", (e) => resolve6({ text, ...usage ? { usage } : {}, error: e.message, exitCode: -1 }));
310
- child.on("close", (code) => {
311
- reader.end();
312
- resolve6({
313
- text,
314
- ...usage ? { usage } : {},
315
- ...rateLimited ? { rateLimited } : {},
316
- ...quota ? { quota } : {},
317
- ...served ? { served } : {},
318
- // stderr only becomes the error when nothing better was said — a CLI that warns on stderr and
319
- // succeeds must not be read as having failed.
320
- ...error ?? (code !== 0 && stderr.trim()) ? { error: error ?? stderr.trim().slice(0, 500) } : {},
321
- exitCode: code ?? -1
322
- });
323
- });
324
- });
325
- }
326
-
327
98
  // src/agent/deadline.ts
328
99
  function withDeadline(work, signal, message) {
329
100
  work.catch(() => {
@@ -369,7 +140,7 @@ ${m.content}` : m.content);
369
140
  return parts.join("\n\n");
370
141
  }
371
142
  function isLoggedOut(text) {
372
- return /not logged in|please run \/login/i.test(text);
143
+ return /not logged in|not signed in|please run \/login/i.test(text);
373
144
  }
374
145
  async function* streamWhileRunning(start) {
375
146
  const queue = [];
@@ -427,11 +198,17 @@ var CliProvider = class {
427
198
  const { model, effort: named } = cliInvocation(req.model);
428
199
  if (model) args.push("--model", model);
429
200
  const effort = req.effort ?? named;
430
- if (effort && kind === "claude") args.push("--effort", effort);
431
- if (this.readOnly && kind === "claude") args.push("--disallowed-tools", "Write", "Edit", "NotebookEdit");
201
+ if (effort && (kind === "claude" || kind === "zai")) args.push("--effort", effort);
202
+ if (effort && kind === "grok") {
203
+ const level = grokEffort(effort);
204
+ if (level) args.push("--reasoning-effort", level);
205
+ }
206
+ if (this.readOnly && (kind === "claude" || kind === "zai")) args.push("--disallowed-tools", "Write", "Edit", "NotebookEdit");
432
207
  if (this.readOnly && kind === "codex") args.push("--sandbox", "read-only");
433
- if (!this.readOnly && kind === "claude") args.push("--permission-mode", "acceptEdits");
208
+ if (this.readOnly && kind === "grok") args.push("--disallowed-tools", "write,search_replace");
209
+ if (!this.readOnly && (kind === "claude" || kind === "zai")) args.push("--permission-mode", "acceptEdits");
434
210
  if (!this.readOnly && kind === "codex") args.push("--sandbox", "workspace-write");
211
+ if (!this.readOnly && kind === "grok") args.push("--permission-mode", "acceptEdits");
435
212
  const account = this.accounts?.pick(kind);
436
213
  let res;
437
214
  yield* streamWhileRunning((push) => runCliAgent({
@@ -471,6 +248,14 @@ var CliProvider = class {
471
248
  return;
472
249
  }
473
250
  if (res.error && !res.text.trim()) {
251
+ if (isLoggedOut(res.error)) {
252
+ yield {
253
+ type: "error",
254
+ retryable: true,
255
+ message: `${kind} CLI is not logged in${account ? ` under profile "${account.name}"` : ""} \u2014 run \`hcode add-provider ${kind}\` to sign it in again`
256
+ };
257
+ return;
258
+ }
474
259
  yield { type: "error", message: `${kind} CLI: ${res.error}`, retryable: res.exitCode !== 0 };
475
260
  return;
476
261
  }
@@ -1328,6 +1113,8 @@ function capabilityScore(model) {
1328
1113
  if (/opus/.test(s)) return 88 + versionBump(s, "opus");
1329
1114
  if (/codex|gpt-5|\bo3\b/.test(s)) return 82 + effortBump(s) + versionBump(s, "gpt") / 100;
1330
1115
  if (/sonnet/.test(s)) return 78 + versionBump(s, "sonnet");
1116
+ if (/grok/.test(s)) return 78 + versionBump(s, "grok");
1117
+ if (/glm/.test(s)) return 78 + versionBump(s, "glm");
1331
1118
  if (/gemini/.test(s) && /pro/.test(s)) return 76 + versionBump(s, "gemini") + effortBump(s);
1332
1119
  if (/gpt-4/.test(s)) return 65;
1333
1120
  if (/deepseek/.test(s)) return 55;
@@ -2988,7 +2775,7 @@ var editFileTool = {
2988
2775
  };
2989
2776
 
2990
2777
  // src/tools/shell.ts
2991
- import { spawn as spawn2 } from "child_process";
2778
+ import { spawn } from "child_process";
2992
2779
  import { resolve as resolve5, sep as sep6 } from "path";
2993
2780
  import { z as z11 } from "zod";
2994
2781
  var params8 = z11.object({
@@ -3105,7 +2892,7 @@ var shellTool = {
3105
2892
  return new Promise((resolvePromise) => {
3106
2893
  let child;
3107
2894
  try {
3108
- child = spawn2(a.command, {
2895
+ child = spawn(a.command, {
3109
2896
  cwd: ctx.cwd,
3110
2897
  shell: true,
3111
2898
  signal: ctx.signal,
@@ -4209,7 +3996,7 @@ function canonicalSource(name) {
4209
3996
  return sourcePrefix(s) ?? s;
4210
3997
  }
4211
3998
  function providerOutage(reason) {
4212
- return /no active credentials for provider:?\s*([\w.-]+)/i.exec(reason)?.[1] ?? /provider\s+'?([\w.-]+)'?\s+is not configured/i.exec(reason)?.[1] ?? /all\s+([\w.-]+)\s+accounts have exhausted their quota/i.exec(reason)?.[1] ?? /shared egress ip quota exhausted\s*\(([\w.-]+)\)/i.exec(reason)?.[1] ?? /^\s*(claude|codex)\s+CLI:\s*rejected\b/i.exec(reason)?.[1]?.toLowerCase();
3999
+ return /no active credentials for provider:?\s*([\w.-]+)/i.exec(reason)?.[1] ?? /provider\s+'?([\w.-]+)'?\s+is not configured/i.exec(reason)?.[1] ?? /all\s+([\w.-]+)\s+accounts have exhausted their quota/i.exec(reason)?.[1] ?? /shared egress ip quota exhausted\s*\(([\w.-]+)\)/i.exec(reason)?.[1] ?? /^\s*(claude|codex|grok|zai)\s+CLI:\s*rejected\b/i.exec(reason)?.[1]?.toLowerCase();
4213
4000
  }
4214
4001
  function quotaResetAt(reason) {
4215
4002
  const iso = /\(resets\s+([0-9T:.\-]+Z)\)/i.exec(reason)?.[1];
@@ -5193,7 +4980,7 @@ async function runCodeReview(deps, workdir, taskTitle, request, emit = () => {
5193
4980
  import { z as z18 } from "zod";
5194
4981
 
5195
4982
  // src/engine/criterion-commands.ts
5196
- import { spawn as spawn3 } from "child_process";
4983
+ import { spawn as spawn2 } from "child_process";
5197
4984
  var RUNNABLE_COMMANDS = [
5198
4985
  "dotnet",
5199
4986
  "npm",
@@ -5229,7 +5016,7 @@ async function runCommand(cwd, argv, timeoutMs = CRITERION_TIMEOUT_MS) {
5229
5016
  return new Promise((resolve6) => {
5230
5017
  let child;
5231
5018
  try {
5232
- child = spawn3(bin, args, { cwd, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, CI: "1" } });
5019
+ child = spawn2(bin, args, { cwd, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, CI: "1" } });
5233
5020
  } catch (e) {
5234
5021
  resolve6({ argv, passed: false, exitCode: null, timedOut: false, output: e instanceof Error ? e.message : String(e) });
5235
5022
  return;
@@ -5288,7 +5075,7 @@ ${r.output.slice(-800)}
5288
5075
  // src/engine/test-runner.ts
5289
5076
  import { readFile as readFile3 } from "fs/promises";
5290
5077
  import { existsSync as existsSync9 } from "fs";
5291
- import { spawn as spawn4 } from "child_process";
5078
+ import { spawn as spawn3 } from "child_process";
5292
5079
  import { join as join9 } from "path";
5293
5080
  var TEST_TIMEOUT_MS = 6e5;
5294
5081
  var MAX_TEST_OUTPUT = 12e3;
@@ -5320,7 +5107,7 @@ async function runProjectTests(cwd, cmd) {
5320
5107
  if (!command) return { skipped: true, passed: true, output: "", timedOut: false };
5321
5108
  const [bin, ...args] = command.argv;
5322
5109
  return new Promise((resolve6) => {
5323
- const child = spawn4(bin, args, { cwd, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, CI: "1" } });
5110
+ const child = spawn3(bin, args, { cwd, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, CI: "1" } });
5324
5111
  let out = "";
5325
5112
  const take = (d) => {
5326
5113
  out += d.toString();
@@ -5823,11 +5610,12 @@ var Board = class _Board {
5823
5610
  };
5824
5611
 
5825
5612
  export {
5826
- checkProfile,
5827
- runLogin,
5828
5613
  SHORT_CALL_MS,
5829
5614
  LONG_CALL_MS,
5615
+ ZAI_MODELS,
5616
+ modelsFor,
5830
5617
  cliCatalog,
5618
+ cliFor,
5831
5619
  CliProvider,
5832
5620
  describeInherited,
5833
5621
  describeTopUp,
@@ -37,7 +37,7 @@ import {
37
37
  worktreeState,
38
38
  writeFileTool,
39
39
  writerRegistry
40
- } from "./chunk-7JMWPTJ5.js";
40
+ } from "./chunk-UEWVVN5L.js";
41
41
  import {
42
42
  resolveMainBranch
43
43
  } from "./chunk-QF4MP6BS.js";
@@ -3217,7 +3217,7 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
3217
3217
  emitPhase("verify");
3218
3218
  const cwd = await documentWorkdir(process.cwd(), prompt, ensureWorktree, r.title);
3219
3219
  laneCheckpoint(cwd, "verify", resume, prompt, r);
3220
- const { runVerify, describeVerify, currentBranchOf } = await import("./verify-6SC4I77M.js");
3220
+ const { runVerify, describeVerify, currentBranchOf } = await import("./verify-LC57A6H2.js");
3221
3221
  const branch = await currentBranchOf(cwd);
3222
3222
  const res = await runVerify({
3223
3223
  deps,
@@ -3282,8 +3282,8 @@ Which is it?`,
3282
3282
  if (small) {
3283
3283
  emitPhase("small change");
3284
3284
  emit({ kind: "note", text: `\u26A1 Small change \u2014 ${size.reason}. No branch, no spec, no plan.` });
3285
- const { runSmallChange, describeSmallChange } = await import("./fix-JOIXQFVP.js");
3286
- const { currentBranchOf } = await import("./verify-6SC4I77M.js");
3285
+ const { runSmallChange, describeSmallChange } = await import("./fix-ONLA45HD.js");
3286
+ const { currentBranchOf } = await import("./verify-LC57A6H2.js");
3287
3287
  const res = await runSmallChange(deps, cwd, r.title, r.refinedPrompt, size);
3288
3288
  return {
3289
3289
  intent: r.intent,
@@ -2,7 +2,7 @@ import {
2
2
  Board,
3
3
  refreshAfterChange,
4
4
  runTaskCycle
5
- } from "./chunk-7JMWPTJ5.js";
5
+ } from "./chunk-UEWVVN5L.js";
6
6
  import {
7
7
  defaultGitRunner
8
8
  } from "./chunk-LPQU436C.js";