@quireco/cli 0.0.11 → 0.0.12

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.
@@ -8,10 +8,10 @@ import { exec, execFile, execFileSync, spawn } from "node:child_process";
8
8
  import pc from "picocolors";
9
9
  import { calculateCost, createAssistantMessageEventStream } from "@earendil-works/pi-ai";
10
10
  import { convertMessages } from "@earendil-works/pi-ai/openai-completions";
11
- import { access, constants, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
11
+ import { access, constants, mkdir, mkdtemp, readFile, readdir, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
12
12
  import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
13
13
  import { homedir, networkInterfaces, tmpdir } from "node:os";
14
- import { closeSync, constants as constants$1, copyFileSync, existsSync, fstatSync, fsyncSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync } from "node:fs";
14
+ import { closeSync, constants as constants$1, copyFileSync, existsSync, fstatSync, fsyncSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync } from "node:fs";
15
15
  import { createHash } from "node:crypto";
16
16
  import { customAlphabet } from "nanoid";
17
17
  import { Type } from "@sinclair/typebox";
@@ -1375,21 +1375,28 @@ const FINDING_RELATIONSHIPS_TO_REQUEST = [
1375
1375
  ];
1376
1376
  function createRunDir(repoPath, options = {}) {
1377
1377
  const workspaceKey = workspaceKeyForRepoPath(resolve(repoPath));
1378
- const runsRoot = resolve(options.runsRoot ?? defaultRunsRoot());
1379
- const { runId, root } = options.runId === void 0 ? createUniqueRunRoot(runsRoot, workspaceKey, options.createRunId ?? createRunId) : {
1378
+ const { runsRoot, runId, root, temporaryRunsRoot } = allocateRunRoot({
1379
+ runsRoot: resolve(options.runsRoot ?? defaultRunsRoot()),
1380
+ workspaceKey,
1380
1381
  runId: options.runId,
1381
- root: join(runsRoot, workspaceKey, options.runId)
1382
- };
1383
- const paths = runDirPaths(root);
1384
- mkdirSync(paths.evidence, {
1385
- recursive: true,
1386
- mode: 448
1382
+ runIdFactory: options.createRunId ?? createRunId,
1383
+ allowTemporaryFallback: options.runsRoot === void 0 && (process.env.QUIRE_RUNS_DIR?.trim().length ?? 0) === 0
1387
1384
  });
1385
+ const paths = runDirPaths(root);
1386
+ try {
1387
+ mkdirSync(paths.evidence, {
1388
+ recursive: true,
1389
+ mode: 448
1390
+ });
1391
+ } catch (error) {
1392
+ throw writableRunsRootError(error, runsRoot);
1393
+ }
1388
1394
  return {
1389
1395
  runId,
1390
1396
  workspaceKey,
1391
1397
  runsRoot,
1392
1398
  root,
1399
+ ...temporaryRunsRoot ? { temporaryRunsRoot: true } : {},
1393
1400
  paths
1394
1401
  };
1395
1402
  }
@@ -1416,6 +1423,9 @@ function runPlanPath(runPath) {
1416
1423
  function runHandoffPath(runPath) {
1417
1424
  return join(resolve(runPath), "handoff.md");
1418
1425
  }
1426
+ function runPendingHandoffPath(runPath) {
1427
+ return join(resolve(runPath), ".pending-handoff.json");
1428
+ }
1419
1429
  function runDirPaths(root) {
1420
1430
  const evidence = join(root, "evidence");
1421
1431
  return {
@@ -1432,6 +1442,51 @@ function runDirPaths(root) {
1432
1442
  harnessSessions: join(root, ".harness-sessions")
1433
1443
  };
1434
1444
  }
1445
+ function allocateRunRoot(input) {
1446
+ try {
1447
+ return {
1448
+ ...createRunRoot(input.runsRoot, input.workspaceKey, input.runId, input.runIdFactory),
1449
+ runsRoot: input.runsRoot,
1450
+ temporaryRunsRoot: false
1451
+ };
1452
+ } catch (error) {
1453
+ if (!isWriteAccessError$2(error)) throw error;
1454
+ if (!input.allowTemporaryFallback) throw writableRunsRootError(error, input.runsRoot);
1455
+ const runsRoot = mkdtempSync(join(tmpdir(), "quire-runs-"));
1456
+ return {
1457
+ ...createRunRoot(runsRoot, input.workspaceKey, input.runId, input.runIdFactory),
1458
+ runsRoot,
1459
+ temporaryRunsRoot: true
1460
+ };
1461
+ }
1462
+ }
1463
+ function createRunRoot(runsRoot, workspaceKey, runId, runIdFactory) {
1464
+ return runId === void 0 ? createUniqueRunRoot(runsRoot, workspaceKey, runIdFactory) : createNamedRunRoot(runsRoot, workspaceKey, runId);
1465
+ }
1466
+ function createNamedRunRoot(runsRoot, workspaceKey, runId) {
1467
+ const root = join(runsRoot, workspaceKey, runId);
1468
+ mkdirSync(join(root, "evidence"), {
1469
+ recursive: true,
1470
+ mode: 448
1471
+ });
1472
+ return {
1473
+ runId,
1474
+ root
1475
+ };
1476
+ }
1477
+ function writableRunsRootError(error, runsRoot) {
1478
+ if (!isWriteAccessError$2(error)) return error;
1479
+ return new CliError$1([
1480
+ `Quire could not write run data at ${runsRoot}: ${error.message}`,
1481
+ "In sandboxed coding-agent environments, retry with a temporary run directory:",
1482
+ " quire_runs_dir=$(mktemp -d -t quire-runs-XXXXXX)",
1483
+ " QUIRE_RUNS_DIR=$quire_runs_dir quire run --detach --json <your request>",
1484
+ "Use the same QUIRE_RUNS_DIR value for follow-up `quire wait`, `quire status`, `quire watch`, or `quire cancel` commands."
1485
+ ].join("\n"), ExitCode.InvalidInput);
1486
+ }
1487
+ function isWriteAccessError$2(error) {
1488
+ return error instanceof Error && "code" in error && (error.code === "EROFS" || error.code === "EACCES" || error.code === "EPERM");
1489
+ }
1435
1490
  function defaultRunsRoot(env = process.env) {
1436
1491
  return defaultRunsRoot$1(env);
1437
1492
  }
@@ -1483,6 +1538,20 @@ function writeRunFinalHandoff(runDir, final) {
1483
1538
  finalResult: final.result
1484
1539
  });
1485
1540
  }
1541
+ function writeRunPendingHandoff(runDir, handoff) {
1542
+ const writtenAt = (/* @__PURE__ */ new Date()).toISOString();
1543
+ writeFileAtomically(runPendingHandoffPath(runDir.root), `${stableJson({
1544
+ version: 1,
1545
+ writtenAt,
1546
+ handoff
1547
+ })}\n`, 384);
1548
+ writeFileAtomically(runDir.paths.handoff, `${handoff.handoffMarkdown.trimEnd()}\n`, 384);
1549
+ }
1550
+ function clearRunPendingHandoff(runDir) {
1551
+ try {
1552
+ unlinkSync(runPendingHandoffPath(runDir.root));
1553
+ } catch {}
1554
+ }
1486
1555
  function patchRunStatusJson(runDir, patch) {
1487
1556
  if (!existsSync(runDir.paths.status)) return;
1488
1557
  let existing;
@@ -1752,18 +1821,32 @@ function readFreshRunStatus(runPath) {
1752
1821
  if (existsSync(runHandoffPath(runPath))) return updateRunStatus(runPath, { status: "completed" });
1753
1822
  return updateRunStatus(runPath, {
1754
1823
  status: "stale",
1755
- error: "Worker process exited before marking the run as running."
1824
+ error: staleWorkerError(runPath, "Worker process exited before marking the run as running.")
1756
1825
  });
1757
1826
  }
1758
1827
  if ((status.status === "running" || status.status === "canceling") && !isPidAlive(status.pid)) {
1759
1828
  if (existsSync(runHandoffPath(runPath))) return updateRunStatus(runPath, { status: "completed" });
1760
1829
  return updateRunStatus(runPath, {
1761
1830
  status: status.status === "canceling" ? "canceled" : "stale",
1762
- error: status.status === "canceling" ? void 0 : "Worker process is no longer running."
1831
+ error: status.status === "canceling" ? void 0 : staleWorkerError(runPath, "Worker process is no longer running.")
1763
1832
  });
1764
1833
  }
1765
1834
  return status;
1766
1835
  }
1836
+ function staleWorkerError(runPath, message) {
1837
+ const workerLogPath = join(runPath, "worker.log");
1838
+ if (!existsSync(workerLogPath)) return message;
1839
+ const tail = readWorkerLogTail$1(workerLogPath);
1840
+ if (tail.length === 0) return `${message}\n\nWorker log: ${workerLogPath} (empty)`;
1841
+ return `${message}\n\nWorker log: ${workerLogPath}\n\nLast worker output:\n${tail}`;
1842
+ }
1843
+ function readWorkerLogTail$1(workerLogPath) {
1844
+ try {
1845
+ return readFileSync(workerLogPath, "utf8").slice(-12e3).trim();
1846
+ } catch {
1847
+ return "";
1848
+ }
1849
+ }
1767
1850
  function readRequiredRunStatus(runPath) {
1768
1851
  const status = readRunStatus(runPath);
1769
1852
  if (status === void 0) throw new CliError$1(`Run status not found: ${runPath}`, ExitCode.InvalidInput);
@@ -1799,10 +1882,27 @@ function writeStatus(filePath, status) {
1799
1882
  mode: 448
1800
1883
  });
1801
1884
  const tempPath = join(dirname(filePath), `.${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.status.tmp`);
1802
- writeFileSync(tempPath, `${JSON.stringify(status, null, 2)}\n`, { mode: 384 });
1803
- fsyncFile(tempPath);
1804
- renameSync(tempPath, filePath);
1805
- fsyncDirectory(dirname(filePath));
1885
+ try {
1886
+ writeFileSync(tempPath, `${JSON.stringify(status, null, 2)}\n`, { mode: 384 });
1887
+ fsyncFile(tempPath);
1888
+ renameSync(tempPath, filePath);
1889
+ fsyncDirectory(dirname(filePath));
1890
+ } catch (error) {
1891
+ throw writableRunStatusError(error, status.runPath);
1892
+ }
1893
+ }
1894
+ function writableRunStatusError(error, runPath) {
1895
+ if (!isWriteAccessError$1(error)) return error;
1896
+ return new CliError$1([
1897
+ `Quire could not update run status at ${runPath}: ${error.message}`,
1898
+ "In sandboxed coding-agent environments, put Quire run data in a temporary directory and reuse it for every lifecycle command:",
1899
+ " quire_runs_dir=$(mktemp -d -t quire-runs-XXXXXX)",
1900
+ " QUIRE_RUNS_DIR=$quire_runs_dir quire run --detach --json <your request>",
1901
+ " QUIRE_RUNS_DIR=$quire_runs_dir quire wait <run-id> --json"
1902
+ ].join("\n"), ExitCode.InvalidInput);
1903
+ }
1904
+ function isWriteAccessError$1(error) {
1905
+ return error instanceof Error && "code" in error && (error.code === "EROFS" || error.code === "EACCES" || error.code === "EPERM");
1806
1906
  }
1807
1907
  function fsyncFile(filePath) {
1808
1908
  const file = openSync(filePath, "r");
@@ -2801,6 +2901,7 @@ function materializeHandoff({ handoff, runDir, continuation, modelUsage, metadat
2801
2901
  result: materializedResult
2802
2902
  });
2803
2903
  writeHandoffArtifact(runDir, materializedHandoff);
2904
+ clearRunPendingHandoff(runDir);
2804
2905
  return {
2805
2906
  result: materializedResult,
2806
2907
  metadata: nextMetadata
@@ -45315,6 +45416,23 @@ const writeHandoffToolDefinition = {
45315
45416
  };
45316
45417
  }
45317
45418
  };
45419
+ function makeWriteHandoffTool(options = {}) {
45420
+ return defineTool({
45421
+ ...writeHandoffToolDefinition,
45422
+ async execute(_toolCallId, params, _signal) {
45423
+ const handoff = normalizeWriteHandoffInput(params);
45424
+ await options.onHandoff?.(handoff);
45425
+ return {
45426
+ content: [{
45427
+ type: "text",
45428
+ text: JSON.stringify(handoff, null, 2)
45429
+ }],
45430
+ details: handoff,
45431
+ terminate: true
45432
+ };
45433
+ }
45434
+ });
45435
+ }
45318
45436
  function normalizeWriteHandoffInput(params) {
45319
45437
  const { status, ...rest } = params;
45320
45438
  return {
@@ -45327,7 +45445,7 @@ function normalizeHandoffStatus(status) {
45327
45445
  if (status === "no_issue_found" || status === "not_a_bug" || status === "verified_no_bug_found" || status === "verified_no_issue_found") return "not_reproduced";
45328
45446
  return status;
45329
45447
  }
45330
- const writeHandoffTool = defineTool(writeHandoffToolDefinition);
45448
+ makeWriteHandoffTool();
45331
45449
  //#endregion
45332
45450
  //#region src/utils/schema.ts
45333
45451
  function stringEnumSchema(values) {
@@ -46153,7 +46271,11 @@ async function runQuireAgent({ intent, runBrief, runInstructions, remoteAddons,
46153
46271
  followup,
46154
46272
  skillPack,
46155
46273
  mcpGateway,
46156
- intent
46274
+ intent,
46275
+ onHandoff: (handoff) => {
46276
+ emittedHandoff ??= handoff;
46277
+ writeRunPendingHandoff(runDir, handoff);
46278
+ }
46157
46279
  });
46158
46280
  const { session, logAgentProgress, registeredTargetTools } = runtime;
46159
46281
  onResolvedModel?.({
@@ -46161,18 +46283,31 @@ async function runQuireAgent({ intent, runBrief, runInstructions, remoteAddons,
46161
46283
  provider: runtime.model.provider,
46162
46284
  modelId: runtime.model.id
46163
46285
  });
46286
+ let rejectCurrentPromptOnModelError;
46164
46287
  const unsubscribe = session.subscribe((event) => {
46165
46288
  touchRunActivity(runDir.root, { minIntervalMs: RUN_ACTIVITY_HEARTBEAT_INTERVAL_MS });
46166
46289
  logAgentProgress(event);
46290
+ const sessionError = modelErrorFromSessionEvent(event);
46291
+ if (sessionError !== void 0) rejectCurrentPromptOnModelError?.(sessionError);
46167
46292
  emittedHandoff ??= extractWriteHandoff(event);
46168
46293
  rememberTargetToolCallArgs(event, registeredTargetTools, targetToolCallArgs);
46169
46294
  const attemptsObserved = countNewTargetAttempts(event, registeredTargetTools, observedTargetToolCallIds, targetToolCallArgs);
46170
46295
  if (attemptsObserved > 0) observedToolAttempts += attemptsObserved;
46171
46296
  });
46172
46297
  try {
46173
- await session.prompt(runBrief, { expandPromptTemplates: false });
46174
- if (emittedHandoff === void 0) await session.prompt(buildMissingRunHandoffPrompt(observedToolAttempts), { expandPromptTemplates: false });
46298
+ try {
46299
+ await promptOrModelError(session.prompt(runBrief, { expandPromptTemplates: false }), (reject) => {
46300
+ rejectCurrentPromptOnModelError = reject;
46301
+ });
46302
+ if (emittedHandoff === void 0) await promptOrModelError(session.prompt(buildMissingRunHandoffPrompt(observedToolAttempts), { expandPromptTemplates: false }), (reject) => {
46303
+ rejectCurrentPromptOnModelError = reject;
46304
+ });
46305
+ } catch (error) {
46306
+ if (emittedHandoff === void 0) throw error;
46307
+ progress?.("Recovered terminal write_handoff after the agent session stopped.");
46308
+ }
46175
46309
  } finally {
46310
+ rejectCurrentPromptOnModelError = void 0;
46176
46311
  runtime.writeSessionState();
46177
46312
  unsubscribe();
46178
46313
  runtime.dispose();
@@ -46182,7 +46317,23 @@ async function runQuireAgent({ intent, runBrief, runInstructions, remoteAddons,
46182
46317
  runDir
46183
46318
  });
46184
46319
  }
46185
- async function createQuireAgentRuntime({ runDir, codebasePath, sessionId, headed, intent, progress, onProgressEvent, runInstructions, remoteAddons, runtimeCapabilities, followup, skillPack, mcpGateway }) {
46320
+ async function promptOrModelError(prompt, setRejectCurrentPromptOnModelError) {
46321
+ try {
46322
+ return await Promise.race([prompt, new Promise((_resolve, reject) => {
46323
+ setRejectCurrentPromptOnModelError(reject);
46324
+ })]);
46325
+ } finally {
46326
+ setRejectCurrentPromptOnModelError(void 0);
46327
+ }
46328
+ }
46329
+ function modelErrorFromSessionEvent(event) {
46330
+ if (!isRecord$14(event) || event.type !== "message" || !isRecord$14(event.message)) return;
46331
+ const message = event.message;
46332
+ if (message.role !== "assistant" || message.stopReason !== "error") return;
46333
+ const errorMessage = typeof message.errorMessage === "string" && message.errorMessage.trim().length > 0 ? message.errorMessage.trim() : "model request failed";
46334
+ return /* @__PURE__ */ new Error(`Quire model request failed: ${errorMessage}`);
46335
+ }
46336
+ async function createQuireAgentRuntime({ runDir, codebasePath, sessionId, headed, intent, progress, onProgressEvent, runInstructions, remoteAddons, runtimeCapabilities, followup, skillPack, mcpGateway, onHandoff }) {
46186
46337
  const logAgentProgress = createAgentProgressLogger({
46187
46338
  runDir,
46188
46339
  write: progress
@@ -46212,6 +46363,7 @@ async function createQuireAgentRuntime({ runDir, codebasePath, sessionId, headed
46212
46363
  gateway: mcpGateway,
46213
46364
  runId: runDir.runId
46214
46365
  });
46366
+ const writeHandoffTool = makeWriteHandoffTool({ onHandoff });
46215
46367
  const registeredTargetTools = [...browserTool === void 0 ? [] : [{
46216
46368
  toolName: "agent-browser",
46217
46369
  target: "web"
@@ -48183,6 +48335,8 @@ function runPath(runDir) {
48183
48335
  }
48184
48336
  //#endregion
48185
48337
  //#region src/pipeline/background-investigation.ts
48338
+ const WORKER_BOOTSTRAP_TIMEOUT_MS = 2e3;
48339
+ const WORKER_LOG_FILE = "worker.log";
48186
48340
  async function startInvestigationRun(options) {
48187
48341
  assertHasInvestigationInput(options.query, options.inputText);
48188
48342
  const repoPath = resolve(options.cwd ?? process.cwd());
@@ -48238,6 +48392,8 @@ async function startInvestigationRun(options) {
48238
48392
  runId: runDir.runId,
48239
48393
  workspaceKey: runDir.workspaceKey,
48240
48394
  runPath: runDir.root,
48395
+ runsRoot: runDir.runsRoot,
48396
+ temporaryRunsRoot: runDir.temporaryRunsRoot === true,
48241
48397
  pid: worker.pid
48242
48398
  });
48243
48399
  }
@@ -48315,6 +48471,8 @@ function workerStartedBrowserSessionIds(runDir) {
48315
48471
  function spawnInvestigationWorker({ runPath }) {
48316
48472
  const entryPoint = process.argv[1];
48317
48473
  if (entryPoint === void 0 || entryPoint.length === 0) throw new CliError$1("Unable to locate Quire CLI entrypoint for background worker.", ExitCode.InternalError);
48474
+ const workerLogPath = `${runPath}/${WORKER_LOG_FILE}`;
48475
+ const workerLogFd = openSync(workerLogPath, "a", 384);
48318
48476
  const child = spawn(process.execPath, [
48319
48477
  ...process.execArgv,
48320
48478
  entryPoint,
@@ -48327,23 +48485,89 @@ function spawnInvestigationWorker({ runPath }) {
48327
48485
  env: process.env,
48328
48486
  stdio: [
48329
48487
  "ignore",
48330
- "ignore",
48331
- "ignore"
48488
+ workerLogFd,
48489
+ workerLogFd
48332
48490
  ]
48333
48491
  });
48334
- child.unref();
48335
- return Promise.resolve({ pid: child.pid });
48492
+ closeSync(workerLogFd);
48493
+ return waitForWorkerBootstrap({
48494
+ child,
48495
+ runPath,
48496
+ workerLogPath
48497
+ }).finally(() => {
48498
+ child.unref();
48499
+ });
48500
+ }
48501
+ async function waitForWorkerBootstrap({ child, runPath, workerLogPath }) {
48502
+ const exit = new Promise((resolveExit) => {
48503
+ child.once("exit", (code, signal) => resolveExit({
48504
+ code,
48505
+ signal
48506
+ }));
48507
+ });
48508
+ const timeout = new Promise((resolveTimeout) => {
48509
+ setTimeout(() => resolveTimeout("timeout"), WORKER_BOOTSTRAP_TIMEOUT_MS).unref();
48510
+ });
48511
+ const started = waitForRunToLeaveQueued(runPath);
48512
+ const result = await Promise.race([
48513
+ exit,
48514
+ timeout,
48515
+ started.promise
48516
+ ]);
48517
+ started.stop();
48518
+ if (result === "started") return { pid: child.pid };
48519
+ const status = readRunStatus(runPath);
48520
+ if (result !== "timeout" && status?.status === "queued") throw new CliError$1(formatEarlyWorkerExitError({
48521
+ workerLogPath,
48522
+ code: result.code,
48523
+ signal: result.signal
48524
+ }), ExitCode.InternalError);
48525
+ return { pid: child.pid };
48526
+ }
48527
+ function waitForRunToLeaveQueued(runPath) {
48528
+ let stopped = false;
48529
+ return {
48530
+ promise: new Promise((resolveStarted) => {
48531
+ const checkStatus = () => {
48532
+ if (stopped) return;
48533
+ const status = readRunStatus(runPath);
48534
+ if (status !== void 0 && status.status !== "queued") {
48535
+ resolveStarted("started");
48536
+ return;
48537
+ }
48538
+ setTimeout(checkStatus, 25).unref();
48539
+ };
48540
+ checkStatus();
48541
+ }),
48542
+ stop: () => {
48543
+ stopped = true;
48544
+ }
48545
+ };
48546
+ }
48547
+ function formatEarlyWorkerExitError({ workerLogPath, code, signal }) {
48548
+ const exit = signal === null ? `exit code ${code ?? "unknown"}` : `signal ${signal}`;
48549
+ const tail = readWorkerLogTail(workerLogPath);
48550
+ return `Worker process exited before marking the run as running (${exit}).\n\n${tail.length === 0 ? `Worker log: ${workerLogPath} (empty)` : `Worker log: ${workerLogPath}\n\nLast worker output:\n${tail}`}`;
48551
+ }
48552
+ function readWorkerLogTail(workerLogPath) {
48553
+ try {
48554
+ return readFileSync(workerLogPath, "utf8").slice(-12e3).trim();
48555
+ } catch {
48556
+ return "";
48557
+ }
48336
48558
  }
48337
48559
  function runHandle(input) {
48560
+ const commandPrefix = input.temporaryRunsRoot ? `QUIRE_RUNS_DIR=${shellQuote(input.runsRoot)} ` : "";
48338
48561
  return {
48339
48562
  status: "queued",
48340
48563
  runId: input.runId,
48341
48564
  workspaceKey: input.workspaceKey,
48342
48565
  runPath: toJsonPath(input.runPath),
48343
48566
  ...input.pid === void 0 ? {} : { pid: input.pid },
48344
- statusCommand: `quire status ${shellQuote(input.runId)} --json`,
48345
- watchCommand: `quire watch ${shellQuote(input.runId)}`,
48346
- cancelCommand: `quire cancel ${shellQuote(input.runId)}`
48567
+ statusCommand: `${commandPrefix}quire status ${shellQuote(input.runId)} --json`,
48568
+ waitCommand: `${commandPrefix}quire wait ${shellQuote(input.runId)} --json`,
48569
+ watchCommand: `${commandPrefix}quire watch ${shellQuote(input.runId)}`,
48570
+ cancelCommand: `${commandPrefix}quire cancel ${shellQuote(input.runId)}`
48347
48571
  };
48348
48572
  }
48349
48573
  function readWorkerRequest(runPath) {
@@ -48528,6 +48752,38 @@ const watchCommand = defineCommand({
48528
48752
  await watchRun(readRequiredString(args.run, "run"));
48529
48753
  }
48530
48754
  });
48755
+ const waitCommand = defineCommand({
48756
+ meta: {
48757
+ name: "wait",
48758
+ description: "Wait for a Quire investigation run to reach a terminal status."
48759
+ },
48760
+ args: {
48761
+ run: {
48762
+ type: "positional",
48763
+ description: "Run id or run directory.",
48764
+ required: true
48765
+ },
48766
+ json: {
48767
+ type: "boolean",
48768
+ description: "Emit terminal status JSON."
48769
+ },
48770
+ "timeout-ms": {
48771
+ type: "string",
48772
+ description: "Maximum time to wait in milliseconds. Defaults to 600000."
48773
+ },
48774
+ "interval-ms": {
48775
+ type: "string",
48776
+ description: "Polling interval in milliseconds. Defaults to 5000."
48777
+ }
48778
+ },
48779
+ async run({ args }) {
48780
+ await waitRun(readRequiredString(args.run, "run"), {
48781
+ json: args.json === true,
48782
+ timeoutMs: readPositiveIntegerOption(args["timeout-ms"], "--timeout-ms") ?? 6e5,
48783
+ intervalMs: readPositiveIntegerOption(args["interval-ms"], "--interval-ms") ?? 5e3
48784
+ });
48785
+ }
48786
+ });
48531
48787
  const cancelCommand = defineCommand({
48532
48788
  meta: {
48533
48789
  name: "cancel",
@@ -48653,6 +48909,28 @@ async function watchRun(run, options = {}) {
48653
48909
  if (sigintHandler !== void 0) process.off("SIGINT", sigintHandler);
48654
48910
  }
48655
48911
  }
48912
+ async function waitRun(run, options = {}) {
48913
+ const stdout = options.io?.stdout ?? process.stdout;
48914
+ const runPath = resolveRunPath(run, options);
48915
+ const sleep = options.sleep ?? defaultSleep$1;
48916
+ const intervalMs = options.intervalMs ?? 5e3;
48917
+ const timeoutMs = options.timeoutMs ?? 6e5;
48918
+ const now = options.now ?? Date.now;
48919
+ const deadline = now() + timeoutMs;
48920
+ while (true) {
48921
+ const status = withSyncStatus(readFreshRunStatus(runPath));
48922
+ if (isTerminalRunStatus(status.status)) {
48923
+ if (options.json === true) writeJson(stdout, status);
48924
+ else {
48925
+ writeLine(stdout, renderFinalStatus(status));
48926
+ writeHandoffSummary(stdout, runDirFromStatus(status).paths.handoff);
48927
+ }
48928
+ return status;
48929
+ }
48930
+ if (now() >= deadline) throw new CliError$1(`Timed out waiting for run ${status.runId} to finish; latest status is ${status.status}. Use \`quire status ${status.runId} --json\` or \`quire watch ${status.runId}\` to inspect progress.`, ExitCode.InvalidInput);
48931
+ await sleep(Math.min(intervalMs, Math.max(0, deadline - now())));
48932
+ }
48933
+ }
48656
48934
  async function cancelRun(run, options = {}) {
48657
48935
  const stdout = options.io?.stdout ?? process.stdout;
48658
48936
  const runPath = resolveRunPath(run, options);
@@ -48713,9 +48991,15 @@ async function syncRun(run, options = {}) {
48713
48991
  function withSyncStatus(status) {
48714
48992
  const sync = readInvestigationSyncStatus(runDirFromStatus(status));
48715
48993
  const { sync: _storedSync, ...baseStatus } = status;
48716
- return sync === void 0 ? baseStatus : {
48994
+ if (sync === void 0) return baseStatus;
48995
+ return sync.caseUrl === void 0 ? {
48717
48996
  ...baseStatus,
48718
48997
  sync
48998
+ } : {
48999
+ ...baseStatus,
49000
+ sync,
49001
+ webUrl: sync.caseUrl,
49002
+ caseUrl: sync.caseUrl
48719
49003
  };
48720
49004
  }
48721
49005
  function signalRunProcess(pid, signal) {
@@ -48738,6 +49022,13 @@ function statusColor(status) {
48738
49022
  function formatNumber(value) {
48739
49023
  return new Intl.NumberFormat("en-US").format(value);
48740
49024
  }
49025
+ function readPositiveIntegerOption(value, name) {
49026
+ if (value === void 0 || value === null || value === false) return;
49027
+ if (typeof value !== "string" || value.trim().length === 0) throw new CliError$1(`${name} must be a positive integer.`, ExitCode.InvalidInput);
49028
+ const parsed = Number(value);
49029
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new CliError$1(`${name} must be a positive integer.`, ExitCode.InvalidInput);
49030
+ return parsed;
49031
+ }
48741
49032
  function defaultSleep$1(ms) {
48742
49033
  return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
48743
49034
  }
@@ -48797,6 +49088,7 @@ const investigationCommandSchema = {
48797
49088
  "workspaceKey",
48798
49089
  "runPath",
48799
49090
  "statusCommand",
49091
+ "waitCommand",
48800
49092
  "watchCommand",
48801
49093
  "cancelCommand"
48802
49094
  ],
@@ -48810,6 +49102,7 @@ const investigationCommandSchema = {
48810
49102
  optional: true
48811
49103
  },
48812
49104
  statusCommand: { type: "string" },
49105
+ waitCommand: { type: "string" },
48813
49106
  watchCommand: { type: "string" },
48814
49107
  cancelCommand: { type: "string" }
48815
49108
  }
@@ -48817,6 +49110,7 @@ const investigationCommandSchema = {
48817
49110
  followUpCommands: {
48818
49111
  continue: "quire continue <run-id> \"additional context or instruction\"",
48819
49112
  status: "quire status <run-id> --json",
49113
+ wait: "quire wait <run-id> --json",
48820
49114
  watch: "quire watch <run-id>",
48821
49115
  cancel: "quire cancel <run-id>"
48822
49116
  },
@@ -48851,6 +49145,7 @@ const investigationCommandSchema = {
48851
49145
  "quire \"verify checkout before I approve this PR\"",
48852
49146
  "printf 'why is CI failing?\\nRelevant log: ...' | quire run --json",
48853
49147
  "quire status run_9x4mdq7p2h8kc6nv4apz --json",
49148
+ "quire wait run_9x4mdq7p2h8kc6nv4apz --json",
48854
49149
  "quire watch run_9x4mdq7p2h8kc6nv4apz"
48855
49150
  ]
48856
49151
  };
@@ -48938,7 +49233,9 @@ async function runInvestigate(options) {
48938
49233
  });
48939
49234
  if (options.detach === true) log.message([
48940
49235
  `${color.label("Run")}: ${color.path(handle.runPath)}`,
49236
+ ...handle.webUrl === void 0 ? [] : [`${color.label("Case")}: ${handle.webUrl}`],
48941
49237
  `${color.label("Status")}: ${color.command(handle.statusCommand)}`,
49238
+ `${color.label("Wait")}: ${color.command(handle.waitCommand)}`,
48942
49239
  `${color.label("Watch")}: ${color.command(handle.watchCommand)}`,
48943
49240
  `${color.label("Cancel")}: ${color.command(handle.cancelCommand)}`
48944
49241
  ], {
@@ -48949,15 +49246,16 @@ async function runInvestigate(options) {
48949
49246
  else log.message([
48950
49247
  `${color.label("Run")}: ${color.path(handle.runPath)}`,
48951
49248
  `${color.label("Status")}: ${color.command(handle.statusCommand)}`,
49249
+ `${color.label("Wait")}: ${color.command(handle.waitCommand)}`,
48952
49250
  `${color.label("Watch")}: ${color.command(handle.watchCommand)}`,
48953
- `This run continues in the background. You can safely exit; use ${color.command(handle.statusCommand)} for the current state or ${color.command(handle.watchCommand)} to follow progress logs.`
49251
+ `This run continues in the background. You can safely exit; use ${color.command(handle.waitCommand)} for the terminal handoff, ${color.command(handle.statusCommand)} for the current state, or ${color.command(handle.watchCommand)} to follow progress logs.`
48954
49252
  ], {
48955
49253
  output: stdout,
48956
49254
  spacing: 0,
48957
49255
  withGuide: true
48958
49256
  });
48959
49257
  }
48960
- if (options.json !== true && options.detach !== true) await (options.watchRunner ?? watchRun)(handle.runId, {
49258
+ if (options.json !== true && options.detach !== true) await (options.watchRunner ?? watchRun)(handle.runPath, {
48961
49259
  cwd: options.cwd,
48962
49260
  runsRoot: options.runsRoot,
48963
49261
  io: { stdout }
@@ -49098,11 +49396,13 @@ function pluralize(count, singular, plural) {
49098
49396
  //#region src/doctor/checks.ts
49099
49397
  const execFileAsync = promisify(execFile);
49100
49398
  function createDoctorContext(overrides = {}) {
49399
+ const env = overrides.env ?? process.env;
49101
49400
  return {
49102
49401
  cwd: resolve(overrides.cwd ?? process.cwd()),
49103
- runsRoot: resolve(overrides.runsRoot ?? defaultRunsRoot()),
49402
+ runsRoot: resolve(overrides.runsRoot ?? defaultRunsRoot(env)),
49403
+ runsRootExplicit: overrides.runsRootExplicit ?? (overrides.runsRoot !== void 0 || (env.QUIRE_RUNS_DIR?.trim().length ?? 0) > 0),
49104
49404
  authStore: overrides.authStore ?? authStore,
49105
- env: overrides.env ?? process.env,
49405
+ env,
49106
49406
  fetchFn: overrides.fetchFn ?? fetch,
49107
49407
  execFn: overrides.execFn ?? defaultExecFn,
49108
49408
  probeTimeoutMs: overrides.probeTimeoutMs ?? 2e3
@@ -49294,6 +49594,22 @@ async function checkRunsRoot(context) {
49294
49594
  message: `Runs root writable (${context.runsRoot}).`
49295
49595
  };
49296
49596
  } catch (error) {
49597
+ if (!context.runsRootExplicit && isWriteAccessError(error)) {
49598
+ const fallbackRunsRoot = await mkdtemp(join(tmpdir(), "quire-runs-"));
49599
+ await mkdir(fallbackRunsRoot, {
49600
+ recursive: true,
49601
+ mode: 448
49602
+ });
49603
+ await access(fallbackRunsRoot, constants.W_OK);
49604
+ context.runsRoot = fallbackRunsRoot;
49605
+ return {
49606
+ id: "runs.rootWritable",
49607
+ section: "runs",
49608
+ severity: "warn",
49609
+ message: `Default runs root not writable (${toMessage(error)}); using temporary runs root ${fallbackRunsRoot}.`,
49610
+ fix: { command: `Set QUIRE_RUNS_DIR=${fallbackRunsRoot} for follow-up Quire commands in this shell.` }
49611
+ };
49612
+ }
49297
49613
  return {
49298
49614
  id: "runs.rootWritable",
49299
49615
  section: "runs",
@@ -49338,6 +49654,9 @@ function checkStuckRuns(context) {
49338
49654
  };
49339
49655
  }
49340
49656
  }
49657
+ function isWriteAccessError(error) {
49658
+ return error instanceof Error && "code" in error && (error.code === "EROFS" || error.code === "EACCES" || error.code === "EPERM");
49659
+ }
49341
49660
  function formatUser$2(user) {
49342
49661
  if (user.email !== void 0 && user.email !== null && user.email.length > 0) return user.email;
49343
49662
  if (user.name !== void 0 && user.name !== null && user.name.length > 0) return user.name;
@@ -49648,6 +49967,7 @@ const logoutCommand = defineCommand({
49648
49967
  async function runLogout(options) {
49649
49968
  const store = options.store ?? authStore;
49650
49969
  const stdout = options.io?.stdout ?? process.stdout;
49970
+ const env = options.env ?? process.env;
49651
49971
  const existing = await store.get();
49652
49972
  await store.clear();
49653
49973
  if (existing === null) log.info("Already logged out.", {
@@ -49658,48 +49978,387 @@ async function runLogout(options) {
49658
49978
  output: stdout,
49659
49979
  spacing: 0
49660
49980
  });
49981
+ if (env["QUIRE_API_TOKEN"]?.trim()) log.warn(`${QUIRE_API_TOKEN_ENV} is still set, so Quire commands may still authenticate.`, {
49982
+ output: stdout,
49983
+ spacing: 0
49984
+ });
49661
49985
  }
49662
49986
  //#endregion
49663
49987
  //#region src/commands/setup.ts
49664
- const SETUP_URL = "https://quire.sh/agent-setup";
49665
- const VERIFICATION_SKILL_URL = "https://quire.sh/agent-verification";
49666
- const AGENT_SETUP_PROMPT = `Set up this repository for Quire investigations.
49988
+ const DEFAULT_PUBLIC_BASE_URL = "https://quire.sh";
49989
+ const SETUP_URL_PLACEHOLDER = "__QUIRE_SETUP_URL__";
49990
+ const VERIFICATION_SKILL_URL_PLACEHOLDER = "__QUIRE_VERIFICATION_SKILL_URL__";
49991
+ const AGENT_SETUP_PROMPT = `Help me connect Quire to this repository end-to-end.
49992
+
49993
+ Use or install the Quire CLI. If \`quire\` is unavailable, use \`npx @quireco/cli@latest\` for this setup or ask before installing it globally.
49994
+
49995
+ Load and follow the Quire setup skill from ${SETUP_URL_PLACEHOLDER}.
49996
+
49997
+ Check \`quire whoami --json\` and \`quire doctor --json\` using the same Quire command throughout setup. If account auth is missing, tell me exactly what I need to do, run \`quire login\` when browser auth is appropriate, and wait for me to finish. Do not ask me to paste secrets into chat.
49998
+
49999
+ After login, Quire Wallet is the default model source. If Wallet has balance, use it. If Wallet needs funds, show me the top-up action from \`quire whoami --json\` or open https://quire.sh/dashboard?settings=billing. If I prefer to use an existing subscription instead, help me connect ChatGPT/Codex with \`quire connect chatgpt\` or GitHub Copilot with \`quire connect copilot\`.
50000
+
50001
+ Inspect the project, ask focused questions for important missing runbook facts instead of writing placeholder unknowns, then create or update .quire/runbook.md with this repo's verification loop: target selection, local startup, browser/API/CLI/mobile checks, console/runtime signals, core flows, trusted commands, design/reference surfaces, auth/data setup, performance/accessibility expectations, and mutation-safety rules.
50002
+
50003
+ Update AGENTS.md with concise Quire notes. Rerun \`quire doctor\`, recover from sandbox write issues if needed, then start one small read-only smoke run with \`quire run --detach --json\`. Wait for a terminal result and report the synced Quire run link, local report/evidence path, and any blocker with the exact next action.`;
50004
+ const QUIRE_VERIFY_SKILL_MARKDOWN = `---
50005
+ name: verify
50006
+ description: Verify claims, code changes, bug fixes, workflows, and release readiness with independent evidence. Use when asked to verify, QA, smoke test, prove a fix, or check non-trivial work before completion.
50007
+ ---
50008
+
50009
+ # Verify
50010
+
50011
+ Load and follow the current Quire verification skill from ${VERIFICATION_SKILL_URL_PLACEHOLDER}.
50012
+
50013
+ Use this skill to prove whether a claim, code change, bug fix, test result, workflow, or release/readiness assertion is actually correct. Quire is the independent read-only QA/verification agent for this workflow. Quire does not implement fixes. Quire reports pass, fail, blocked, or partial results honestly with evidence.
50014
+
50015
+ ## When to use this skill
50016
+
50017
+ Use this skill when the user asks to verify, QA, smoke test, validate, prove a fix, check a workflow, confirm a release claim, ask Quire, run Quire, or get a Quire run id/verdict/handoff.
50018
+
50019
+ Also use Quire before declaring non-trivial coding work complete when independent verification would materially increase confidence, especially for UI/workflow changes, bug fixes, failing or flaky tests, auth, security, permissions, billing, data loss, migrations, concurrency, integrations, or release-readiness work.
49667
50020
 
49668
- Please load and follow the Quire agent setup instructions from ${SETUP_URL}.
50021
+ Do not use Quire for every tiny task. Usually skip independent verification for purely conversational answers, simple docs/formatting edits, trivial mechanical changes where local checks are enough, or repeated verification of the same unchanged diff. Exception: if the user explicitly asks for Quire or independent verification, use Quire anyway.
49669
50022
 
49670
- Inspect the project, ask focused questions for important missing runbook facts instead of writing placeholder unknowns, then create a .quire/runbook.md that encodes the best-practices verification loop for this specific project. Capture how Quire should self-verify work in this domain: target/environment choice, local startup, browser/API/CLI/mobile checks, console/runtime signals, core flows, verification commands, design/reference surfaces, auth/data setup, performance/accessibility expectations, and mutation-safety rules. Update AGENTS.md with concise Quire notes, then run Quire doctor using the available Quire command, e.g. \`quire doctor\` or \`npx @quireco/cli doctor\`, and report anything still blocked.`;
49671
- const QUIRE_SKILL_MARKDOWN = `---
50023
+ ## Hard requirement
50024
+
50025
+ If the caller explicitly asks for Quire or independent verification, do not substitute your own direct checks, web fetches, curl commands, tests, browser checks, or manual reasoning and claim that is equivalent. You may do basic preparation, but Quire itself must be invoked.
50026
+
50027
+ Recommended path: if this harness exposes a native subagent named \`quire\`, delegate the verification brief to that subagent and wait for its result. Do not decide the task is simple enough to run \`quire\` directly from the parent session when a native \`quire\` subagent is available. Use CLI fallback only when no native \`quire\` subagent is available.
50028
+
50029
+ If the caller asks whether the Quire agent/subagent path was used, answer from your actual invocation path. If you used a native \`quire\` child task/subagent, report that the native Quire subagent path was used even when \`quire wait\` returns metadata for an older run whose internal harness was \`pi\`, Codex, or another agent. Treat old run metadata as separate historical context, not as evidence that the current adapter path was not used.
50030
+
50031
+ When communicating Quire results to the user, surface the synced Quire run link from \`webUrl\`, \`caseUrl\`, or \`sync.caseUrl\` when present. Include the report/handoff markdown summary and call out useful artifacts such as screenshots, recordings, traces, or other evidence when they help the user trust or review the result.
50032
+
50033
+ ## How to invoke Quire
50034
+
50035
+ Preferred order:
50036
+
50037
+ 1. If this harness exposes a native subagent named \`quire\`, delegate the verification brief to that subagent and wait for its result.
50038
+ 2. If no native Quire subagent is available, run the CLI directly.
50039
+
50040
+ CLI fallback:
50041
+
50042
+ \`\`\`bash
50043
+ run_json=$(quire run --detach --json --url "<url>" "<verification request>")
50044
+ run_id=$(printf '%s' "$run_json" | jq -r '.runId')
50045
+ quire wait "$run_id" --json
50046
+ \`\`\`
50047
+
50048
+ If the caller provides an existing Quire run id, do not start a new run and do not use \`quire status\` as the final result. Wait for and return the handoff with:
50049
+
50050
+ \`\`\`bash
50051
+ quire wait "<run-id>" --json
50052
+ \`\`\`
50053
+
50054
+ The JSON output includes \`webUrl\` / \`caseUrl\` when the run has synced to Quire's web app. Use that link as the shareable evidence URL in user-facing summaries.
50055
+
50056
+ Omit \`--url\` when there is no URL and include the target context in the natural-language request. Use \`quire wait\` as the waiting primitive. Do not manually poll unless \`quire wait\` is unavailable.
50057
+
50058
+ If Quire reports \`EROFS\`, \`EACCES\`, or another write error under \`~/.local/share/quire/runs\`, retry with a temporary run store and reuse the same value for every lifecycle command:
50059
+
50060
+ \`\`\`bash
50061
+ quire_runs_dir=$(mktemp -d -t quire-runs-XXXXXX)
50062
+ QUIRE_RUNS_DIR=$quire_runs_dir quire run --detach --json --url "<url>" "<verification request>"
50063
+ QUIRE_RUNS_DIR=$quire_runs_dir quire wait "<run-id>" --json
50064
+ \`\`\`
50065
+
50066
+ ## Verification brief template
50067
+
50068
+ - Claim/change to verify:
50069
+ - Relevant files/paths:
50070
+ - Commands already run:
50071
+ - Expected behavior:
50072
+ - Known risks or edge cases:
50073
+ - Constraints: read-only verification; do not edit code.
50074
+
50075
+ Return Quire's terminal handoff with:
50076
+
50077
+ - Verdict: pass, fail, blocked, partial, or setup-blocked
50078
+ - Quire run id
50079
+ - Quire web URL when present
50080
+ - What Quire verified
50081
+ - Key evidence, artifacts, report, or handoff excerpt
50082
+ - Follow-up actions for the implementation agent
50083
+ `;
50084
+ const QUIRE_DEBUG_SKILL_MARKDOWN = `---
50085
+ name: debug
50086
+ description: Diagnose bugs, regressions, flaky behavior, and broken workflows with a reproduce-first loop. Use when asked to debug, diagnose, investigate a failure, reproduce a bug, or triage a regression.
50087
+ ---
50088
+
50089
+ # Debug
50090
+
50091
+ Use this skill for disciplined diagnosis: reproduce the failure, minimize the loop, form hypotheses, instrument only what distinguishes them, fix, and regression-test. Quire is the independent evidence collector for this workflow when a fresh reproduction, browser/API/mobile check, or external verification handoff would materially improve confidence.
50092
+
50093
+ ## When to use Quire during diagnosis
50094
+
50095
+ Use the native \`quire\` subagent when you need an independent read-only investigator to:
50096
+
50097
+ - reproduce a reported bug or flaky workflow;
50098
+ - verify a suspected fix against the real app, browser, API, CLI, or mobile surface;
50099
+ - gather evidence such as screenshots, recordings, console output, terminal logs, or a concise handoff;
50100
+ - check whether a failure is environment/setup-blocked versus a product bug;
50101
+ - produce a run id and terminal verdict that another agent can use in an implementation/verification loop.
50102
+
50103
+ Do not outsource implementation to Quire. Quire should not edit code. The parent/debugging agent owns hypotheses, code changes, and regression tests.
50104
+
50105
+ ## Recommended invocation
50106
+
50107
+ 1. Build or identify the best reproduction loop you can from the report.
50108
+ 2. If this harness exposes a native subagent named \`quire\`, delegate a concise reproduction or verification brief to that subagent and wait for its handoff.
50109
+ 3. If no native \`quire\` subagent is available, use the Quire CLI fallback from ${VERIFICATION_SKILL_URL_PLACEHOLDER}.
50110
+ 4. Use Quire's result as evidence, not as a substitute for understanding the code path. When communicating the result, surface the synced Quire run link from \`webUrl\`, \`caseUrl\`, or \`sync.caseUrl\` when present, and mention useful report artifacts such as screenshots, recordings, traces, or the handoff markdown when they help the user review the diagnosis. If Quire reports blocked, preserve that honestly and fix the setup or ask for the missing environment.
50111
+
50112
+ ## Quire brief template
50113
+
50114
+ - Failure or regression to reproduce:
50115
+ - User-visible symptom:
50116
+ - Target URL, command, app, or environment:
50117
+ - Steps already tried:
50118
+ - Expected behavior:
50119
+ - Actual behavior:
50120
+ - Evidence to capture:
50121
+ - Constraints: read-only investigation; do not edit code.
50122
+
50123
+ ## CLI fallback
50124
+
50125
+ Use CLI fallback only if the native \`quire\` subagent is unavailable. Prefer detached JSON mode and wait for the terminal handoff:
50126
+
50127
+ \`\`\`bash
50128
+ run_json=$(quire run --detach --json --url "<url>" "<reproduction or verification request>")
50129
+ run_id=$(printf '%s' "$run_json" | jq -r '.runId')
50130
+ quire wait "$run_id" --json
50131
+ \`\`\`
50132
+
50133
+ If the caller provides an existing Quire run id, do not start a new run and do not use \`quire status\` as the final result:
50134
+
50135
+ \`\`\`bash
50136
+ quire wait "<run-id>" --json
50137
+ \`\`\`
50138
+
50139
+ For more CLI details, sandbox recovery, and handoff interpretation, follow ${VERIFICATION_SKILL_URL_PLACEHOLDER}.
50140
+ `;
50141
+ const QUIRE_AGENT_MARKDOWN = `---
49672
50142
  name: quire
49673
- description: Runs Quire CLI evidence-backed QA, verification, triage, and investigation work. Use after implementing UI or workflow changes, when testing local/preview/prod URLs, or when claims need screenshots, command output, observations, and a trustworthy handoff.
50143
+ description: "Run Quire, the independent read-only QA/verification/triage agent. Use when the user explicitly asks for Quire or when independent verification of a non-trivial change, bug fix, test failure, or release claim is warranted. Returns pass, fail, blocked, or partial with evidence; never edits code."
50144
+ color: green
50145
+ model: inherit
49674
50146
  ---
49675
50147
 
49676
- # Quire verification
50148
+ You are Quire's native harness adapter. Your job is to launch Quire through the Quire CLI, wait for the result, and return the evidence-backed result to the parent agent.
50149
+
50150
+ Do not implement code changes. Do not commit, stage, format, rewrite, patch, or edit files. Do not perform broad independent QA outside Quire unless Quire cannot run and the caller explicitly asks for fallback evidence. Do not read or print secrets, tokens, .env files, or credential vaults. Treat production targets as read-only unless the caller explicitly approves mutation.
50151
+
50152
+ When reporting whether the Quire agent/subagent path was used, answer only about this native adapter invocation. If you are running these instructions as the \`quire\` subagent, then the native Quire subagent path was used. Do not add a second "internal run harness" answer, do not discuss a previous run's \`harness: "pi"\` metadata, and do not answer "no" for the current adapter path.
49677
50153
 
49678
- Load and follow the current Quire verification skill from ${VERIFICATION_SKILL_URL}.
50154
+ ## Workflow
49679
50155
 
49680
- Use Quire when a task needs evidence-backed verification, QA, product dogfooding, bug reproduction, or triage. Prefer \`quire run\` for new work.
50156
+ 1. Convert the parent request into a concise Quire verification brief.
50157
+ 2. Prefer the local \`quire\` command. Use the exact command style available in the workspace if the caller provides one. Do not downgrade an explicit Quire request to direct curl/browser/test checks, and do not report \`N/A\` for the run id because the claim looked small.
50158
+ 3. Include \`--url <url>\` when the caller provides a URL or the project runbook clearly identifies the local/preview target.
50159
+ 4. Start one Quire run for the verification claim. Do not start duplicate runs for the same claim just because the first run is still active.
50160
+
50161
+ If the caller provides an existing Quire run id, do not start a new run. Use \`quire wait "<run-id>" --json\` and return its terminal handoff.
50162
+
50163
+ Prefer detached JSON mode for agent handoffs:
50164
+
50165
+ \`\`\`bash
50166
+ run_json=$(quire run --detach --json --url "<url>" "<verification request>")
50167
+ run_id=$(printf '%s' "$run_json" | jq -r '.runId')
50168
+ \`\`\`
50169
+
50170
+ If there is no URL, omit \`--url\` and put target context in the natural-language request.
50171
+
50172
+ 5. Wait patiently until terminal status:
50173
+
50174
+ \`\`\`bash
50175
+ quire wait "$run_id" --json
50176
+ \`\`\`
50177
+
50178
+ If Quire reports \`EROFS\`, \`EACCES\`, or another write error under \`~/.local/share/quire/runs\`, retry with a temporary run store and reuse the same value for every lifecycle command:
50179
+
50180
+ \`\`\`bash
50181
+ quire_runs_dir=$(mktemp -d -t quire-runs-XXXXXX)
50182
+ QUIRE_RUNS_DIR=$quire_runs_dir quire run --detach --json --url "<url>" "<verification request>"
50183
+ QUIRE_RUNS_DIR=$quire_runs_dir quire wait "<run-id>" --json
50184
+ \`\`\`
50185
+
50186
+ Terminal statuses are \`completed\`, \`failed\`, \`canceled\`, and \`stale\`. Quire can take several minutes while it inspects context, opens browsers, runs shell checks, captures evidence, and writes a handoff.
50187
+
50188
+ 6. Return \`finalHandoff.handoffMarkdown\` from \`quire wait --json\` when present. Include the synced Quire run link from \`webUrl\`, \`caseUrl\`, or \`sync.caseUrl\` when present, and mention useful report artifacts such as screenshots, recordings, traces, or evidence references when they help the parent agent communicate the result.
50189
+ 7. If Quire reports blocked, failed, stale, or untested checks, preserve that honestly and include the next action. Do not summarize blocked or untested assertions as passed.
50190
+ 8. If Quire cannot start because auth, model provider, wallet, browser, mobile, or target setup is missing, report setup-blocked with the exact reason and the next command Quire suggested.
50191
+
50192
+ ## Return format
50193
+
50194
+ - Native Quire adapter invocation: yes, if you are running as the \`quire\` subagent.
50195
+ - Verdict: pass, fail, blocked, partial, or setup-blocked
50196
+ - Quire run id
50197
+ - Quire web URL when present
50198
+ - What Quire verified
50199
+ - Key evidence, artifacts, report, or handoff excerpt
50200
+ - Follow-up actions for the implementation agent
49681
50201
  `;
49682
- const SKILL_INSTALL_TARGETS = [{
49683
- id: "agents",
49684
- label: "Universal",
49685
- targetPath: ".agents/skills/quire/SKILL.md",
49686
- hint: "~/.agents/skills/quire/SKILL.md · Pi, OpenCode, Amp, Codex-style agents",
49687
- aliases: [
49688
- "agents",
49689
- "agent-skills",
49690
- "pi",
49691
- "opencode",
49692
- "amp",
49693
- "codex",
49694
- "other"
49695
- ]
49696
- }, {
49697
- id: "claude",
49698
- label: "Claude Code",
49699
- targetPath: ".claude/skills/quire/SKILL.md",
49700
- hint: "~/.claude/skills/quire/SKILL.md",
49701
- aliases: ["claude", "claude-code"]
49702
- }];
50202
+ const QUIRE_CURSOR_MARKDOWN = QUIRE_AGENT_MARKDOWN.replace("model: inherit\n---", "model: inherit\nreadonly: true\n---");
50203
+ const QUIRE_CODEX_TOML = `name = "quire"
50204
+ description = "Run Quire, the independent read-only QA/verification/triage agent. Use when explicitly asked for Quire or when independent verification of a non-trivial change, bug fix, test failure, or release claim is warranted. Returns pass, fail, blocked, or partial with evidence; never edits code."
50205
+
50206
+ developer_instructions = """
50207
+ You are Quire's native harness adapter. Your job is to launch Quire through the Quire CLI, wait for the result, and return the evidence-backed result to the parent agent.
50208
+
50209
+ Do not implement code changes. Do not commit, stage, format, rewrite, patch, or edit files. Do not perform broad independent QA outside Quire unless Quire cannot run and the caller explicitly asks for fallback evidence. Do not read or print secrets, tokens, .env files, or credential vaults. Treat production targets as read-only unless the caller explicitly approves mutation.
50210
+
50211
+ When reporting whether the Quire agent/subagent path was used, answer only about this native adapter invocation. If you are running these instructions as the \`quire\` subagent, then the native Quire subagent path was used. Do not add a second "internal run harness" answer, do not discuss a previous run's \`harness: "pi"\` metadata, and do not answer "no" for the current adapter path.
50212
+
50213
+ Workflow:
50214
+ 1. Convert the parent request into a concise Quire verification brief.
50215
+ 2. Prefer the local \`quire\` command. Use the exact command style available in the workspace if the caller provides one. Do not downgrade an explicit Quire request to direct curl/browser/test checks, and do not report \`N/A\` for the run id because the claim looked small.
50216
+ 3. Include \`--url <url>\` when the caller provides a URL or the project runbook clearly identifies the local/preview target.
50217
+ 4. Start one Quire run for the verification claim. Do not start duplicate runs for the same claim just because the first run is still active.
50218
+
50219
+ If the caller provides an existing Quire run id, do not start a new run. Use \`quire wait "<run-id>" --json\` and return its terminal handoff.
50220
+
50221
+ run_json=$(quire run --detach --json --url "<url>" "<verification request>")
50222
+ run_id=$(printf '%s' "$run_json" | jq -r '.runId')
50223
+ quire wait "$run_id" --json
50224
+
50225
+ 5. If Quire reports EROFS, EACCES, or another write error under ~/.local/share/quire/runs, retry with a temporary run store and reuse the same value for every lifecycle command:
50226
+
50227
+ quire_runs_dir=$(mktemp -d -t quire-runs-XXXXXX)
50228
+ QUIRE_RUNS_DIR=$quire_runs_dir quire run --detach --json --url "<url>" "<verification request>"
50229
+ QUIRE_RUNS_DIR=$quire_runs_dir quire wait "<run-id>" --json
50230
+
50231
+ 6. Return finalHandoff.handoffMarkdown from \`quire wait --json\` when present. Include the synced Quire run link from \`webUrl\`, \`caseUrl\`, or \`sync.caseUrl\` when present, and mention useful report artifacts such as screenshots, recordings, traces, or evidence references when they help the parent agent communicate the result.
50232
+ 7. If Quire reports blocked, failed, stale, or untested checks, preserve that honestly and include the next action. Do not summarize blocked or untested assertions as passed.
50233
+
50234
+ Return:
50235
+ - Native Quire adapter invocation: yes, if you are running as the \`quire\` subagent.
50236
+ - Verdict: pass, fail, blocked, partial, or setup-blocked
50237
+ - Quire run id
50238
+ - Quire web URL when present
50239
+ - What Quire verified
50240
+ - Key evidence, artifacts, report, or handoff excerpt
50241
+ - Follow-up actions for the implementation agent
50242
+ """
50243
+ `;
50244
+ const QUIRE_OPENCODE_MARKDOWN = `---
50245
+ description: Native @quire subagent. If the user mentions @quire, the parent must invoke the Task tool's quire subagent and must not run Bash/CLI directly in the parent session. For existing run IDs, this subagent uses quire wait <run-id> --json, not quire status. If asked whether the agent/subagent path was used, answer yes for this native child invocation; old run harness metadata is separate historical context.
50246
+ mode: subagent
50247
+ permission:
50248
+ edit: deny
50249
+ ---
50250
+
50251
+ You are Quire's native harness adapter. Your job is to launch Quire through the Quire CLI, wait for the result, and return the evidence-backed result to the parent agent.
50252
+
50253
+ Do not implement code changes. Do not commit, stage, format, rewrite, patch, or edit files. Do not perform broad independent QA outside Quire unless Quire cannot run and the caller explicitly asks for fallback evidence. Do not read or print secrets, tokens, .env files, or credential vaults. Treat production targets as read-only unless the caller explicitly approves mutation.
50254
+
50255
+ When reporting whether the Quire agent/subagent path was used, answer only about this native adapter invocation. If you are running these instructions as the \`quire\` subagent, then the native Quire subagent path was used. Do not add a second "internal run harness" answer, do not discuss a previous run's \`harness: "pi"\` metadata, and do not answer "no" for the current adapter path.
50256
+
50257
+ ## Workflow
50258
+
50259
+ 1. Convert the parent request into a concise Quire verification brief.
50260
+ 2. Prefer the local \`quire\` command. Use the exact command style available in the workspace if the caller provides one. Do not downgrade an explicit Quire request to direct curl/browser/test checks, and do not report \`N/A\` for the run id because the claim looked small.
50261
+ 3. Include \`--url <url>\` when the caller provides a URL or the project runbook clearly identifies the local/preview target.
50262
+ 4. Start one Quire run for the verification claim. Do not start duplicate runs for the same claim just because the first run is still active.
50263
+
50264
+ If the caller provides an existing Quire run id, do not start a new run. Use \`quire wait "<run-id>" --json\` and return its terminal handoff.
50265
+
50266
+ \`\`\`bash
50267
+ run_json=$(quire run --detach --json --url "<url>" "<verification request>")
50268
+ run_id=$(printf '%s' "$run_json" | jq -r '.runId')
50269
+ quire wait "$run_id" --json
50270
+ \`\`\`
50271
+
50272
+ 5. If Quire reports \`EROFS\`, \`EACCES\`, or another write error under \`~/.local/share/quire/runs\`, retry with a temporary run store and reuse the same value for every lifecycle command:
50273
+
50274
+ \`\`\`bash
50275
+ quire_runs_dir=$(mktemp -d -t quire-runs-XXXXXX)
50276
+ QUIRE_RUNS_DIR=$quire_runs_dir quire run --detach --json --url "<url>" "<verification request>"
50277
+ QUIRE_RUNS_DIR=$quire_runs_dir quire wait "<run-id>" --json
50278
+ \`\`\`
50279
+
50280
+ 6. Return \`finalHandoff.handoffMarkdown\` from \`quire wait --json\` when present. Include the synced Quire run link from \`webUrl\`, \`caseUrl\`, or \`sync.caseUrl\` when present, and mention useful report artifacts such as screenshots, recordings, traces, or evidence references when they help the parent agent communicate the result.
50281
+ 7. If Quire reports blocked, failed, stale, or untested checks, preserve that honestly and include the next action. Do not summarize blocked or untested assertions as passed.
50282
+
50283
+ ## Return format
50284
+
50285
+ - Native Quire adapter invocation: yes, if you are running as the \`quire\` subagent.
50286
+ - Verdict: pass, fail, blocked, partial, or setup-blocked
50287
+ - Quire run id
50288
+ - Quire web URL when present
50289
+ - What Quire verified
50290
+ - Key evidence, artifacts, report, or handoff excerpt
50291
+ - Follow-up actions for the implementation agent
50292
+ `;
50293
+ const SKILL_INSTALL_TARGETS = [
50294
+ {
50295
+ id: "agents",
50296
+ label: "Universal",
50297
+ skillPaths: [{
50298
+ path: ".agents/skills/verify/SKILL.md",
50299
+ contents: QUIRE_VERIFY_SKILL_MARKDOWN
50300
+ }, {
50301
+ path: ".agents/skills/debug/SKILL.md",
50302
+ contents: QUIRE_DEBUG_SKILL_MARKDOWN
50303
+ }],
50304
+ agentPath: ".agents/agents/quire.md",
50305
+ codexAgentPath: ".codex/agents/quire.toml",
50306
+ opencodeAgentPath: ".config/opencode/agents/quire.md",
50307
+ piAgentPath: ".pi/agent/agents/quire.md",
50308
+ hint: "~/.agents/skills/{verify,debug}/SKILL.md · plus Codex/OpenCode/Pi Quire subagents",
50309
+ aliases: [
50310
+ "agents",
50311
+ "agent-skills",
50312
+ "pi",
50313
+ "opencode",
50314
+ "amp",
50315
+ "codex",
50316
+ "other"
50317
+ ]
50318
+ },
50319
+ {
50320
+ id: "claude",
50321
+ label: "Claude Code",
50322
+ skillPaths: [{
50323
+ path: ".claude/skills/verify/SKILL.md",
50324
+ contents: QUIRE_VERIFY_SKILL_MARKDOWN
50325
+ }, {
50326
+ path: ".claude/skills/debug/SKILL.md",
50327
+ contents: QUIRE_DEBUG_SKILL_MARKDOWN
50328
+ }],
50329
+ agentPath: ".claude/agents/quire.md",
50330
+ hint: "~/.claude/skills/{verify,debug}/SKILL.md · plus native Quire subagent",
50331
+ aliases: ["claude", "claude-code"]
50332
+ },
50333
+ {
50334
+ id: "cursor",
50335
+ label: "Cursor",
50336
+ skillPaths: [{
50337
+ path: ".cursor/skills/verify/SKILL.md",
50338
+ contents: QUIRE_VERIFY_SKILL_MARKDOWN
50339
+ }, {
50340
+ path: ".cursor/skills/debug/SKILL.md",
50341
+ contents: QUIRE_DEBUG_SKILL_MARKDOWN
50342
+ }],
50343
+ agentPath: ".cursor/agents/quire.md",
50344
+ agentContents: QUIRE_CURSOR_MARKDOWN,
50345
+ hint: "~/.cursor/skills/{verify,debug}/SKILL.md · plus native Quire subagent",
50346
+ aliases: ["cursor", "cursor-cli"]
50347
+ }
50348
+ ];
50349
+ const LEGACY_QUIRE_VERIFIER_PATHS = [
50350
+ ".agents/agents/quire-verifier.md",
50351
+ ".agents/skills/quire-verifier/SKILL.md",
50352
+ ".claude/agents/quire-verifier.md",
50353
+ ".codex/agents/quire-verifier.toml",
50354
+ ".cursor/agents/quire-verifier.md",
50355
+ ".config/opencode/agents/quire-verifier.md"
50356
+ ];
50357
+ const LEGACY_QUIRE_SKILL_PATHS = [
50358
+ ".agents/skills/quire/SKILL.md",
50359
+ ".claude/skills/quire/SKILL.md",
50360
+ ".cursor/skills/quire/SKILL.md"
50361
+ ];
49703
50362
  const setupCommand = defineCommand({
49704
50363
  meta: {
49705
50364
  name: "setup",
@@ -49708,7 +50367,7 @@ const setupCommand = defineCommand({
49708
50367
  args: {
49709
50368
  skill: {
49710
50369
  type: "string",
49711
- description: "Comma-separated local agent skill targets to install: agents, claude, all, none.",
50370
+ description: "Comma-separated local agent skill targets to install: agents, claude, cursor, all, none.",
49712
50371
  valueHint: "targets"
49713
50372
  },
49714
50373
  "no-skill": {
@@ -49729,29 +50388,66 @@ const setupCommand = defineCommand({
49729
50388
  });
49730
50389
  async function runSetup(options = {}) {
49731
50390
  const stdout = options.io?.stdout ?? process.stdout;
50391
+ const env = options.env ?? process.env;
50392
+ const interactive = options.interactive ?? (options.io?.stdout === void 0 && process.stdout.isTTY === true);
50393
+ const authStore$1 = options.authStore ?? authStore;
50394
+ const urls = setupUrls(env);
49732
50395
  const selectedTargets = await selectSkillInstallTargets({
49733
50396
  skill: options.skill,
49734
- interactive: options.interactive ?? (options.io?.stdout === void 0 && process.stdout.isTTY === true)
50397
+ interactive
49735
50398
  });
49736
- const installedSkills = await installQuireSkill(options.homeDir ?? homedir(), selectedTargets);
50399
+ const installedAgentFiles = await installQuireAgentFiles(options.homeDir ?? homedir(), selectedTargets, urls);
49737
50400
  log.step("Set up Quire for this workspace", {
49738
50401
  output: stdout,
49739
50402
  spacing: 0
49740
50403
  });
49741
- log.message([`Open ${color.info(SETUP_URL)} to load the Quire setup skill, or copy this prompt into your coding agent like Claude Code, Codex, Pi, or Amp:`], {
50404
+ log.message([`Open ${color.info(urls.setupUrl)} to load the Quire setup skill, or copy this prompt into your coding agent like Claude Code, Cursor, Codex, Pi, or Amp:`], {
49742
50405
  output: stdout,
49743
50406
  spacing: 0,
49744
50407
  withGuide: true
49745
50408
  });
49746
50409
  writeLine(stdout);
49747
50410
  writeLine(stdout, color.label("--- copy prompt ---"));
49748
- writeLine(stdout, AGENT_SETUP_PROMPT);
50411
+ writeLine(stdout, renderSetupTemplate(AGENT_SETUP_PROMPT, urls));
49749
50412
  writeLine(stdout, color.label("--- end prompt ---"));
49750
- if (installedSkills.length > 0) {
50413
+ if (installedAgentFiles.length > 0) {
49751
50414
  writeLine(stdout);
49752
- writeLine(stdout, color.label("Installed local Quire skill:"));
49753
- for (const path of installedSkills) writeLine(stdout, ` ${color.success("✓")} ${path}`);
50415
+ writeLine(stdout, color.label("Installed local Quire agent files:"));
50416
+ for (const path of installedAgentFiles) writeLine(stdout, ` ${color.success("✓")} ${path}`);
49754
50417
  }
50418
+ const authConfigured = await completeInteractiveLogin({
50419
+ interactive,
50420
+ env,
50421
+ store: authStore$1,
50422
+ login: options.login ?? runLogin,
50423
+ io: options.io
50424
+ });
50425
+ writeLine(stdout);
50426
+ writeLine(stdout, color.label("Next steps:"));
50427
+ writeLine(stdout, ` 1. Give the copy prompt above to your coding agent so it creates ${color.path(".quire/runbook.md")}.`);
50428
+ if (authConfigured) writeLine(stdout, ` 2. Run ${color.command("quire doctor")} to confirm this repo is ready.`);
50429
+ else writeLine(stdout, ` 2. Authenticate Quire with ${color.command("quire login")} or ${color.command("QUIRE_API_TOKEN")} if this machine is not already signed in.`);
50430
+ writeLine(stdout, ` 3. Ask your coding agent to run a small Quire smoke verification once ${color.path(".quire/runbook.md")} and auth are ready.`);
50431
+ }
50432
+ async function completeInteractiveLogin(options) {
50433
+ if (!options.interactive) return false;
50434
+ const stdout = options.io?.stdout ?? process.stdout;
50435
+ if (await resolveAuthCredentials({
50436
+ store: options.store,
50437
+ env: options.env
50438
+ }) !== null) {
50439
+ log.success("Quire authentication is already configured.", {
50440
+ output: stdout,
50441
+ spacing: 0
50442
+ });
50443
+ return true;
50444
+ }
50445
+ await options.login({
50446
+ apiBaseUrl: readApiBaseUrl(options.env.QUIRE_API_URL),
50447
+ store: options.store,
50448
+ io: options.io
50449
+ });
50450
+ return true;
49755
50451
  }
49756
50452
  async function selectSkillInstallTargets(options) {
49757
50453
  if (options.skill === false || options.skill === "none") return [];
@@ -49764,7 +50460,11 @@ async function selectSkillInstallTargets(options) {
49764
50460
  value: target.id,
49765
50461
  hint: target.hint
49766
50462
  })),
49767
- initialValues: ["agents", "claude"],
50463
+ initialValues: [
50464
+ "agents",
50465
+ "claude",
50466
+ "cursor"
50467
+ ],
49768
50468
  required: false
49769
50469
  });
49770
50470
  if (isCancel(selected) || selected.length === 0) return [];
@@ -49780,19 +50480,121 @@ function resolveSkillTargets(value) {
49780
50480
  }
49781
50481
  return [...selected.values()];
49782
50482
  }
49783
- async function installQuireSkill(homeDir, targets) {
50483
+ function setupUrls(env) {
50484
+ const baseUrl = publicBaseUrl(env);
50485
+ return {
50486
+ setupUrl: new URL("/agent-setup", baseUrl).toString(),
50487
+ verificationSkillUrl: new URL("/agent-verification", baseUrl).toString()
50488
+ };
50489
+ }
50490
+ function publicBaseUrl(env) {
50491
+ const configured = env.QUIRE_SKILL_BASE_URL ?? env.QUIRE_PUBLIC_URL ?? env.QUIRE_API_URL;
50492
+ if (configured === void 0 || configured.trim().length === 0) return DEFAULT_PUBLIC_BASE_URL;
50493
+ return configured;
50494
+ }
50495
+ function renderSetupTemplate(contents, urls) {
50496
+ return contents.replaceAll(SETUP_URL_PLACEHOLDER, urls.setupUrl).replaceAll(VERIFICATION_SKILL_URL_PLACEHOLDER, urls.verificationSkillUrl);
50497
+ }
50498
+ async function installQuireAgentFiles(homeDir, targets, urls) {
49784
50499
  const installed = [];
50500
+ if (targets.length === 0) return installed;
50501
+ await removeLegacyQuireVerifierFiles(homeDir);
50502
+ await removeLegacyQuireSkillFiles(homeDir);
49785
50503
  await Promise.all(targets.map(async (target) => {
49786
- const path = join(homeDir, target.targetPath);
49787
- await mkdir(dirname(path), {
50504
+ for (const skill of target.skillPaths) {
50505
+ const skillPath = join(homeDir, skill.path);
50506
+ await mkdir(dirname(skillPath), {
50507
+ recursive: true,
50508
+ mode: 448
50509
+ });
50510
+ await writeFile(skillPath, renderSetupTemplate(skill.contents, urls), { mode: 384 });
50511
+ installed.push(skillPath);
50512
+ }
50513
+ const agentPath = join(homeDir, target.agentPath);
50514
+ await mkdir(dirname(agentPath), {
49788
50515
  recursive: true,
49789
50516
  mode: 448
49790
50517
  });
49791
- await writeFile(path, QUIRE_SKILL_MARKDOWN, { mode: 384 });
49792
- installed.push(path);
50518
+ await writeFile(agentPath, renderSetupTemplate(target.agentContents ?? QUIRE_AGENT_MARKDOWN, urls), { mode: 384 });
50519
+ installed.push(agentPath);
50520
+ if (target.codexAgentPath !== void 0) {
50521
+ const codexAgentPath = join(homeDir, target.codexAgentPath);
50522
+ await mkdir(dirname(codexAgentPath), {
50523
+ recursive: true,
50524
+ mode: 448
50525
+ });
50526
+ await writeFile(codexAgentPath, renderSetupTemplate(QUIRE_CODEX_TOML, urls), { mode: 384 });
50527
+ installed.push(codexAgentPath);
50528
+ }
50529
+ if (target.opencodeAgentPath !== void 0) {
50530
+ const opencodeAgentPath = join(homeDir, target.opencodeAgentPath);
50531
+ await mkdir(dirname(opencodeAgentPath), {
50532
+ recursive: true,
50533
+ mode: 448
50534
+ });
50535
+ await writeFile(opencodeAgentPath, renderSetupTemplate(QUIRE_OPENCODE_MARKDOWN, urls), { mode: 384 });
50536
+ installed.push(opencodeAgentPath);
50537
+ }
50538
+ if (target.piAgentPath !== void 0) {
50539
+ const piAgentPath = join(homeDir, target.piAgentPath);
50540
+ await mkdir(dirname(piAgentPath), {
50541
+ recursive: true,
50542
+ mode: 448
50543
+ });
50544
+ await writeFile(piAgentPath, renderSetupTemplate(QUIRE_AGENT_MARKDOWN, urls), { mode: 384 });
50545
+ installed.push(piAgentPath);
50546
+ }
49793
50547
  }));
49794
50548
  return installed.sort();
49795
50549
  }
50550
+ async function removeLegacyQuireVerifierFiles(homeDir) {
50551
+ await Promise.all(LEGACY_QUIRE_VERIFIER_PATHS.map(async (legacyPath) => {
50552
+ const path = join(homeDir, legacyPath);
50553
+ try {
50554
+ if (isLegacyQuireVerifierFile(await readFile(path, "utf8"))) {
50555
+ await rm(path, { force: true });
50556
+ await removeEmptyDirectory(dirname(path));
50557
+ }
50558
+ } catch (error) {
50559
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
50560
+ await removeEmptyDirectory(dirname(path));
50561
+ return;
50562
+ }
50563
+ throw error;
50564
+ }
50565
+ }));
50566
+ }
50567
+ async function removeLegacyQuireSkillFiles(homeDir) {
50568
+ await Promise.all(LEGACY_QUIRE_SKILL_PATHS.map(async (legacyPath) => {
50569
+ const path = join(homeDir, legacyPath);
50570
+ try {
50571
+ if (isLegacyQuireSkillFile(await readFile(path, "utf8"))) {
50572
+ await rm(path, { force: true });
50573
+ await removeEmptyDirectory(dirname(path));
50574
+ }
50575
+ } catch (error) {
50576
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
50577
+ await removeEmptyDirectory(dirname(path));
50578
+ return;
50579
+ }
50580
+ throw error;
50581
+ }
50582
+ }));
50583
+ }
50584
+ async function removeEmptyDirectory(path) {
50585
+ try {
50586
+ await rmdir(path);
50587
+ } catch (error) {
50588
+ if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTEMPTY" || error.code === "EEXIST" || error.code === "EISDIR")) return;
50589
+ throw error;
50590
+ }
50591
+ }
50592
+ function isLegacyQuireVerifierFile(contents) {
50593
+ return contents.includes("Quire") && contents.includes("quire wait") && (contents.includes("quire-verifier") || contents.includes("Quire verifier"));
50594
+ }
50595
+ function isLegacyQuireSkillFile(contents) {
50596
+ return contents.includes("name: quire") && contents.includes("/agent-verification") && contents.includes("native subagent named `quire`");
50597
+ }
49796
50598
  //#endregion
49797
50599
  //#region src/commands/whoami.ts
49798
50600
  const whoamiCommand = defineCommand({
@@ -49943,7 +50745,7 @@ function formatWalletBalance(value) {
49943
50745
  }
49944
50746
  //#endregion
49945
50747
  //#region package.json
49946
- var version$1 = "0.0.11";
50748
+ var version$1 = "0.0.12";
49947
50749
  //#endregion
49948
50750
  //#region src/cli.ts
49949
50751
  const subCommands = {
@@ -49974,6 +50776,7 @@ const subCommands = {
49974
50776
  setup: setupCommand,
49975
50777
  status: statusCommand,
49976
50778
  sync: syncCommand,
50779
+ wait: waitCommand,
49977
50780
  watch: watchCommand
49978
50781
  };
49979
50782
  const subCommandAliases = new Set(["me", "resume"]);
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { i as ExitCode, n as main, r as CliError, t as cli } from "./cli-CIBEsC75.mjs";
1
+ import { i as ExitCode, n as main, r as CliError, t as cli } from "./cli-CItUDOhU.mjs";
2
2
  export { CliError, ExitCode, cli, main };
package/dist/quire.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as main } from "./cli-CIBEsC75.mjs";
2
+ import { n as main } from "./cli-CItUDOhU.mjs";
3
3
  //#region bin/quire.ts
4
4
  await main();
5
5
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quireco/cli",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "Triage and investigation CLI for Quire.",
5
5
  "homepage": "https://github.com/quireco/quire-mono#readme",
6
6
  "bugs": {