@drisp/cli 0.7.7 → 0.7.9

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.
package/dist/cli.js CHANGED
@@ -142,7 +142,7 @@ import {
142
142
  visualPositionToOffset,
143
143
  wrapText,
144
144
  writeIdentityOnce
145
- } from "./chunk-D246FR4I.js";
145
+ } from "./chunk-TWOJJ2JD.js";
146
146
  import {
147
147
  generateId as generateId2
148
148
  } from "./chunk-PQIB66SG.js";
@@ -161,6 +161,7 @@ import {
161
161
  DESCRIPTION_TEMPLATE,
162
162
  GATES,
163
163
  LAYOUT_MARKERS,
164
+ MERGE_SIGN_OFF,
164
165
  NO_GATE_TEXT_TEMPLATE,
165
166
  ORCHESTRATOR_LIMITS,
166
167
  QA_SECTIONS,
@@ -200,7 +201,7 @@ import {
200
201
  workflowRegistryDir,
201
202
  writeGlobalConfig,
202
203
  writeProjectConfig
203
- } from "./chunk-HZ6GRLOM.js";
204
+ } from "./chunk-FYXEDUQ6.js";
204
205
  import {
205
206
  __toESM
206
207
  } from "./chunk-G3PMV62Z.js";
@@ -208,10 +209,10 @@ import {
208
209
  // src/app/entry/cli.tsx
209
210
  import { render } from "ink";
210
211
  import meow from "meow";
211
- import fs11 from "fs";
212
+ import fs12 from "fs";
212
213
  import { createRequire } from "module";
213
214
  import os5 from "os";
214
- import path11 from "path";
215
+ import path12 from "path";
215
216
  import { fileURLToPath } from "url";
216
217
 
217
218
  // src/app/shell/sessionUiState.ts
@@ -5330,14 +5331,14 @@ function extractWebSearch(_input, response) {
5330
5331
  return { type: "text", content: extractTextContent(response), maxLines: 10 };
5331
5332
  }
5332
5333
  function extractNotebookEdit(input, _response) {
5333
- const path12 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5334
+ const path13 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5334
5335
  const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
5335
5336
  const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
5336
- if (!source) return { type: "text", content: `${mode} cell in ${path12}` };
5337
+ if (!source) return { type: "text", content: `${mode} cell in ${path13}` };
5337
5338
  return {
5338
5339
  type: "code",
5339
5340
  content: source,
5340
- language: detectLanguage(path12),
5341
+ language: detectLanguage(path13),
5341
5342
  maxLines: 10
5342
5343
  };
5343
5344
  }
@@ -5863,10 +5864,10 @@ function extractToolSubject(toolName, toolInput) {
5863
5864
  const parts = [];
5864
5865
  const pattern = str(input, "pattern");
5865
5866
  const glob = str(input, "glob");
5866
- const path12 = str(input, "path");
5867
+ const path13 = str(input, "path");
5867
5868
  if (pattern) parts.push(`"${pattern}"`);
5868
5869
  if (glob) parts.push(glob);
5869
- if (path12) parts.push(`in ${path12}`);
5870
+ if (path13) parts.push(`in ${path13}`);
5870
5871
  return parts.length > 0 ? parts.join(" ") : void 0;
5871
5872
  }
5872
5873
  case "Skill":
@@ -13097,7 +13098,7 @@ var thisMachine = (installationId) => ({
13097
13098
  os: `${platform()} ${release()}`.slice(0, 64),
13098
13099
  softwareVersion: runnerSoftwareVersion
13099
13100
  });
13100
- var post = (origin, path12, body) => fetch(new URL(path12, origin), {
13101
+ var post = (origin, path13, body) => fetch(new URL(path13, origin), {
13101
13102
  method: "POST",
13102
13103
  headers: { "Content-Type": "application/json" },
13103
13104
  body: JSON.stringify(body)
@@ -13116,8 +13117,8 @@ async function alreadyConnected(existing, { forTeam, ownIdentity }) {
13116
13117
  const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
13117
13118
  const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
13118
13119
  if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
13119
- const path12 = (own ?? forTeam(existing.orgId)[0])?.path;
13120
- return { status: "identity_unusable", ...runner, problem, ...path12 ? { path: path12 } : {} };
13120
+ const path13 = (own ?? forTeam(existing.orgId)[0])?.path;
13121
+ return { status: "identity_unusable", ...runner, problem, ...path13 ? { path: path13 } : {} };
13121
13122
  }
13122
13123
  async function connectWithCode({
13123
13124
  code,
@@ -13189,20 +13190,20 @@ async function saveIdentity(origin, credential, { single, forTeam }, directory)
13189
13190
  name: credential.name
13190
13191
  });
13191
13192
  const replaced = [];
13192
- for (const { path: path13 } of forTeam(credential.orgId)) {
13193
- const aside = `${path13}.replaced-${Date.now()}`;
13194
- await rename(path13, aside);
13193
+ for (const { path: path14 } of forTeam(credential.orgId)) {
13194
+ const aside = `${path14}.replaced-${Date.now()}`;
13195
+ await rename(path14, aside);
13195
13196
  replaced.push(aside);
13196
13197
  }
13197
- const path12 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13198
+ const path13 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13198
13199
  try {
13199
- await writeIdentityOnce(path12, JSON.stringify(config, null, 2) + "\n");
13200
+ await writeIdentityOnce(path13, JSON.stringify(config, null, 2) + "\n");
13200
13201
  } catch (error) {
13201
13202
  if (error.code === "EEXIST")
13202
- throw new Error(`Another Connect wrote a Runner identity for this Team at ${path12} meanwhile; it was left unchanged.`);
13203
+ throw new Error(`Another Connect wrote a Runner identity for this Team at ${path13} meanwhile; it was left unchanged.`);
13203
13204
  throw error;
13204
13205
  }
13205
- return { status: "connected", config, path: path12, replaced };
13206
+ return { status: "connected", config, path: path13, replaced };
13206
13207
  }
13207
13208
  function pathSegment(value) {
13208
13209
  const segment = encodeURIComponent(value);
@@ -13242,7 +13243,7 @@ async function refuseExisting(target) {
13242
13243
  throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
13243
13244
  }
13244
13245
  async function disconnectRunner({
13245
- identity: { path: path12, config },
13246
+ identity: { path: path13, config },
13246
13247
  serviceManager,
13247
13248
  othersRemain
13248
13249
  }) {
@@ -13275,7 +13276,7 @@ async function disconnectRunner({
13275
13276
  await restore();
13276
13277
  throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
13277
13278
  }
13278
- await forgetRunner(path12);
13279
+ await forgetRunner(path13);
13279
13280
  if (othersRemain) await restore();
13280
13281
  else await serviceManager?.remove();
13281
13282
  return result;
@@ -13428,18 +13429,18 @@ async function runRunnerCommandV2(input, options = {}) {
13428
13429
  `This machine is already connected to this Team as ${outcome.name}, but ${why}. ` + (code ? `The code was not used: connect a new Runner in its place with ${retry}. ` : `Connect a new Runner in its place with a Connect code from Settings \u2192 Runners: ${retry}. `) + `The old ${outcome.name} stays listed in Settings \u2192 Runners, offline.`
13429
13430
  );
13430
13431
  }
13431
- const { config, path: path12, replaced } = outcome;
13432
+ const { config, path: path13, replaced } = outcome;
13432
13433
  for (const aside of replaced)
13433
13434
  process.stdout.write(
13434
13435
  `The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
13435
13436
  `
13436
13437
  );
13437
13438
  process.stdout.write(
13438
- `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path12}.
13439
+ `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path13}.
13439
13440
  `
13440
13441
  );
13441
13442
  if (services) {
13442
- const service = await ensureService(services, config, path12);
13443
+ const service = await ensureService(services, config, path13);
13443
13444
  process.stdout.write(`${serviceStarted(service)}
13444
13445
  `);
13445
13446
  return 0;
@@ -13448,7 +13449,7 @@ async function runRunnerCommandV2(input, options = {}) {
13448
13449
  process.stdout.write(
13449
13450
  "No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
13450
13451
  );
13451
- await runConfiguredRunner(path12, options.signal);
13452
+ await runConfiguredRunner(path13, options.signal);
13452
13453
  return 0;
13453
13454
  }
13454
13455
  case "disconnect": {
@@ -13519,9 +13520,9 @@ async function runRunnerCommandV2(input, options = {}) {
13519
13520
  configured: true,
13520
13521
  // One entry per Team this machine serves; credentials are never printed.
13521
13522
  teams: identities.map(
13522
- ({ path: path12, config: { token: _token, ...config } }) => ({
13523
+ ({ path: path13, config: { token: _token, ...config } }) => ({
13523
13524
  ...config,
13524
- path: path12
13525
+ path: path13
13525
13526
  })
13526
13527
  ),
13527
13528
  service: service ? {
@@ -13677,7 +13678,9 @@ function computeTemplateVersion() {
13677
13678
  JSON.stringify(QA_SECTIONS),
13678
13679
  JSON.stringify(GATES),
13679
13680
  JSON.stringify(RENDERER_RULES),
13680
- JSON.stringify(LAYOUT_MARKERS)
13681
+ JSON.stringify(LAYOUT_MARKERS),
13682
+ JSON.stringify(MERGE_SIGN_OFF),
13683
+ BUILD_STRINGS.mergeSignOff
13681
13684
  ];
13682
13685
  const hash = createHash("sha256");
13683
13686
  for (const part of parts) hash.update(`${part.length}:${part}`);
@@ -13782,7 +13785,8 @@ function runBuildPlan(deps) {
13782
13785
  fills: result.fills.map((fill) => ({
13783
13786
  ...fill,
13784
13787
  path: fillPath(fill.identifier)
13785
- }))
13788
+ })),
13789
+ ...result.reason ? { reason: result.reason } : {}
13786
13790
  };
13787
13791
  const host = deps.hostCapabilities();
13788
13792
  for (const workflow of result.workflows) {
@@ -13866,9 +13870,9 @@ var DrispClient = class _DrispClient {
13866
13870
  if (!match) throw new Error("invalid_hub_url");
13867
13871
  return new _DrispClient(url.origin, decodeURIComponent(match[1]), async () => token);
13868
13872
  }
13869
- async request(path12, body) {
13873
+ async request(path13, body) {
13870
13874
  const response = await fetch(
13871
- `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path12}`,
13875
+ `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path13}`,
13872
13876
  {
13873
13877
  method: body === void 0 ? "GET" : "POST",
13874
13878
  headers: {
@@ -14776,6 +14780,15 @@ var receiptSchema = external_exports.strictObject({
14776
14780
  evidence: external_exports.string().min(1).max(16e3),
14777
14781
  kind: external_exports.literal("agent-attestation")
14778
14782
  });
14783
+ var COMMIT_SHA = /^[0-9a-f]{40}$/;
14784
+ var gateEvidenceSchema = external_exports.strictObject({
14785
+ receipts: external_exports.array(receiptSchema).max(100),
14786
+ /**
14787
+ * The Sign-off on the Merge step (`merge: you`): the head commit the child attests the member signed off on in
14788
+ * its Thread. Like the receipts, an agent attestation.
14789
+ */
14790
+ signOff: external_exports.strictObject({ sha: external_exports.string().regex(COMMIT_SHA) }).optional()
14791
+ });
14779
14792
  var QA_KINDS = fillSchema.shape.qa.options;
14780
14793
  function runBuildStep(input) {
14781
14794
  const directory = requireEnv(input.env, "DRISP_RUN_ARTIFACTS");
@@ -14805,19 +14818,57 @@ function runBuildStep(input) {
14805
14818
  throw new BuildUsageError(
14806
14819
  `Step must be a whole number between 1 and ${titles.length}, not "${input.n ?? ""}".`
14807
14820
  );
14821
+ if (input.merge !== void 0 && input.merge !== "agent" && input.merge !== "you")
14822
+ throw new BuildUsageError("--merge must be agent or you.");
14823
+ const mergeStep = titles.indexOf("Merge") + 1;
14824
+ const signsOff = input.merge === "you" && index === mergeStep;
14825
+ if (input.signOff !== void 0) {
14826
+ if (input.merge !== "you")
14827
+ throw new BuildUsageError(
14828
+ "--sign-off needs --merge you: agents merge this pull request."
14829
+ );
14830
+ if (mergeStep === 0)
14831
+ throw new BuildUsageError(
14832
+ "--sign-off is for the Merge step, and this workflow has none."
14833
+ );
14834
+ if (!signsOff)
14835
+ throw new BuildUsageError(
14836
+ `--sign-off is for the Merge step (${mergeStep}), not step ${index}.`
14837
+ );
14838
+ if (!COMMIT_SHA.test(input.signOff))
14839
+ throw new BuildUsageError(
14840
+ "--sign-off needs the full 40-character commit SHA the member signed off on."
14841
+ );
14842
+ }
14808
14843
  if (!fs8.statSync(directory, { throwIfNoEntry: false })?.isDirectory())
14809
14844
  throw new BuildUsageError(
14810
14845
  `DRISP_RUN_ARTIFACTS (${directory}) is not a directory.`
14811
14846
  );
14812
14847
  const receiptsPath = path8.join(directory, "gate_evidence.json");
14813
14848
  let receipts = [];
14849
+ let signOff;
14814
14850
  if (fs8.existsSync(receiptsPath)) {
14815
14851
  try {
14816
- receipts = external_exports.strictObject({ receipts: external_exports.array(receiptSchema).max(100) }).parse(JSON.parse(fs8.readFileSync(receiptsPath, "utf8"))).receipts;
14852
+ ({ receipts, signOff } = gateEvidenceSchema.parse(
14853
+ JSON.parse(fs8.readFileSync(receiptsPath, "utf8"))
14854
+ ));
14817
14855
  } catch {
14818
14856
  throw new BuildUsageError("Gate evidence is invalid.");
14819
14857
  }
14820
14858
  }
14859
+ const save = () => fs8.writeFileSync(
14860
+ receiptsPath,
14861
+ JSON.stringify({ receipts, ...signOff ? { signOff } : {} }) + "\n"
14862
+ );
14863
+ const head = signsOff && (input.signOff || input.verify) ? headCommit(input.cwd ?? process.cwd()) : "";
14864
+ if (input.signOff !== void 0) {
14865
+ if (input.signOff !== head)
14866
+ throw new BuildUsageError(
14867
+ `The Sign-off names ${input.signOff}, but HEAD is ${head}. ${askForSignOff(head)}`
14868
+ );
14869
+ signOff = { sha: head };
14870
+ save();
14871
+ }
14821
14872
  const revision = receipts.length || input.complete || input.verify ? workRevision(input.cwd ?? process.cwd()) : "";
14822
14873
  const fresh = (receipt) => ["Explore", "Pull request", "Evidence", "Merge"].includes(receipt.title) || receipt.revision === revision;
14823
14874
  if (input.verify || input.complete && titles[index - 1] === "Evidence") {
@@ -14827,6 +14878,20 @@ function runBuildStep(input) {
14827
14878
  `Missing or stale evidence: ${missing.join(", ")}. Repeat those steps on the current revision.`
14828
14879
  );
14829
14880
  }
14881
+ if (signsOff && input.verify) {
14882
+ if (!signOff)
14883
+ throw new BuildUsageError(
14884
+ `No Sign-off on HEAD (${head}) yet. ${askForSignOff(head)}`
14885
+ );
14886
+ if (signOff.sha !== head)
14887
+ throw new BuildUsageError(
14888
+ `The Sign-off names ${signOff.sha}, but HEAD is now ${head}. ${askForSignOff(head)}`
14889
+ );
14890
+ }
14891
+ if (signsOff && input.complete && !signOff)
14892
+ throw new BuildUsageError(
14893
+ "No Sign-off was recorded: verify and merge only after the member signs off on HEAD."
14894
+ );
14830
14895
  if (input.complete) {
14831
14896
  if (!input.evidence)
14832
14897
  throw new BuildUsageError(
@@ -14850,7 +14915,7 @@ function runBuildStep(input) {
14850
14915
  evidence,
14851
14916
  kind: "agent-attestation"
14852
14917
  });
14853
- fs8.writeFileSync(receiptsPath, JSON.stringify({ receipts }) + "\n");
14918
+ save();
14854
14919
  }
14855
14920
  const artifact = {
14856
14921
  index,
@@ -14867,12 +14932,23 @@ function runBuildStep(input) {
14867
14932
  );
14868
14933
  return artifact;
14869
14934
  }
14935
+ function askForSignOff(sha) {
14936
+ return `Ask the member: end the Turn with <!-- NEEDS_HUMAN: ${BUILD_STRINGS.mergeSignOff.replace("{sha}", sha)} -->`;
14937
+ }
14938
+ var gitIn = (cwd) => (...args) => execFileSync("git", args, {
14939
+ cwd,
14940
+ encoding: "utf8",
14941
+ stdio: ["ignore", "pipe", "pipe"]
14942
+ });
14943
+ function headCommit(cwd) {
14944
+ try {
14945
+ return gitIn(cwd)("rev-parse", "HEAD").trim();
14946
+ } catch {
14947
+ throw new BuildUsageError("A Sign-off must be recorded in a git worktree.");
14948
+ }
14949
+ }
14870
14950
  function workRevision(cwd) {
14871
- const git2 = (...args) => execFileSync("git", args, {
14872
- cwd,
14873
- encoding: "utf8",
14874
- stdio: ["ignore", "pipe", "pipe"]
14875
- });
14951
+ const git2 = gitIn(cwd);
14876
14952
  try {
14877
14953
  const hash = createHash2("sha256").update(git2("rev-parse", "HEAD")).update(git2("diff", "HEAD", "--", ".", ":!.athena/evidence"));
14878
14954
  for (const name of git2("ls-files", "--others", "--exclude-standard", "-z").split("\0").filter(Boolean).sort()) {
@@ -14939,22 +15015,87 @@ function runBuildMerged(input) {
14939
15015
  return parsed.data;
14940
15016
  }
14941
15017
 
15018
+ // src/core/build/check.ts
15019
+ import { spawnSync } from "child_process";
15020
+ import fs10 from "fs";
15021
+ import path10 from "path";
15022
+ var checkRecordSchema = external_exports.strictObject({
15023
+ name: external_exports.string().trim().min(1).max(64),
15024
+ outcome: external_exports.enum(["passed", "failed"])
15025
+ });
15026
+ var NAME_PATTERN = /^[^\p{Cc}]+$/u;
15027
+ var MAX_RECORDS = 50;
15028
+ function spawnCheck(command, cwd, onOutput) {
15029
+ const [program, ...args] = command;
15030
+ const result = spawnSync(program, args, {
15031
+ cwd,
15032
+ stdio: onOutput ? ["inherit", "pipe", "inherit"] : "inherit",
15033
+ maxBuffer: 64 * 1024 * 1024
15034
+ });
15035
+ if (onOutput) onOutput(result.stdout);
15036
+ if (result.error) return 127;
15037
+ return result.status ?? 1;
15038
+ }
15039
+ function readRecords(file) {
15040
+ try {
15041
+ const parsed = external_exports.array(checkRecordSchema).safeParse(JSON.parse(fs10.readFileSync(file, "utf8")));
15042
+ return parsed.success ? parsed.data : [];
15043
+ } catch {
15044
+ return [];
15045
+ }
15046
+ }
15047
+ function runBuildCheck(input) {
15048
+ const directory = requireEnv(input.env, "DRISP_RUN_ARTIFACTS");
15049
+ const name = (input.name ?? "").trim();
15050
+ if (!name || name.length > 64 || !NAME_PATTERN.test(name))
15051
+ throw new BuildUsageError(
15052
+ "A check needs a name of 1 to 64 characters on one line."
15053
+ );
15054
+ if (input.command.length === 0 || input.command.some((token) => !token))
15055
+ throw new BuildUsageError("A check needs a command after `--`.");
15056
+ if (!fs10.statSync(directory, { throwIfNoEntry: false })?.isDirectory())
15057
+ throw new BuildUsageError(
15058
+ `DRISP_RUN_ARTIFACTS (${directory}) is not a directory.`
15059
+ );
15060
+ const code = input.run(input.command, input.cwd);
15061
+ const file = path10.join(directory, "checks.json");
15062
+ const record = { name, outcome: code === 0 ? "passed" : "failed" };
15063
+ const records = readRecords(file);
15064
+ const at = records.findIndex((existing) => existing.name === name);
15065
+ if (at >= 0) records[at] = record;
15066
+ else records.push(record);
15067
+ const temporary = `${file}.${process.pid}.tmp`;
15068
+ fs10.writeFileSync(
15069
+ temporary,
15070
+ `${JSON.stringify(records.slice(-MAX_RECORDS))}
15071
+ `
15072
+ );
15073
+ fs10.renameSync(temporary, file);
15074
+ return code;
15075
+ }
15076
+
14942
15077
  // src/app/entry/buildCommand.ts
14943
15078
  var USAGE3 = `Usage: drisp build plan --input <path>
14944
15079
  drisp build tick --input <path> [--rebase-failed <IDENTIFIER>]...
14945
15080
  drisp build checks
14946
15081
  drisp build step <n> [--qa none|web|macos|both] [--profile implementation|documentation|investigation]
14947
- [--test-first true|false] [--review true|false] [--complete --evidence <file>] [--verify]
15082
+ [--test-first true|false] [--review true|false] [--merge agent|you] [--sign-off <sha>]
15083
+ [--complete --evidence <file>] [--verify]
14948
15084
  drisp build merged [<pr url or number>]
15085
+ drisp build check <name> -- <command>
14949
15086
  plan and tick run inside an Orchestrator Run (DRISP_RUN_ID, DRISP_RUNNER_ID and DRISP_RUN_ARTIFACTS set; tick also
14950
15087
  DRISP_RUN_TOKEN and DRISP_HUB_URL). DRISP_BUILD_PLAN_INPUT=<path> stands in for plan's --input, with
14951
15088
  fills/<IDENTIFIER>.json beside it. checks runs in a child's worktree: the merge gate's verdict on HEAD's GitHub runs.
14952
15089
  step runs in any Run with DRISP_RUN_ARTIFACTS set: it writes the step artifact {index, title, total} for gate <n> of the
14953
15090
  selected build-child profile (QA is a gate unless --qa none) and refuses an index outside the list.
14954
15091
  --complete --evidence <file> records an agent attestation bound to the source revision; --verify requires fresh
14955
- receipts for all preceding steps. Keep evidence files under .athena/evidence/.
15092
+ receipts for all preceding steps. Keep evidence files under .athena/evidence/. With --merge you, the Merge step's
15093
+ --verify also needs the member's Sign-off on HEAD (--complete one at all), which --sign-off <sha> records for that full
15094
+ SHA only.
14956
15095
  merged runs in a child's worktree after its pull request merged: it writes the pull_request artifact
14957
- {url, number, sha, mergedAt} and refuses a pull request that is not merged.`;
15096
+ {url, number, sha, mergedAt} and refuses a pull request that is not merged.
15097
+ check runs <command>, passes its output through, exits with its code and records {name, outcome} in the checks run artifact
15098
+ (DRISP_RUN_ARTIFACTS set); it refuses a missing name or command before running anything.`;
14958
15099
  function githubPullRequests(folder, branch) {
14959
15100
  const output = execFileSync2(
14960
15101
  "gh",
@@ -15072,6 +15213,16 @@ async function runBuildCommand(input) {
15072
15213
  case "checks":
15073
15214
  output = runBuildChecks(input.cwd, input.checks ?? defaultChecks);
15074
15215
  break;
15216
+ case "check": {
15217
+ const code = runBuildCheck({
15218
+ env: env2,
15219
+ cwd: input.cwd,
15220
+ name: input.positional?.[0],
15221
+ command: input.command ?? [],
15222
+ run: input.checkRunner ?? spawnCheck
15223
+ });
15224
+ return code;
15225
+ }
15075
15226
  case "merged":
15076
15227
  output = runBuildMerged({
15077
15228
  env: env2,
@@ -15088,6 +15239,8 @@ async function runBuildCommand(input) {
15088
15239
  profile: input.flags.profile,
15089
15240
  testFirst: input.flags.testFirst,
15090
15241
  review: input.flags.review,
15242
+ merge: input.flags.merge,
15243
+ signOff: input.flags.signOff,
15091
15244
  complete: input.flags.complete,
15092
15245
  verify: input.flags.verify,
15093
15246
  evidence: input.flags.evidence,
@@ -15116,9 +15269,9 @@ import process7 from "process";
15116
15269
 
15117
15270
  // src/harnesses/claude/system/doctorEnvironment.ts
15118
15271
  import { execFileSync as execFileSync3 } from "child_process";
15119
- import * as fs10 from "fs";
15272
+ import * as fs11 from "fs";
15120
15273
  import * as os4 from "os";
15121
- import * as path10 from "path";
15274
+ import * as path11 from "path";
15122
15275
  var MANAGED_POLICY_KEYS = [
15123
15276
  "forceLoginMethod",
15124
15277
  "forceLoginOrgUUID",
@@ -15222,8 +15375,8 @@ function collectEnvironment(options = {}) {
15222
15375
  const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
15223
15376
  const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
15224
15377
  const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
15225
- const statFn = options.statFn ?? ((p) => fs10.statSync(p));
15226
- const readFileFn = options.readFileFn ?? ((p) => fs10.readFileSync(p, "utf8"));
15378
+ const statFn = options.statFn ?? ((p) => fs11.statSync(p));
15379
+ const readFileFn = options.readFileFn ?? ((p) => fs11.readFileSync(p, "utf8"));
15227
15380
  const settingsPaths = resolveClaudeSettingsSurfacePaths(
15228
15381
  homeDir,
15229
15382
  cwd,
@@ -15268,7 +15421,7 @@ function collectEnvironment(options = {}) {
15268
15421
  });
15269
15422
  let globalConfigPresent = false;
15270
15423
  try {
15271
- statFn(path10.join(homeDir, ".claude.json"));
15424
+ statFn(path11.join(homeDir, ".claude.json"));
15272
15425
  globalConfigPresent = true;
15273
15426
  } catch {
15274
15427
  globalConfigPresent = false;
@@ -15748,13 +15901,13 @@ function runRunsCommand(input) {
15748
15901
  // src/app/entry/cli.tsx
15749
15902
  import { jsx as jsx24 } from "react/jsx-runtime";
15750
15903
  function resolvePackageJsonPath(entryUrl) {
15751
- let currentDir = path11.dirname(fileURLToPath(entryUrl));
15904
+ let currentDir = path12.dirname(fileURLToPath(entryUrl));
15752
15905
  for (; ; ) {
15753
- const candidatePath = path11.join(currentDir, "package.json");
15754
- if (fs11.existsSync(candidatePath)) {
15906
+ const candidatePath = path12.join(currentDir, "package.json");
15907
+ if (fs12.existsSync(candidatePath)) {
15755
15908
  return candidatePath;
15756
15909
  }
15757
- const parentDir = path11.dirname(currentDir);
15910
+ const parentDir = path12.dirname(currentDir);
15758
15911
  if (parentDir === currentDir) {
15759
15912
  throw new Error("Could not locate package.json for CLI metadata.");
15760
15913
  }
@@ -15926,7 +16079,8 @@ var cli = meow(
15926
16079
  build plan --input <path> Plan a Build inside an Orchestrator Run (prints one JSON object)
15927
16080
  build tick --input <path> Run one step of a started Build (prints the next action; [--rebase-failed <ID>]...)
15928
16081
  build checks The merge gate's verdict on HEAD's GitHub runs (in a child's worktree)
15929
- build step <n> Report the Run's current step: writes the step run artifact ([--qa none|web|macos|both], --profile, --complete --evidence, --verify)
16082
+ build step <n> Report the Run's current step: writes the step run artifact ([--qa none|web|macos|both], --profile, --merge you, --sign-off <sha>, --complete --evidence, --verify)
16083
+ build check <name> -- <cmd> Run a command and record its outcome: writes the checks run artifact
15930
16084
  build merged Record the merged pull request: writes the pull_request run artifact
15931
16085
  telemetry [action] Manage anonymous telemetry (enable/disable/status)
15932
16086
  doctor Diagnose Claude headless setup (use with --harness=claude)
@@ -16068,6 +16222,8 @@ var cli = meow(
16068
16222
  profile: { type: "string" },
16069
16223
  testFirst: { type: "string" },
16070
16224
  review: { type: "string" },
16225
+ merge: { type: "string" },
16226
+ signOff: { type: "string" },
16071
16227
  complete: { type: "boolean" },
16072
16228
  verify: { type: "boolean" },
16073
16229
  evidence: { type: "string" },
@@ -16113,7 +16269,7 @@ var cli = meow(
16113
16269
  }
16114
16270
  );
16115
16271
  async function main() {
16116
- const projectDir = path11.resolve(cli.flags.projectDir);
16272
+ const projectDir = path12.resolve(cli.flags.projectDir);
16117
16273
  const [command, ...commandArgs] = cli.input;
16118
16274
  const isRunCommand = command === "run" || command === "exec";
16119
16275
  if (command && !KNOWN_COMMANDS.has(command)) {
@@ -16167,7 +16323,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16167
16323
  await exitWith(1);
16168
16324
  return;
16169
16325
  }
16170
- const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-VVEEJW25.js");
16326
+ const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-FKASDA6N.js");
16171
16327
  const { waitUntilExit } = render(
16172
16328
  /* @__PURE__ */ jsx24(
16173
16329
  WorkflowInstallWizard,
@@ -16246,16 +16402,23 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16246
16402
  return;
16247
16403
  }
16248
16404
  if (command === "build") {
16249
- const [subcommand = "", ...buildArgs] = commandArgs;
16405
+ const rawArgs = process.argv.slice(2);
16406
+ const dashIdx = rawArgs.indexOf("--");
16407
+ const checkTokens = dashIdx >= 0 ? rawArgs.slice(dashIdx + 1) : [];
16408
+ const cleanArgs = checkTokens.length > 0 ? commandArgs.slice(0, commandArgs.length - checkTokens.length) : commandArgs;
16409
+ const [subcommand = "", ...buildArgs] = cleanArgs;
16250
16410
  await exitWith(
16251
16411
  await runBuildCommand({
16252
16412
  subcommand,
16253
16413
  positional: buildArgs,
16414
+ command: checkTokens,
16254
16415
  flags: {
16255
16416
  ...typeof cli.flags.qa === "string" ? { qa: cli.flags.qa } : {},
16256
16417
  profile: cli.flags.profile,
16257
16418
  testFirst: cli.flags.testFirst,
16258
16419
  review: cli.flags.review,
16420
+ merge: cli.flags.merge,
16421
+ signOff: cli.flags.signOff,
16259
16422
  complete: cli.flags.complete,
16260
16423
  verify: cli.flags.verify,
16261
16424
  evidence: cli.flags.evidence,
@@ -16340,8 +16503,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16340
16503
  const showSetup = isRunCommand ? false : shouldShowSetup({
16341
16504
  cliInput: cli.input,
16342
16505
  setupComplete: globalConfig.setupComplete,
16343
- globalConfigExists: fs11.existsSync(
16344
- path11.join(os5.homedir(), ".config", "athena", "config.json")
16506
+ globalConfigExists: fs12.existsSync(
16507
+ path12.join(os5.homedir(), ".config", "athena", "config.json")
16345
16508
  )
16346
16509
  });
16347
16510
  let runtimeConfig;
@@ -1,6 +1,6 @@
1
- import "./chunk-D5VZT7M3.js";
2
- import "./chunk-D246FR4I.js";
1
+ import "./chunk-Z5KVWWZU.js";
2
+ import "./chunk-TWOJJ2JD.js";
3
3
  import "./chunk-PQIB66SG.js";
4
- import "./chunk-HZ6GRLOM.js";
4
+ import "./chunk-FYXEDUQ6.js";
5
5
  import "./chunk-G3PMV62Z.js";
6
6
  //# sourceMappingURL=dashboard-daemon.js.map
package/dist/runner.js CHANGED
@@ -1,6 +1,6 @@
1
- import "./chunk-D5VZT7M3.js";
2
- import "./chunk-D246FR4I.js";
1
+ import "./chunk-Z5KVWWZU.js";
2
+ import "./chunk-TWOJJ2JD.js";
3
3
  import "./chunk-PQIB66SG.js";
4
- import "./chunk-HZ6GRLOM.js";
4
+ import "./chunk-FYXEDUQ6.js";
5
5
  import "./chunk-G3PMV62Z.js";
6
6
  //# sourceMappingURL=runner.js.map
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "hooks",
18
18
  "dashboard"
19
19
  ],
20
- "version": "0.7.7",
20
+ "version": "0.7.9",
21
21
  "license": "MIT",
22
22
  "bin": {
23
23
  "athena": "dist/cli.js",