glimmervoid 0.38.2 → 0.40.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 (41) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/glimmervoid.js +33 -42
  3. package/dist/chunks/{adapters-CAHxZYT3.js → adapters-B6tbX3Aj.js} +27 -14
  4. package/dist/chunks/{adapters-DDjC7_vG.js → adapters-CXQfLoyj.js} +1 -1
  5. package/dist/chunks/agent-api-cli-BBech5lF.js +1 -0
  6. package/dist/chunks/{agent-api-cli-B5Tm0wXO.js → agent-api-cli-BNHJTmNW.js} +44 -77
  7. package/dist/chunks/agent-api-core-DDrJQEdE.js +304 -0
  8. package/dist/chunks/{agent-setup-cli-DmMfCw5O.js → agent-setup-cli-CeBBwKuL.js} +2 -2
  9. package/dist/chunks/config-store-BZpN2djp.js +1 -0
  10. package/dist/chunks/{config-store-BUNZs4j7.js → config-store-DqMScSdg.js} +82 -268
  11. package/dist/chunks/contracts-CloExMlS.js +680 -0
  12. package/dist/chunks/{contracts-HVCEJfxs.js → control-messages-DpBaniJi.js} +1275 -1021
  13. package/dist/chunks/{git-version-rWZjF2Px.js → git-version-CJQMfxtZ.js} +2 -2
  14. package/dist/chunks/{grok-B6bsCgdd.js → grok-BLzhApwT.js} +7 -8
  15. package/dist/chunks/{guardrails-deps-Cb-ki4EU.js → guardrails-deps-CLxK4J-g.js} +1 -1
  16. package/dist/chunks/{json-file-CIJf0Cel.js → json-file-DFCTvPjj.js} +3 -7
  17. package/dist/chunks/{main-BNSbP-Yk.js → main-DDU8IvFD.js} +28791 -31978
  18. package/dist/chunks/{pair-cli-DNp8i5L4.js → pair-cli-CsOVTQGJ.js} +2 -2
  19. package/dist/chunks/{remote-config-C_s2pnLC.js → remote-config-HlABuLTp.js} +2 -2
  20. package/dist/chunks/{session-BlT68ppa.js → session-BwB43SCa.js} +119 -71
  21. package/dist/chunks/{session-routes-CT1JrjoT.js → session-routes-DNJzsckH.js} +25 -10
  22. package/dist/chunks/{settings-cli-BbzxCyG5.js → settings-cli-DkkbK-dj.js} +30 -156
  23. package/dist/chunks/settings-readout-core-DrXT4aWm.js +201 -0
  24. package/dist/chunks/{ssh-sign-cli-C74BGTQS.js → ssh-sign-cli-CYeKRevK.js} +2 -2
  25. package/dist/client/assets/index-BK9jLR47.js +148 -0
  26. package/dist/client/assets/index-CU_uqJmi.css +2 -0
  27. package/dist/client/assets/xterm-kR64YvZ-.js +79 -0
  28. package/dist/client/assets/zod-BRLv4KqJ.js +55 -0
  29. package/dist/client/index.html +7 -3
  30. package/dist/scripts/postinstall-path-check.js +21 -3
  31. package/dist/server/index.js +1 -1
  32. package/package.json +3 -4
  33. package/dist/chunks/agent-api-cli-BDu1CxZf.js +0 -1
  34. package/dist/chunks/agent-api-core-B_mUSenR.js +0 -341
  35. package/dist/chunks/browser-config-BrBT8269.js +0 -762
  36. package/dist/chunks/config-CjawSOBk.js +0 -219
  37. package/dist/chunks/config-store-MAVQhGMh.js +0 -1
  38. package/dist/chunks/factory-D7ryES9n.js +0 -240
  39. package/dist/chunks/knowledge-graph-cli-ByUJoKw_.js +0 -1354
  40. package/dist/client/assets/index-B5uVtedd.css +0 -1
  41. package/dist/client/assets/index-b13-NGGS.js +0 -275
package/README.md CHANGED
@@ -76,7 +76,7 @@ Declare another agent under `customAgents` in `config.json` with an `id`, `label
76
76
 
77
77
  ## Features
78
78
 
79
- - **Focus view.** A rail of every session on the left, the selected session's live terminal in the middle, its changes on the right.
79
+ - **Focus view.** A rail of every session on the left, the selected session's live terminal in the middle, its changes on the right. The top of the rail counts what needs you, and `Cmd/Alt+J` jumps straight to the next one from any tab.
80
80
  - **A worktree per agent.** Every session in a git repo gets its own worktree, so agents never step on your checkout or each other. Review the diff and merge with one click while the agent keeps working. A conflicted merge parks, and **Resolve in session** hands it back to the agent that wrote it.
81
81
  - **Exact status.** Hooks report every state change and the terminal title fills the gaps. The screen is never scraped ([how](#how-status-detection-works)).
82
82
  - **Notifications.** Browser notifications when a session needs input, finishes or fails, with your own alert sounds if you like (drop `.ogg`, `.mp3`, `.wav`, `.m4a` or `.webm` files into `~/.glimmervoid/sounds/`). Telegram covers you when no dashboard is open.
@@ -2,12 +2,13 @@
2
2
  import { o as nodePtyRebuildHint, r as firstLine, t as probeNodePty } from "../chunks/node-pty-preflight-C8QIlrtU.js";
3
3
  import { i as errorText } from "../chunks/text-DoirglWV.js";
4
4
  import { a as packageRoot } from "../chunks/runtime-paths-BkeftE60.js";
5
- import { t as AGENT_API_VERBS } from "../chunks/session-BlT68ppa.js";
5
+ import { t as AGENT_API_VERBS } from "../chunks/session-BwB43SCa.js";
6
6
  import { i as execSync } from "../chunks/child-process-safe-CbQnyh7D.js";
7
+ import { r as agentCommandHelpLines } from "../chunks/agent-api-core-DDrJQEdE.js";
7
8
  import { t as renderTable } from "../chunks/ascii-figure-core-pDAwtlKO.js";
8
9
  import { n as glimmervoidHomeDir, t as decideConfigPath } from "../chunks/config-path-core-DFt-TemS.js";
9
- import { i as parseCommandLine, r as gitDoctorRows, t as probeGitVersion } from "../chunks/git-version-rWZjF2Px.js";
10
- import { i as guardrailsDoctorRows, r as probeGuardrailsDependencies } from "../chunks/guardrails-deps-Cb-ki4EU.js";
10
+ import { i as parseCommandLine, r as gitDoctorRows, t as probeGitVersion } from "../chunks/git-version-CJQMfxtZ.js";
11
+ import { i as guardrailsDoctorRows, r as probeGuardrailsDependencies } from "../chunks/guardrails-deps-CLxK4J-g.js";
11
12
  import { i as pnpmGlobalBinDir, n as npmGlobalBinDir, r as onPath, t as formatPathNotice } from "../chunks/path-doctor-C23VAqeu.js";
12
13
  import fs from "node:fs";
13
14
  import path from "node:path";
@@ -35,21 +36,9 @@ Editor integration (Visions):
35
36
  visions relay Run the Visions LSP relay on stdio (an editor's LSP client starts this)
36
37
 
37
38
  Inside a Glimmervoid session:
38
- spawn <prompt> Start a sibling session on that prompt
39
- attention <note> Flag this session as needing the operator
40
- board List the live sessions
41
- push Land this session's commits on the integration branch and push it
42
- edit <path> Open a text file for the operator to edit in the dashboard
43
- limits List which of these commands answer here and what the guardrails block
44
- factory status Print each factory intent's tickets, state, last reason and next action
45
- factory retry <id> Reopen the parked tickets of a factory intent
46
- factory setup Prove and save the factory checks of this session's repository
47
- factory queue <file> Queue a factory intent (a spec cut.ts file or Factory tab request) on this session's repository
39
+ ${agentCommandHelpLines().join("\n")}
48
40
  ssh-sign Sign a commit with the operator's forwarded ssh agent (git runs this)
49
41
 
50
- Experimental:
51
- kg [command] Typed knowledge and task graph (Settings > Lanes > Knowledge graph)
52
-
53
42
  Options:
54
43
  --port <number> Server port (default: 3000)
55
44
  --config <path> Config file (default: ~/.glimmervoid/config.json)
@@ -70,47 +59,49 @@ if (commandLine.kind === "version") {
70
59
  }
71
60
  if (commandLine.globalOptions.configPath) process.env.GLIMMERVOID_CONFIG = commandLine.globalOptions.configPath;
72
61
  if (commandLine.globalOptions.port) process.env.GLIMMERVOID_PORT = commandLine.globalOptions.port;
73
- var SUBCOMMAND_RUNNERS = new Map([
62
+ var OWN_SUBCOMMAND_RUNNERS = [
74
63
  ["doctor", async () => {
75
64
  await runDoctor();
76
65
  return 0;
77
66
  }],
78
67
  ["pair", async (subcommandArgs) => {
79
- const { runPairCli } = await import("../chunks/pair-cli-DNp8i5L4.js");
68
+ const { runPairCli } = await import("../chunks/pair-cli-CsOVTQGJ.js");
80
69
  return runPairCli(subcommandArgs);
81
70
  }],
82
71
  ["agent", async (subcommandArgs) => {
83
- const { runAgentSetupCli } = await import("../chunks/agent-setup-cli-DmMfCw5O.js");
72
+ const { runAgentSetupCli } = await import("../chunks/agent-setup-cli-CeBBwKuL.js");
84
73
  return runAgentSetupCli(subcommandArgs);
85
74
  }],
86
- ["kg", async (subcommandArgs) => {
87
- const { runKnowledgeGraphCommand } = await import("../chunks/knowledge-graph-cli-ByUJoKw_.js");
88
- return runKnowledgeGraphCommand(subcommandArgs);
89
- }],
90
75
  ["settings", async (subcommandArgs) => {
91
- const { runSettingsCommand } = await import("../chunks/settings-cli-BbzxCyG5.js");
92
- return runSettingsCommand(subcommandArgs);
76
+ const { runSettingsCli } = await import("../chunks/settings-cli-DkkbK-dj.js");
77
+ return runSettingsCli(subcommandArgs);
93
78
  }],
94
79
  ["visions", async (subcommandArgs) => {
95
80
  const { runVisionsCli } = await import("../chunks/visions-cli-CYujESdJ.js");
96
81
  return runVisionsCli(subcommandArgs);
97
82
  }],
98
83
  ["ssh-sign", async (subcommandArgs) => {
99
- const { runSshSignCli } = await import("../chunks/ssh-sign-cli-C74BGTQS.js");
84
+ const { runSshSignCli } = await import("../chunks/ssh-sign-cli-CYeKRevK.js");
100
85
  return runSshSignCli(subcommandArgs);
101
- }],
102
- ["factory", async (subcommandArgs) => {
103
- const { runAgentApiCli } = await import("../chunks/agent-api-cli-BDu1CxZf.js");
104
- return runAgentApiCli(["factory", ...subcommandArgs]);
105
- }],
106
- ...AGENT_API_VERBS.map((verb) => [verb, async (subcommandArgs, subcommandName) => {
107
- const { runAgentApiCli } = await import("../chunks/agent-api-cli-BDu1CxZf.js");
108
- return runAgentApiCli([subcommandName, ...subcommandArgs]);
109
- }])
110
- ]);
86
+ }]
87
+ ];
88
+ var ownSubcommandNames = new Set(OWN_SUBCOMMAND_RUNNERS.map(([name]) => name));
89
+ var SUBCOMMAND_RUNNERS = new Map([...OWN_SUBCOMMAND_RUNNERS, ...AGENT_API_VERBS.filter((verb) => !ownSubcommandNames.has(verb)).map((verb) => [verb, async (subcommandArgs, subcommandName) => {
90
+ const { runAgentApiCli } = await import("../chunks/agent-api-cli-BBech5lF.js");
91
+ return runAgentApiCli([subcommandName, ...subcommandArgs]);
92
+ }])]);
93
+ var AGENT_INSTALL_COMMAND_BY_ID = {
94
+ "claude-code": "npm install -g @anthropic-ai/claude-code",
95
+ codex: "npm install -g @openai/codex"
96
+ };
97
+ function missingAgentText(agentId) {
98
+ const installCommand = AGENT_INSTALL_COMMAND_BY_ID[agentId];
99
+ if (!installCommand) return "not found on PATH";
100
+ return `not found on PATH; install with: ${installCommand}`;
101
+ }
111
102
  async function printSessionLimits() {
112
103
  if (!process.env["GLIMMERVOID_AGENT_URL"]) return;
113
- const { sessionLimitsHelpLines } = await import("../chunks/agent-api-cli-BDu1CxZf.js");
104
+ const { sessionLimitsHelpLines } = await import("../chunks/agent-api-cli-BBech5lF.js");
114
105
  const limitsLines = await sessionLimitsHelpLines();
115
106
  console.log(`\n${limitsLines.join("\n")}`);
116
107
  }
@@ -167,7 +158,7 @@ async function readDeclaredCustomAgents() {
167
158
  declared: [],
168
159
  error: null
169
160
  };
170
- const { loadConfigFile } = await import("../chunks/config-store-MAVQhGMh.js");
161
+ const { loadConfigFile } = await import("../chunks/config-store-BZpN2djp.js");
171
162
  try {
172
163
  const loaded = loadConfigFile(decided.path, { exitOnError: false });
173
164
  if (!loaded.config) return {
@@ -243,15 +234,15 @@ async function runDoctor() {
243
234
  }
244
235
  switchSection("Agents");
245
236
  try {
246
- const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-DDjC7_vG.js");
237
+ const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-CXQfLoyj.js");
247
238
  const declaredCustomAgents = await readDeclaredCustomAgents();
248
239
  if (declaredCustomAgents.error) line("config", declaredCustomAgents.error);
249
240
  setCustomAgents(declaredCustomAgents.declared);
250
241
  for (const id of listAgentIds()) {
251
242
  const { label, path: resolvedPath } = describeAgentResolvability(id);
252
- line(`${id} (${label})`, resolvedPath ?? "not found on PATH");
243
+ line(`${id} (${label})`, resolvedPath ?? missingAgentText(id));
253
244
  }
254
- const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-DmMfCw5O.js");
245
+ const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-CeBBwKuL.js");
255
246
  const grokSetup = inspectGrokAgentSetup();
256
247
  line("grok hook setup", `${grokSetup.classification}: ${grokSetup.filePath}`);
257
248
  } catch (err) {
@@ -268,7 +259,7 @@ async function runDoctor() {
268
259
  try {
269
260
  const { resolvePackageBin } = await import("../chunks/runtime-paths-DSmormp7.js");
270
261
  const { execFileSync } = await import("../chunks/child-process-safe-ChRmWAJP.js");
271
- const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-DmMfCw5O.js");
262
+ const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-CeBBwKuL.js");
272
263
  const binPath = resolvePackageBin("cc-safety-net", "cc-safety-net");
273
264
  line("Sane YOLO", binPath || "not installed");
274
265
  if (binPath) line("cc-safety-net version", execFileSync(process.execPath, [binPath, "--version"], {
@@ -1,12 +1,13 @@
1
1
  import { o as relayPath } from "./runtime-paths-BkeftE60.js";
2
2
  import { r as execFileSync } from "./child-process-safe-CbQnyh7D.js";
3
+ import "./control-messages-DpBaniJi.js";
4
+ import { o as supervisionLine } from "./session-routes-DNJzsckH.js";
3
5
  import { n as resolveAgentCommand, t as buildAgentSpawnCommand } from "./spawn-command-Cc40FiZI.js";
4
6
  import { s as rawTextOr } from "./coerce-NSst8oRP.js";
5
7
  import { r as nextBackoffMs } from "./backoff-DjVpfHk7.js";
6
- import { l as supervisionLine } from "./session-routes-CT1JrjoT.js";
7
- import { c as buildAgentEnv, i as isSpinnerChar, n as classifyAgentTitle, r as isBrailleChar, t as grok } from "./grok-B6bsCgdd.js";
8
+ import { i as isSpinnerChar, n as classifyAgentTitle, r as isBrailleChar, s as buildAgentEnv, t as grok } from "./grok-BLzhApwT.js";
8
9
  import { t as buildHookCommand } from "./hook-command-core-qTNK-xMF.js";
9
- import "./contracts-HVCEJfxs.js";
10
+ import "./contracts-CloExMlS.js";
10
11
  import { n as mergeCodexPreToolUse, t as HOOK_TOOLS } from "./hook-tools-Dl7-r2Eb.js";
11
12
  import fs from "node:fs";
12
13
  import path from "node:path";
@@ -204,13 +205,6 @@ var claudeCode = {
204
205
  };
205
206
  //#endregion
206
207
  //#region server/usage/core/usage-scan-core.ts
207
- function laneSpendStatus({ isTrackingEnabled, scan }) {
208
- if (!isTrackingEnabled) return "tracking-off";
209
- if (scan === null) return "scanner-missing";
210
- if (scan.lastOutcome === "io-failed") return "read-failing";
211
- if (!scan.isHistoryCaughtUp || scan.lastOutcome === null) return "catching-up";
212
- return "known";
213
- }
214
208
  function scopedClaudeRoots(scopeDirs, claudeDirs) {
215
209
  if (!scopeDirs || scopeDirs.length === 0) return null;
216
210
  const isInside = (candidate, parent) => candidate.startsWith(`${parent}${path.sep}`);
@@ -473,9 +467,11 @@ function mayContributeHooks(configText) {
473
467
  function userConfigFolders(env) {
474
468
  return codexHomes(env, null);
475
469
  }
476
- function buildHookArgs({ relayPath = RELAY_PATH, events = HOOK_EVENTS, bypassHookTrust = false, hookTools = [], hookToolRelayPath = HOOK_TOOL_RELAY_PATH } = {}) {
470
+ var TOML_LITERAL_STRING_FORBIDDEN = /[\x00-\x08\x0a-\x1f\x27\x7f]/;
471
+ function buildHookArgs({ relayPath = RELAY_PATH, events = HOOK_EVENTS, bypassHookTrust = false, hookTools = [], hookToolRelayPath = HOOK_TOOL_RELAY_PATH, userHooks = [] } = {}) {
477
472
  const args = bypassHookTrust ? [TRUST_BYPASS_FLAG] : [];
478
473
  const preToolUseGroups = [];
474
+ const groupsByEvent = /* @__PURE__ */ new Map();
479
475
  for (const event of events) {
480
476
  const command = buildHookCommand(relayPath, event);
481
477
  if (!command) return null;
@@ -483,13 +479,30 @@ function buildHookArgs({ relayPath = RELAY_PATH, events = HOOK_EVENTS, bypassHoo
483
479
  preToolUseGroups.push(`{hooks=[{type='command',command='${command}'}]}`);
484
480
  continue;
485
481
  }
486
- args.push("-c", `hooks.${event}=[{hooks=[{type='command',command='${command}'}]}]`);
482
+ groupsByEvent.set(event, [`{hooks=[{type='command',command='${command}'}]}`]);
487
483
  }
488
484
  const groups = hookTools.flatMap((tool) => {
489
485
  const group = HOOK_TOOLS[tool.id].codexGroup(tool, hookToolRelayPath);
490
486
  return group ? [group] : [];
491
487
  });
492
- args.push(...mergeCodexPreToolUse([...preToolUseGroups, ...groups]));
488
+ preToolUseGroups.push(...groups);
489
+ for (const hook of userHooks) {
490
+ if (!hook.enabled || hook.type !== "command" || !hook.command) continue;
491
+ if (TOML_LITERAL_STRING_FORBIDDEN.test(hook.command) || hook.matcher !== void 0 && TOML_LITERAL_STRING_FORBIDDEN.test(hook.matcher)) {
492
+ console.warn(`[codex] hook ${hook.id} skipped: command or matcher cannot be expressed as a TOML literal string`);
493
+ continue;
494
+ }
495
+ const group = `{${hook.matcher === void 0 ? "" : `matcher='${hook.matcher}',`}hooks=[{type='command',command='${hook.command}'}]}`;
496
+ if (hook.event === "PreToolUse") {
497
+ preToolUseGroups.push(group);
498
+ continue;
499
+ }
500
+ const eventGroups = groupsByEvent.get(hook.event) ?? [];
501
+ eventGroups.push(group);
502
+ groupsByEvent.set(hook.event, eventGroups);
503
+ }
504
+ for (const [event, eventGroups] of groupsByEvent) args.push("-c", `hooks.${event}=[${eventGroups.join(",")}]`);
505
+ args.push(...mergeCodexPreToolUse(preToolUseGroups));
493
506
  return args;
494
507
  }
495
508
  var hooks = {
@@ -790,4 +803,4 @@ function resetCommandCache() {
790
803
  resolvedCommands.clear();
791
804
  }
792
805
  //#endregion
793
- export { shouldPersistWarehouse as A, isUsageFile as C, resolveProjectsDirs as D, projectDirCandidates as E, claudeCode as M, scopedClaudeRoots as O, grokRootCandidates as S, passOutcome as T, continuationDelayMs as _, describeAgentResolvability as a, expandTilde as b, isKnownAgentId as c, resolveAdapter as d, setCustomAgents as f, codexSessionIdFromPath as g, codexRootCandidates as h, customAgentFingerprint as i, splitLines as j, shouldEvaluateDespiteIoFailures as k, listAgentIds as l, codexHomes as m, cachedAgentResolvability as n, getAdapter as o, codexFallbackRoots as p, commandFor as r, hookProfileOf as s, DEFAULT_AGENT_ID as t, resetCommandCache as u, decideFileRead as v, laneSpendStatus as w, grokHomes as x, dedupeCodexFiles as y };
806
+ export { splitLines as A, isUsageFile as C, scopedClaudeRoots as D, resolveProjectsDirs as E, shouldEvaluateDespiteIoFailures as O, grokRootCandidates as S, projectDirCandidates as T, continuationDelayMs as _, describeAgentResolvability as a, expandTilde as b, isKnownAgentId as c, resolveAdapter as d, setCustomAgents as f, codexSessionIdFromPath as g, codexRootCandidates as h, customAgentFingerprint as i, claudeCode as j, shouldPersistWarehouse as k, listAgentIds as l, codexHomes as m, cachedAgentResolvability as n, getAdapter as o, codexFallbackRoots as p, commandFor as r, hookProfileOf as s, DEFAULT_AGENT_ID as t, resetCommandCache as u, decideFileRead as v, passOutcome as w, grokHomes as x, dedupeCodexFiles as y };
@@ -1 +1 @@
1
- export { a as describeAgentResolvability, l as listAgentIds, f as setCustomAgents } from "./adapters-CAHxZYT3.js";
1
+ export { a as describeAgentResolvability, l as listAgentIds, f as setCustomAgents } from "./adapters-B6tbX3Aj.js";
@@ -0,0 +1 @@
1
+ export { t as askAgentApi, r as runAgentApiCli, i as sessionLimitsHelpLines } from "./agent-api-cli-BNHJTmNW.js";
@@ -1,21 +1,19 @@
1
1
  import { t as errorCode } from "./text-DoirglWV.js";
2
- import { a as AgentDispatchRequest, f as AgentTicketRequest, l as AgentFactoryStatusRequest, n as AGENT_URL_ENV, s as AgentFactoryRetryRequest, u as AgentLimitsReply } from "./session-BlT68ppa.js";
2
+ import { n as AGENT_URL_ENV, o as AgentLimitsReply } from "./session-BwB43SCa.js";
3
+ import { i as formatSessionLimits } from "./session-routes-DNJzsckH.js";
4
+ import { l as isAgentApiVerb, t as AGENT_COMMAND_HELP } from "./agent-api-core-DDrJQEdE.js";
3
5
  import { i as resolveHookTarget } from "./hook-relay-core-DA_bz2KA.js";
4
6
  import { n as decideGuardrailsProxy } from "./guardrails-proxy-core-BI3rdY-m.js";
5
- import { n as isRecord } from "./coerce-NSst8oRP.js";
6
7
  import { t as parseJsonOrNull } from "./json-core-dfa_7Fak.js";
7
- import { a as formatSessionLimits } from "./session-routes-CT1JrjoT.js";
8
- import { g as isOrchestratorAgentVerb, h as isAgentApiVerb } from "./agent-api-core-B_mUSenR.js";
9
- import { readFileSync } from "node:fs";
10
8
  import path from "node:path";
11
9
  import http from "node:http";
12
10
  //#region server/agent-api-cli.ts
13
11
  var AGENT_PATH_PREFIX = "/agent/";
14
12
  var HELP_LIMITS_TIMEOUT_MS = 2e3;
15
13
  var HELP_LIMITS_UNKNOWN_LINE = "This session: Glimmervoid did not report its limits, so run glimmervoid limits to see why";
16
- function ticketBody(rest) {
17
- const parsed = AgentTicketRequest.safeParse({ workId: rest[0] });
18
- return parsed.success ? parsed.data : null;
14
+ function pullRequestBody(rest) {
15
+ const pullRequest = rest.join(" ").trim();
16
+ return pullRequest ? { pr: pullRequest } : null;
19
17
  }
20
18
  var BODY_BUILDERS = {
21
19
  spawn: (rest) => {
@@ -33,41 +31,15 @@ var BODY_BUILDERS = {
33
31
  return docPath ? { path: path.resolve(docPath) } : null;
34
32
  },
35
33
  limits: () => ({}),
36
- dispatch: (rest) => {
37
- const parsed = AgentDispatchRequest.safeParse(rest[0] === "--ready" ? { readyIntent: rest[1] } : { workId: rest[0] });
38
- return parsed.success ? parsed.data : null;
39
- },
40
- reopen: (rest) => ticketBody(rest),
41
- cancel: (rest) => ticketBody(rest),
42
- "factory-status": (rest) => {
43
- if (rest.length === 0) return {};
44
- if (rest.length !== 2 || rest[0] !== "--project") return null;
45
- const parsed = AgentFactoryStatusRequest.safeParse({ projectId: rest[1] });
46
- return parsed.success ? parsed.data : null;
47
- },
48
- "factory-retry": (rest) => {
49
- if (rest.length !== 1) return null;
50
- const parsed = AgentFactoryRetryRequest.safeParse({ intentId: rest[0] });
51
- return parsed.success ? parsed.data : null;
52
- },
53
- "factory-setup": (rest) => rest.length === 0 ? {} : null,
54
- "factory-queue": (rest) => {
55
- if (rest.length !== 1) return null;
56
- const intent = parseJsonOrNull(readFileSync(rest[0], "utf8"));
57
- if (!isRecord(intent)) return null;
58
- const { name: _name, type: _type, requestId: _requestId, projectId: _projectId, ...request } = intent;
59
- return request;
60
- }
34
+ review: pullRequestBody,
35
+ reviews: () => ({}),
36
+ prs: () => ({}),
37
+ "link-pr": pullRequestBody,
38
+ settings: ([command, settingPath]) => ({
39
+ command,
40
+ ...settingPath === void 0 ? {} : { path: settingPath }
41
+ })
61
42
  };
62
- var FACTORY_USAGE = Object.freeze({
63
- "factory-status": "status [--project <projectId>]",
64
- "factory-retry": "retry <intentId>",
65
- "factory-setup": "setup",
66
- "factory-queue": "queue <intent.json>"
67
- });
68
- function isFactoryVerb(verb) {
69
- return verb in FACTORY_USAGE;
70
- }
71
43
  function bodySaysOk(text) {
72
44
  try {
73
45
  const parsed = JSON.parse(text);
@@ -82,13 +54,6 @@ function limitsLines(replyText) {
82
54
  return formatSessionLimits(limits.data);
83
55
  }
84
56
  function printableReply(verb, replyText) {
85
- if (isFactoryVerb(verb)) {
86
- const reply = parseJsonOrNull(replyText);
87
- if (typeof reply !== "object" || reply === null) return replyText;
88
- if ("text" in reply && typeof reply.text === "string") return reply.text;
89
- if ("error" in reply && typeof reply.error === "string") return reply.error;
90
- return replyText;
91
- }
92
57
  if (verb !== "limits") return replyText;
93
58
  return limitsLines(replyText)?.join("\n") ?? replyText;
94
59
  }
@@ -188,13 +153,6 @@ async function sessionLimitsHelpLines({ timeoutMs = HELP_LIMITS_TIMEOUT_MS } = {
188
153
  }
189
154
  }
190
155
  async function runAgentApiCli(args) {
191
- if (args[0] === "factory") {
192
- if (!(`factory-${args[1]}` in FACTORY_USAGE)) {
193
- console.error(`glimmervoid factory: expected ${Object.values(FACTORY_USAGE).join(", or ")}`);
194
- return 1;
195
- }
196
- return runAgentApiCli([`factory-${args[1]}`, ...args.slice(2)]);
197
- }
198
156
  const verb = args[0];
199
157
  if (!verb || !isAgentApiVerb(verb)) {
200
158
  console.error(`glimmervoid: ${verb || "that"} is not an agent command`);
@@ -202,29 +160,12 @@ async function runAgentApiCli(args) {
202
160
  }
203
161
  const body = BODY_BUILDERS[verb](args.slice(1));
204
162
  if (!body) {
205
- if (isFactoryVerb(verb)) {
206
- console.error(`glimmervoid factory: expected ${FACTORY_USAGE[verb]}`);
207
- return 1;
208
- }
209
- if (isOrchestratorAgentVerb(verb)) {
210
- console.error(`glimmervoid ${verb}: expected a work id matching wrk-[0-9a-f]{16}`);
211
- return 1;
212
- }
213
- if (verb === "edit") {
214
- console.error("glimmervoid edit: this command needs a file path, for example \"glimmervoid edit drafts/post.md\"");
215
- return 1;
216
- }
217
- console.error(`glimmervoid ${verb}: this command needs text, for example "glimmervoid ${verb} check the failing test"`);
218
- return 1;
219
- }
220
- const rawUrl = process.env[AGENT_URL_ENV];
221
- if (!rawUrl) {
222
- console.error(`glimmervoid: ${AGENT_URL_ENV} is not set, so this is not a Glimmervoid session`);
163
+ console.error(AGENT_COMMAND_HELP[verb].missingArgumentHint ?? `glimmervoid ${verb}: this command needs an argument`);
223
164
  return 1;
224
165
  }
225
- const target = verbEndpoint(rawUrl, verb);
166
+ const target = sessionEndpoint(verb);
226
167
  if (!target.ok) {
227
- console.error(`glimmervoid: ${AGENT_URL_ENV} must be a loopback http url under ${AGENT_PATH_PREFIX}, so nothing was sent`);
168
+ console.error(target.error);
228
169
  return 1;
229
170
  }
230
171
  const reply = await postToAgentIngress({
@@ -235,5 +176,31 @@ async function runAgentApiCli(args) {
235
176
  console.log(printableReply(verb, reply.text));
236
177
  return reply.hasSuccessStatus && bodySaysOk(reply.text) ? 0 : 1;
237
178
  }
179
+ function sessionEndpoint(verb) {
180
+ const rawUrl = process.env[AGENT_URL_ENV];
181
+ if (!rawUrl) return {
182
+ ok: false,
183
+ error: `glimmervoid: ${AGENT_URL_ENV} is not set, so this is not a Glimmervoid session`
184
+ };
185
+ const target = verbEndpoint(rawUrl, verb);
186
+ if (!target.ok) return {
187
+ ok: false,
188
+ error: `glimmervoid: ${AGENT_URL_ENV} must be a loopback http url under ${AGENT_PATH_PREFIX}, so nothing was sent`
189
+ };
190
+ return target;
191
+ }
192
+ async function askAgentApi(verb, body) {
193
+ const target = sessionEndpoint(verb);
194
+ if (!target.ok) return {
195
+ ok: false,
196
+ error: target.error
197
+ };
198
+ const reply = await postToAgentIngress({
199
+ endpoint: target.endpoint,
200
+ token: target.token,
201
+ body
202
+ });
203
+ return parseJsonOrNull(reply.text);
204
+ }
238
205
  //#endregion
239
- export { runAgentApiCli as n, sessionLimitsHelpLines as r, postToAgentIngress as t };
206
+ export { sessionLimitsHelpLines as i, postToAgentIngress as n, runAgentApiCli as r, askAgentApi as t };