@drisp/cli 0.7.0 → 0.7.2

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,7 +8,7 @@ import {
8
8
  installWorkflowPlugins,
9
9
  resolveWorkflow,
10
10
  writeGlobalConfig
11
- } from "./chunk-HVMTDYND.js";
11
+ } from "./chunk-2CJ6YKTL.js";
12
12
  import "./chunk-G3PMV62Z.js";
13
13
 
14
14
  // src/setup/steps/WorkflowInstallWizard.tsx
@@ -93,4 +93,4 @@ function WorkflowInstallWizard({ source, onDone }) {
93
93
  export {
94
94
  WorkflowInstallWizard as default
95
95
  };
96
- //# sourceMappingURL=WorkflowInstallWizard-OJAB2Z2X.js.map
96
+ //# sourceMappingURL=WorkflowInstallWizard-K3DWPLYZ.js.map
@@ -21432,7 +21432,7 @@ Commands that only read (\`ls\`, \`cat\`, \`grep\`) need no grant; anything else
21432
21432
  Write and Edit tools, never through shell redirection or a heredoc, and keep \`\${\u2026}\` and \`$(\u2026)\` expansions out of
21433
21433
  shell commands.
21434
21434
 
21435
- A refused tool call is not a stage that cannot pass: never answer one with a stage's "cannot pass" line. Do the work
21435
+ A refused tool call is not a gate that cannot pass: never answer one with a gate's "cannot pass" line. Do the work
21436
21436
  another way within these limits; when there is none, end the Turn with
21437
21437
  \`<!-- NEEDS_HUMAN: A tool this Build does not allow was refused. Open the Thread to see which one. -->\` as the
21438
21438
  Journal's last line.
@@ -21441,27 +21441,28 @@ Journal's last line.
21441
21441
 
21442
21442
  {instructions}
21443
21443
 
21444
- ## Stages
21444
+ ## Gates
21445
21445
 
21446
- Work through the stages in this order. Record each stage in the Journal before moving on. Never skip a stage and never
21447
- reorder them. When a stage's gate cannot pass, end the Turn with its "cannot pass" line exactly as written here,
21448
- inside \`<!-- NEEDS_HUMAN: \u2026 -->\`, as the Journal's last line; never write your own reason.
21446
+ Work through the gates in this order. Start each gate by running the command its "Start" line gives, which reports the
21447
+ gate to the Build page, then record the gate in the Journal before moving on. Never skip a gate and never reorder
21448
+ them. When a gate cannot pass, end the Turn with its "cannot pass" line exactly as written here, inside
21449
+ \`<!-- NEEDS_HUMAN: \u2026 -->\`, as the Journal's last line; never write your own reason.
21449
21450
 
21450
- {stages}
21451
+ {gates}
21451
21452
 
21452
21453
  ## Merge gate
21453
21454
 
21454
- After the last rebase, in this Run: Checks pass; both reviewers pass; QA passes when it is a stage; no remote check
21455
+ After the last rebase, in this Run: Checks pass; both reviewers pass; QA passes when it is a gate; no remote check
21455
21456
  failed after its job started.
21456
21457
 
21457
21458
  Before merging: \`git fetch origin\`; if \`origin/main\` is not an ancestor of HEAD, \`git rebase origin/main\`, run
21458
- Checks again and \`git push --force-with-lease\` to this branch. Repeat at most 3 times; then the Merge stage cannot
21459
+ Checks again and \`git push --force-with-lease\` to this branch. Repeat at most 3 times; then the Merge gate cannot
21459
21460
  pass.
21460
21461
 
21461
21462
  Then run \`drisp build checks\` in this worktree. It reads the GitHub runs on HEAD (\`gh run view <id> --json jobs\` for
21462
21463
  each failed one) and prints \`{"ok", "failed", "ignored", "pending"}\`. A failed run whose failed jobs ran zero steps
21463
21464
  is in \`ignored\`: it does not block the merge and its name goes in the evidence comment. While \`pending\` is not
21464
- empty, wait a minute and run it again. When \`failed\` is not empty, fix the change; if it still fails, the Merge stage
21465
+ empty, wait a minute and run it again. When \`failed\` is not empty, fix the change; if it still fails, the Merge gate
21465
21466
  cannot pass. Never judge a check by its timing.
21466
21467
 
21467
21468
  Merge with \`gh pr merge --squash --delete-branch --match-head-commit <sha>\`, where \`<sha>\` is HEAD after the last
@@ -21486,10 +21487,12 @@ Ignored checks (no steps ran): <names> | none
21486
21487
  Your Linear writes are exactly: In Progress in Explore, \`PR: <url>\` in Pull request, the evidence comment and Done in
21487
21488
  Evidence. Never create issues and never edit an issue's title or description.
21488
21489
 
21489
- When every stage has passed, end with \`<!-- WORKFLOW_COMPLETE -->\` as the Journal's last line.
21490
+ When every gate has passed, end with \`<!-- WORKFLOW_COMPLETE -->\` as the Journal's last line.
21490
21491
  `;
21491
21492
  var STAGE_TEMPLATE = `### {n}. {title}
21492
21493
 
21494
+ Start: run \`{stepCommand}\`.
21495
+
21493
21496
  Passes when: {passesWhen}.`;
21494
21497
  var CANNOT_PASS_TEMPLATE = `
21495
21498
 
@@ -21567,6 +21570,7 @@ var RENDERER_RULES = {
21567
21570
  edit: ["Edit(./**)"],
21568
21571
  shell: [
21569
21572
  "drisp build checks",
21573
+ "drisp build step",
21570
21574
  "gh pr checks",
21571
21575
  "gh pr create",
21572
21576
  "gh pr list",
@@ -21697,6 +21701,7 @@ function renderStages(fill, checks, install) {
21697
21701
  const stage = fillSlots(STAGE_TEMPLATE, {
21698
21702
  n: String(index + 1),
21699
21703
  title: gate.title,
21704
+ stepCommand: `drisp build step ${index + 1} --qa ${fill.qa}`,
21700
21705
  passesWhen
21701
21706
  });
21702
21707
  return stage + (gate.cannotPass ? fillSlots(CANNOT_PASS_TEMPLATE, { cannotPass: gate.cannotPass }) : NO_GATE_TEXT_TEMPLATE);
@@ -21729,7 +21734,7 @@ function render(fill, workflowName, layout) {
21729
21734
  const workflowMd = fillSlots(WORKFLOW_MD_TEMPLATE, {
21730
21735
  identifier: parsed.identifier,
21731
21736
  instructions: fill.instructions.trim(),
21732
- stages: renderStages(fill, checks, installCommand(fill, layout)),
21737
+ gates: renderStages(fill, checks, installCommand(fill, layout)),
21733
21738
  checksEvidence: checks.map((command) => `${command} \u2713`).join(" \xB7 "),
21734
21739
  shellCommands: checksText([...RENDERER_RULES.permissions.shell])
21735
21740
  });
@@ -22317,6 +22322,10 @@ import path10 from "path";
22317
22322
  // src/core/workflows/continuationPolicy.ts
22318
22323
  var DEFAULT_RESTART_TOKENS = 2e3;
22319
22324
  var DEFAULT_WORKING_TOKENS = 8e3;
22325
+ function budgetTokens(memory) {
22326
+ if (memory.cumulativeTokens === null) return null;
22327
+ return Math.max(0, memory.cumulativeTokens - (memory.usage?.cacheRead ?? 0));
22328
+ }
22320
22329
  function resourceInterruption(stop) {
22321
22330
  const names2 = {
22322
22331
  tokens: "token budget",
@@ -23297,7 +23306,7 @@ function admitResult(result, previous, cfg) {
23297
23306
  const stop = admitContinuation({
23298
23307
  loop: cfg.loop,
23299
23308
  iteration: memory.iteration,
23300
- tokens: memory.cumulativeTokens,
23309
+ tokens: budgetTokens(memory),
23301
23310
  checkpointPath: memory.checkpoint?.path,
23302
23311
  context: fresh ? context : void 0
23303
23312
  });
@@ -23745,7 +23754,7 @@ function createWorkflowRunner(input2) {
23745
23754
  cumulativeTokens: total,
23746
23755
  stop: {
23747
23756
  ...event.stop,
23748
- ...event.stop.cause === "tokens" ? { used: total } : {}
23757
+ ...event.stop.cause === "tokens" ? { used: budgetTokens(memory) ?? total } : {}
23749
23758
  }
23750
23759
  };
23751
23760
  }
@@ -23780,7 +23789,7 @@ function createWorkflowRunner(input2) {
23780
23789
  const limit = admitContinuation({
23781
23790
  loop,
23782
23791
  iteration: memory.iteration,
23783
- tokens: memory.cumulativeTokens
23792
+ tokens: budgetTokens(memory)
23784
23793
  });
23785
23794
  if (limit) {
23786
23795
  stop(limit);
@@ -23797,7 +23806,7 @@ function createWorkflowRunner(input2) {
23797
23806
  const decision = admitContinuation({
23798
23807
  loop,
23799
23808
  iteration: memory.iteration,
23800
- tokens: memory.cumulativeTokens,
23809
+ tokens: budgetTokens(memory),
23801
23810
  context: {
23802
23811
  opening: usage.openingContextSize,
23803
23812
  required: required2,
@@ -24242,4 +24251,4 @@ export {
24242
24251
  compileWorkflowPlan,
24243
24252
  collectMcpServersWithOptions
24244
24253
  };
24245
- //# sourceMappingURL=chunk-HVMTDYND.js.map
24254
+ //# sourceMappingURL=chunk-2CJ6YKTL.js.map
@@ -55,7 +55,7 @@ import {
55
55
  verifyRendered,
56
56
  wakesFreshAfterHandover,
57
57
  workflowRegistryDir
58
- } from "./chunk-HVMTDYND.js";
58
+ } from "./chunk-2CJ6YKTL.js";
59
59
  import {
60
60
  __commonJS,
61
61
  __toESM
@@ -3487,6 +3487,7 @@ function createTokenAccumulator() {
3487
3487
  let openingContextSize = 0;
3488
3488
  let contextWindowSize = null;
3489
3489
  let hasMessageUsage = false;
3490
+ const countedById = /* @__PURE__ */ new Map();
3490
3491
  function promptSize(usage) {
3491
3492
  return (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
3492
3493
  }
@@ -3544,10 +3545,13 @@ function createTokenAccumulator() {
3544
3545
  }
3545
3546
  } else {
3546
3547
  hasMessageUsage = true;
3547
- inputTokens += usage.input_tokens ?? 0;
3548
- outputTokens += usage.output_tokens ?? 0;
3549
- cacheRead += usage.cache_read_input_tokens ?? 0;
3550
- cacheWrite += usage.cache_creation_input_tokens ?? 0;
3548
+ const messageId = isAssistantEnvelope ? parsed.message["id"] : parsed["id"];
3549
+ const counted = typeof messageId === "string" ? countedById.get(messageId) : void 0;
3550
+ inputTokens += (usage.input_tokens ?? 0) - (counted?.input_tokens ?? 0);
3551
+ outputTokens += (usage.output_tokens ?? 0) - (counted?.output_tokens ?? 0);
3552
+ cacheRead += (usage.cache_read_input_tokens ?? 0) - (counted?.cache_read_input_tokens ?? 0);
3553
+ cacheWrite += (usage.cache_creation_input_tokens ?? 0) - (counted?.cache_creation_input_tokens ?? 0);
3554
+ if (typeof messageId === "string") countedById.set(messageId, usage);
3551
3555
  if (!isSubagent) {
3552
3556
  observeRootContext(promptSize(usage));
3553
3557
  }
@@ -3601,6 +3605,7 @@ function createTokenAccumulator() {
3601
3605
  reset() {
3602
3606
  buffer = "";
3603
3607
  hasMessageUsage = false;
3608
+ countedById.clear();
3604
3609
  inputTokens = 0;
3605
3610
  outputTokens = 0;
3606
3611
  cacheRead = 0;
@@ -14868,7 +14873,7 @@ async function restoreCheckpoint(store, input) {
14868
14873
 
14869
14874
  // ../../modules/runtime/src/software-version.ts
14870
14875
  import "fs";
14871
- var runnerSoftwareVersion = true ? "0.7.0" : JSON.parse(readFileSync2(new URL("../../../apps/cli/package.json", import.meta.url), "utf8")).version;
14876
+ var runnerSoftwareVersion = true ? "0.7.2" : JSON.parse(readFileSync2(new URL("../../../apps/cli/package.json", import.meta.url), "utf8")).version;
14872
14877
 
14873
14878
  // ../../modules/runtime/src/connection.ts
14874
14879
  var RunnerDisconnectedError = class extends Error {
@@ -20125,7 +20130,7 @@ function sessionStore(store, runId, options = {}) {
20125
20130
 
20126
20131
  // ../../modules/runtime/src/workflow-executor.ts
20127
20132
  import { execFileSync as execFileSync9 } from "child_process";
20128
- import { statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
20133
+ import { statSync as statSync3, writeFileSync as writeFileSync4 } from "fs";
20129
20134
  import { join as join8 } from "path";
20130
20135
 
20131
20136
  // src/core/build/workspace.ts
@@ -21148,20 +21153,22 @@ async function closeOrphanedRuns(store, {
21148
21153
 
21149
21154
  // ../../modules/runtime/src/run-artifacts.ts
21150
21155
  import { createHash as createHash3 } from "crypto";
21151
- import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
21156
+ import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
21152
21157
  import { tmpdir as tmpdir5 } from "os";
21153
21158
  import { dirname as dirname4, join as join7 } from "path";
21154
21159
  var ARTIFACT_MAX_BYTES = 64 * 1024;
21155
21160
  var ARTIFACT_NAME = /^[a-z][a-z0-9_]{0,63}$/;
21156
21161
  var NEVER_EMITTED = /* @__PURE__ */ new Set(["answer"]);
21157
21162
  var EMITTED_STATE = ".emitted.json";
21163
+ var MID_TURN_MAX_BYTES = 1024 * 1024;
21164
+ var MID_TURN_MIN_GAP_MS = 5e3;
21158
21165
  function runnerDataDirectory(databasePath) {
21159
21166
  return databasePath && databasePath !== ":memory:" ? dirname4(databasePath) : join7(tmpdir5(), "drisp-runner");
21160
21167
  }
21161
21168
  function runArtifactsDirectory(dataDirectory, runId) {
21162
21169
  return join7(dataDirectory, "artifacts", runId) + "/";
21163
21170
  }
21164
- function runArtifactEmitter(directory, record) {
21171
+ function runArtifactEmitter(directory, record, midTurnMinGapMs = MID_TURN_MIN_GAP_MS) {
21165
21172
  mkdirSync2(directory, { recursive: true, mode: 448 });
21166
21173
  const statePath = join7(directory, EMITTED_STATE);
21167
21174
  let emitted = {};
@@ -21169,27 +21176,43 @@ function runArtifactEmitter(directory, record) {
21169
21176
  emitted = JSON.parse(readFileSync4(statePath, "utf8"));
21170
21177
  } catch {
21171
21178
  }
21172
- return () => {
21179
+ const lastEmittedAt = {};
21180
+ return (midTurn = false) => {
21173
21181
  let changed = false;
21182
+ const now = Date.now();
21174
21183
  for (const entry of readdirSync2(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
21175
21184
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
21176
21185
  const name = entry.name.slice(0, -".json".length);
21177
21186
  if (!ARTIFACT_NAME.test(name) || NEVER_EMITTED.has(name)) continue;
21178
- const bytes = readFileSync4(join7(directory, entry.name));
21187
+ const path23 = join7(directory, entry.name);
21188
+ let bytes;
21189
+ try {
21190
+ if (midTurn) {
21191
+ if (statSync2(path23).size > MID_TURN_MAX_BYTES) continue;
21192
+ if (now - (lastEmittedAt[name] ?? -Infinity) < midTurnMinGapMs) continue;
21193
+ }
21194
+ bytes = readFileSync4(path23);
21195
+ } catch {
21196
+ continue;
21197
+ }
21179
21198
  const hash = createHash3("sha256").update(bytes).digest("hex");
21180
21199
  if (emitted[name] === hash) continue;
21181
- emitted[name] = hash;
21182
- changed = true;
21183
21200
  let json;
21184
21201
  try {
21185
21202
  json = JSON.parse(bytes.toString("utf8"));
21186
21203
  } catch {
21204
+ if (midTurn) continue;
21187
21205
  record("run.artifact_rejected", { name, bytes: bytes.length });
21206
+ emitted[name] = hash;
21207
+ changed = true;
21188
21208
  continue;
21189
21209
  }
21190
21210
  const serialized = Buffer.byteLength(JSON.stringify(json));
21191
21211
  if (serialized > ARTIFACT_MAX_BYTES) record("run.artifact_rejected", { name, bytes: serialized });
21192
21212
  else record("run.artifact", { name, json });
21213
+ emitted[name] = hash;
21214
+ lastEmittedAt[name] = now;
21215
+ changed = true;
21193
21216
  }
21194
21217
  if (changed) writeFileSync3(statePath, JSON.stringify(emitted));
21195
21218
  };
@@ -21225,7 +21248,7 @@ function delegationEnvironment(grant, hubUrl) {
21225
21248
  function requireProjectDirectory(projectDir) {
21226
21249
  let directory = false;
21227
21250
  try {
21228
- directory = statSync2(projectDir).isDirectory();
21251
+ directory = statSync3(projectDir).isDirectory();
21229
21252
  } catch {
21230
21253
  }
21231
21254
  if (!directory)
@@ -21316,7 +21339,7 @@ function createWorkflowExecutor(dependencies = {}) {
21316
21339
  dependencies.dataDirectory ?? runnerDataDirectory(store.sql.database.name),
21317
21340
  grant.runId
21318
21341
  );
21319
- const emitArtifacts2 = runArtifactEmitter(artifacts, record);
21342
+ const emitArtifacts2 = runArtifactEmitter(artifacts, record, (dependencies.artifactIntervalMs ?? 2e3) * 2.5);
21320
21343
  const loop = bootstrapped.workflow?.loop;
21321
21344
  const childLoop = grant.parent && loop ? { ...loop, maxRunTokens: Math.min(loop.maxRunTokens ?? Infinity, grant.parent.tokenCap) } : loop;
21322
21345
  const config2 = {
@@ -21403,6 +21426,13 @@ ${prompt2}`;
21403
21426
  const { workingDirectory, config, view, adapterResumeSessionId, storedAnswer, prompt, displayText, emitArtifacts } = setup2;
21404
21427
  const queue = createSteerQueue();
21405
21428
  queues.set(grant.runId, queue);
21429
+ const sweep = setInterval(() => {
21430
+ try {
21431
+ emitArtifacts(true);
21432
+ } catch {
21433
+ }
21434
+ }, dependencies.artifactIntervalMs ?? 2e3);
21435
+ sweep.unref();
21406
21436
  const result = await trackHarnessProcesses(store, grant.runId, () => (dependencies.execute ?? runExec)({
21407
21437
  prompt,
21408
21438
  promptPresentation: displayText === void 0 ? { origin: "workflow" } : { origin: "human", displayText },
@@ -21440,6 +21470,7 @@ ${prompt2}`;
21440
21470
  json: false
21441
21471
  })).finally(() => {
21442
21472
  queues.delete(grant.runId);
21473
+ clearInterval(sweep);
21443
21474
  emitArtifacts();
21444
21475
  });
21445
21476
  if (!result.workflowOutcome)
@@ -21852,6 +21883,7 @@ export {
21852
21883
  ExecutionStore,
21853
21884
  hubHttpUrl,
21854
21885
  runnerSoftwareVersion,
21886
+ fillSchema,
21855
21887
  planInputSchema,
21856
21888
  buildPlanSchema,
21857
21889
  git2 as git,
@@ -21886,4 +21918,4 @@ export {
21886
21918
  runMachineRunners,
21887
21919
  writeIdentityOnce
21888
21920
  };
21889
- //# sourceMappingURL=chunk-36375KNH.js.map
21921
+ //# sourceMappingURL=chunk-DAQU2MPK.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runMachineRunners
3
- } from "./chunk-36375KNH.js";
3
+ } from "./chunk-DAQU2MPK.js";
4
4
 
5
5
  // src/app/entry/runnerDaemon.ts
6
6
  if (process.argv.includes("--help")) {
@@ -13,4 +13,4 @@ if (process.argv.includes("--help")) {
13
13
  process.exitCode = 1;
14
14
  });
15
15
  }
16
- //# sourceMappingURL=chunk-DRPOYSOH.js.map
16
+ //# sourceMappingURL=chunk-Q4I2HYJ5.js.map
package/dist/cli.js CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  extractFriendlyServerName,
43
43
  extractPermissionSnapshot,
44
44
  fallbackBranch,
45
+ fillSchema,
45
46
  finalizePlan,
46
47
  fit,
47
48
  fitAnsi,
@@ -137,7 +138,7 @@ import {
137
138
  visualPositionToOffset,
138
139
  wrapText,
139
140
  writeIdentityOnce
140
- } from "./chunk-36375KNH.js";
141
+ } from "./chunk-DAQU2MPK.js";
141
142
  import {
142
143
  generateId as generateId2
143
144
  } from "./chunk-PQIB66SG.js";
@@ -167,6 +168,7 @@ import {
167
168
  deserializeRunMemory,
168
169
  external_exports,
169
170
  findMarketplaceRepoDir,
171
+ gateTitles,
170
172
  gatherMarketplaceWorkflowSources,
171
173
  hasProjectWorkflow,
172
174
  installWorkflowFromSource,
@@ -194,7 +196,7 @@ import {
194
196
  workflowRegistryDir,
195
197
  writeGlobalConfig,
196
198
  writeProjectConfig
197
- } from "./chunk-HVMTDYND.js";
199
+ } from "./chunk-2CJ6YKTL.js";
198
200
  import {
199
201
  __toESM
200
202
  } from "./chunk-G3PMV62Z.js";
@@ -202,10 +204,10 @@ import {
202
204
  // src/app/entry/cli.tsx
203
205
  import { render } from "ink";
204
206
  import meow from "meow";
205
- import fs9 from "fs";
207
+ import fs10 from "fs";
206
208
  import { createRequire } from "module";
207
209
  import os5 from "os";
208
- import path9 from "path";
210
+ import path10 from "path";
209
211
  import { fileURLToPath } from "url";
210
212
 
211
213
  // src/app/shell/sessionUiState.ts
@@ -5324,14 +5326,14 @@ function extractWebSearch(_input, response) {
5324
5326
  return { type: "text", content: extractTextContent(response), maxLines: 10 };
5325
5327
  }
5326
5328
  function extractNotebookEdit(input, _response) {
5327
- const path10 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5329
+ const path11 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
5328
5330
  const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
5329
5331
  const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
5330
- if (!source) return { type: "text", content: `${mode} cell in ${path10}` };
5332
+ if (!source) return { type: "text", content: `${mode} cell in ${path11}` };
5331
5333
  return {
5332
5334
  type: "code",
5333
5335
  content: source,
5334
- language: detectLanguage(path10),
5336
+ language: detectLanguage(path11),
5335
5337
  maxLines: 10
5336
5338
  };
5337
5339
  }
@@ -5857,10 +5859,10 @@ function extractToolSubject(toolName, toolInput) {
5857
5859
  const parts = [];
5858
5860
  const pattern = str(input, "pattern");
5859
5861
  const glob = str(input, "glob");
5860
- const path10 = str(input, "path");
5862
+ const path11 = str(input, "path");
5861
5863
  if (pattern) parts.push(`"${pattern}"`);
5862
5864
  if (glob) parts.push(glob);
5863
- if (path10) parts.push(`in ${path10}`);
5865
+ if (path11) parts.push(`in ${path11}`);
5864
5866
  return parts.length > 0 ? parts.join(" ") : void 0;
5865
5867
  }
5866
5868
  case "Skill":
@@ -13091,7 +13093,7 @@ var thisMachine = (installationId) => ({
13091
13093
  os: `${platform()} ${release()}`.slice(0, 64),
13092
13094
  softwareVersion: runnerSoftwareVersion
13093
13095
  });
13094
- var post = (origin, path10, body) => fetch(new URL(path10, origin), {
13096
+ var post = (origin, path11, body) => fetch(new URL(path11, origin), {
13095
13097
  method: "POST",
13096
13098
  headers: { "Content-Type": "application/json" },
13097
13099
  body: JSON.stringify(body)
@@ -13110,8 +13112,8 @@ async function alreadyConnected(existing, { forTeam, ownIdentity }) {
13110
13112
  const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
13111
13113
  const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
13112
13114
  if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
13113
- const path10 = (own ?? forTeam(existing.orgId)[0])?.path;
13114
- return { status: "identity_unusable", ...runner, problem, ...path10 ? { path: path10 } : {} };
13115
+ const path11 = (own ?? forTeam(existing.orgId)[0])?.path;
13116
+ return { status: "identity_unusable", ...runner, problem, ...path11 ? { path: path11 } : {} };
13115
13117
  }
13116
13118
  async function connectWithCode({
13117
13119
  code,
@@ -13183,20 +13185,20 @@ async function saveIdentity(origin, credential, { single, forTeam }, directory)
13183
13185
  name: credential.name
13184
13186
  });
13185
13187
  const replaced = [];
13186
- for (const { path: path11 } of forTeam(credential.orgId)) {
13187
- const aside = `${path11}.replaced-${Date.now()}`;
13188
- await rename(path11, aside);
13188
+ for (const { path: path12 } of forTeam(credential.orgId)) {
13189
+ const aside = `${path12}.replaced-${Date.now()}`;
13190
+ await rename(path12, aside);
13189
13191
  replaced.push(aside);
13190
13192
  }
13191
- const path10 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13193
+ const path11 = single ?? teamIdentityPath(origin, credential.orgId, directory);
13192
13194
  try {
13193
- await writeIdentityOnce(path10, JSON.stringify(config, null, 2) + "\n");
13195
+ await writeIdentityOnce(path11, JSON.stringify(config, null, 2) + "\n");
13194
13196
  } catch (error) {
13195
13197
  if (error.code === "EEXIST")
13196
- throw new Error(`Another Connect wrote a Runner identity for this Team at ${path10} meanwhile; it was left unchanged.`);
13198
+ throw new Error(`Another Connect wrote a Runner identity for this Team at ${path11} meanwhile; it was left unchanged.`);
13197
13199
  throw error;
13198
13200
  }
13199
- return { status: "connected", config, path: path10, replaced };
13201
+ return { status: "connected", config, path: path11, replaced };
13200
13202
  }
13201
13203
  function pathSegment(value) {
13202
13204
  const segment = encodeURIComponent(value);
@@ -13236,7 +13238,7 @@ async function refuseExisting(target) {
13236
13238
  throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
13237
13239
  }
13238
13240
  async function disconnectRunner({
13239
- identity: { path: path10, config },
13241
+ identity: { path: path11, config },
13240
13242
  serviceManager,
13241
13243
  othersRemain
13242
13244
  }) {
@@ -13269,7 +13271,7 @@ async function disconnectRunner({
13269
13271
  await restore();
13270
13272
  throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
13271
13273
  }
13272
- await forgetRunner(path10);
13274
+ await forgetRunner(path11);
13273
13275
  if (othersRemain) await restore();
13274
13276
  else await serviceManager?.remove();
13275
13277
  return result;
@@ -13422,18 +13424,18 @@ async function runRunnerCommandV2(input, options = {}) {
13422
13424
  `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.`
13423
13425
  );
13424
13426
  }
13425
- const { config, path: path10, replaced } = outcome;
13427
+ const { config, path: path11, replaced } = outcome;
13426
13428
  for (const aside of replaced)
13427
13429
  process.stdout.write(
13428
13430
  `The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
13429
13431
  `
13430
13432
  );
13431
13433
  process.stdout.write(
13432
- `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path10}.
13434
+ `Connected as ${config.name ?? config.runnerId}. Identity saved to ${path11}.
13433
13435
  `
13434
13436
  );
13435
13437
  if (services) {
13436
- const service = await ensureService(services, config, path10);
13438
+ const service = await ensureService(services, config, path11);
13437
13439
  process.stdout.write(`${serviceStarted(service)}
13438
13440
  `);
13439
13441
  return 0;
@@ -13442,7 +13444,7 @@ async function runRunnerCommandV2(input, options = {}) {
13442
13444
  process.stdout.write(
13443
13445
  "No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
13444
13446
  );
13445
- await runConfiguredRunner(path10, options.signal);
13447
+ await runConfiguredRunner(path11, options.signal);
13446
13448
  return 0;
13447
13449
  }
13448
13450
  case "disconnect": {
@@ -13513,9 +13515,9 @@ async function runRunnerCommandV2(input, options = {}) {
13513
13515
  configured: true,
13514
13516
  // One entry per Team this machine serves; credentials are never printed.
13515
13517
  teams: identities.map(
13516
- ({ path: path10, config: { token: _token, ...config } }) => ({
13518
+ ({ path: path11, config: { token: _token, ...config } }) => ({
13517
13519
  ...config,
13518
- path: path10
13520
+ path: path11
13519
13521
  })
13520
13522
  ),
13521
13523
  service: service ? {
@@ -13860,9 +13862,9 @@ var DrispClient = class _DrispClient {
13860
13862
  if (!match) throw new Error("invalid_hub_url");
13861
13863
  return new _DrispClient(url.origin, decodeURIComponent(match[1]), async () => token);
13862
13864
  }
13863
- async request(path10, body) {
13865
+ async request(path11, body) {
13864
13866
  const response = await fetch(
13865
- `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path10}`,
13867
+ `${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path11}`,
13866
13868
  {
13867
13869
  method: body === void 0 ? "GET" : "POST",
13868
13870
  headers: {
@@ -14703,13 +14705,50 @@ function evaluateRemoteChecks(runs) {
14703
14705
  return verdict;
14704
14706
  }
14705
14707
 
14708
+ // src/core/build/step.ts
14709
+ import fs8 from "fs";
14710
+ import path8 from "path";
14711
+ var QA_KINDS = fillSchema.shape.qa.options;
14712
+ function runBuildStep(input) {
14713
+ const directory = requireEnv(input.env, "DRISP_RUN_ARTIFACTS");
14714
+ const qa = input.qa ?? "web";
14715
+ if (!QA_KINDS.includes(qa))
14716
+ throw new BuildUsageError(
14717
+ `--qa must be one of ${QA_KINDS.join(", ")}, not "${qa}".`
14718
+ );
14719
+ const titles = gateTitles({ qa });
14720
+ const index = /^[0-9]+$/.test(input.n ?? "") ? Number(input.n) : 0;
14721
+ if (index < 1 || index > titles.length)
14722
+ throw new BuildUsageError(
14723
+ `Step must be a whole number between 1 and ${titles.length}, not "${input.n ?? ""}".`
14724
+ );
14725
+ if (!fs8.statSync(directory, { throwIfNoEntry: false })?.isDirectory())
14726
+ throw new BuildUsageError(
14727
+ `DRISP_RUN_ARTIFACTS (${directory}) is not a directory.`
14728
+ );
14729
+ const artifact = {
14730
+ index,
14731
+ title: titles[index - 1],
14732
+ total: titles.length
14733
+ };
14734
+ fs8.writeFileSync(
14735
+ path8.join(directory, "step.json"),
14736
+ `${JSON.stringify(artifact)}
14737
+ `
14738
+ );
14739
+ return artifact;
14740
+ }
14741
+
14706
14742
  // src/app/entry/buildCommand.ts
14707
14743
  var USAGE3 = `Usage: drisp build plan --input <path>
14708
14744
  drisp build tick --input <path> [--rebase-failed <IDENTIFIER>]...
14709
14745
  drisp build checks
14746
+ drisp build step <n> [--qa none|web|macos]
14710
14747
  plan and tick run inside an Orchestrator Run (DRISP_RUN_ID, DRISP_RUNNER_ID and DRISP_RUN_ARTIFACTS set; tick also
14711
14748
  DRISP_RUN_TOKEN and DRISP_HUB_URL). DRISP_BUILD_PLAN_INPUT=<path> stands in for plan's --input, with
14712
- fills/<IDENTIFIER>.json beside it. checks runs in a child's worktree: the merge gate's verdict on HEAD's GitHub runs.`;
14749
+ fills/<IDENTIFIER>.json beside it. checks runs in a child's worktree: the merge gate's verdict on HEAD's GitHub runs.
14750
+ step runs in any Run with DRISP_RUN_ARTIFACTS set: it writes the step artifact {index, title, total} for gate <n> of the
14751
+ build-child template (QA is a gate unless --qa none) and refuses an index outside the list.`;
14713
14752
  function githubPullRequests(folder, branch) {
14714
14753
  const output = execFileSync(
14715
14754
  "gh",
@@ -14827,6 +14866,13 @@ async function runBuildCommand(input) {
14827
14866
  case "checks":
14828
14867
  output = runBuildChecks(input.cwd, input.checks ?? defaultChecks);
14829
14868
  break;
14869
+ case "step":
14870
+ output = runBuildStep({
14871
+ env: env2,
14872
+ n: input.positional?.[0],
14873
+ ...input.flags.qa ? { qa: input.flags.qa } : {}
14874
+ });
14875
+ break;
14830
14876
  default:
14831
14877
  stderr.write(`${USAGE3}
14832
14878
  `);
@@ -14849,9 +14895,9 @@ import process7 from "process";
14849
14895
 
14850
14896
  // src/harnesses/claude/system/doctorEnvironment.ts
14851
14897
  import { execFileSync as execFileSync2 } from "child_process";
14852
- import * as fs8 from "fs";
14898
+ import * as fs9 from "fs";
14853
14899
  import * as os4 from "os";
14854
- import * as path8 from "path";
14900
+ import * as path9 from "path";
14855
14901
  var MANAGED_POLICY_KEYS = [
14856
14902
  "forceLoginMethod",
14857
14903
  "forceLoginOrgUUID",
@@ -14955,8 +15001,8 @@ function collectEnvironment(options = {}) {
14955
15001
  const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
14956
15002
  const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
14957
15003
  const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
14958
- const statFn = options.statFn ?? ((p) => fs8.statSync(p));
14959
- const readFileFn = options.readFileFn ?? ((p) => fs8.readFileSync(p, "utf8"));
15004
+ const statFn = options.statFn ?? ((p) => fs9.statSync(p));
15005
+ const readFileFn = options.readFileFn ?? ((p) => fs9.readFileSync(p, "utf8"));
14960
15006
  const settingsPaths = resolveClaudeSettingsSurfacePaths(
14961
15007
  homeDir,
14962
15008
  cwd,
@@ -15001,7 +15047,7 @@ function collectEnvironment(options = {}) {
15001
15047
  });
15002
15048
  let globalConfigPresent = false;
15003
15049
  try {
15004
- statFn(path8.join(homeDir, ".claude.json"));
15050
+ statFn(path9.join(homeDir, ".claude.json"));
15005
15051
  globalConfigPresent = true;
15006
15052
  } catch {
15007
15053
  globalConfigPresent = false;
@@ -15481,13 +15527,13 @@ function runRunsCommand(input) {
15481
15527
  // src/app/entry/cli.tsx
15482
15528
  import { jsx as jsx24 } from "react/jsx-runtime";
15483
15529
  function resolvePackageJsonPath(entryUrl) {
15484
- let currentDir = path9.dirname(fileURLToPath(entryUrl));
15530
+ let currentDir = path10.dirname(fileURLToPath(entryUrl));
15485
15531
  for (; ; ) {
15486
- const candidatePath = path9.join(currentDir, "package.json");
15487
- if (fs9.existsSync(candidatePath)) {
15532
+ const candidatePath = path10.join(currentDir, "package.json");
15533
+ if (fs10.existsSync(candidatePath)) {
15488
15534
  return candidatePath;
15489
15535
  }
15490
- const parentDir = path9.dirname(currentDir);
15536
+ const parentDir = path10.dirname(currentDir);
15491
15537
  if (parentDir === currentDir) {
15492
15538
  throw new Error("Could not locate package.json for CLI metadata.");
15493
15539
  }
@@ -15659,6 +15705,7 @@ var cli = meow(
15659
15705
  build plan --input <path> Plan a Build inside an Orchestrator Run (prints one JSON object)
15660
15706
  build tick --input <path> Run one step of a started Build (prints the next action; [--rebase-failed <ID>]...)
15661
15707
  build checks The merge gate's verdict on HEAD's GitHub runs (in a child's worktree)
15708
+ build step <n> Report the Run's current step: writes the step run artifact ([--qa none|web|macos])
15662
15709
  telemetry [action] Manage anonymous telemetry (enable/disable/status)
15663
15710
  doctor Diagnose Claude headless setup (use with --harness=claude)
15664
15711
 
@@ -15796,6 +15843,9 @@ var cli = meow(
15796
15843
  type: "string",
15797
15844
  isMultiple: true
15798
15845
  },
15846
+ qa: {
15847
+ type: "string"
15848
+ },
15799
15849
  foreground: {
15800
15850
  type: "boolean",
15801
15851
  default: false
@@ -15835,7 +15885,7 @@ var cli = meow(
15835
15885
  }
15836
15886
  );
15837
15887
  async function main() {
15838
- const projectDir = path9.resolve(cli.flags.projectDir);
15888
+ const projectDir = path10.resolve(cli.flags.projectDir);
15839
15889
  const [command, ...commandArgs] = cli.input;
15840
15890
  const isRunCommand = command === "run" || command === "exec";
15841
15891
  if (command && !KNOWN_COMMANDS.has(command)) {
@@ -15889,7 +15939,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
15889
15939
  await exitWith(1);
15890
15940
  return;
15891
15941
  }
15892
- const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-OJAB2Z2X.js");
15942
+ const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-K3DWPLYZ.js");
15893
15943
  const { waitUntilExit } = render(
15894
15944
  /* @__PURE__ */ jsx24(
15895
15945
  WorkflowInstallWizard,
@@ -15968,11 +16018,13 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
15968
16018
  return;
15969
16019
  }
15970
16020
  if (command === "build") {
15971
- const [subcommand = ""] = commandArgs;
16021
+ const [subcommand = "", ...buildArgs] = commandArgs;
15972
16022
  await exitWith(
15973
16023
  await runBuildCommand({
15974
16024
  subcommand,
16025
+ positional: buildArgs,
15975
16026
  flags: {
16027
+ ...typeof cli.flags.qa === "string" ? { qa: cli.flags.qa } : {},
15976
16028
  ...typeof cli.flags.input === "string" ? { input: cli.flags.input } : {},
15977
16029
  ...cli.flags.rebaseFailed?.length ? { rebaseFailed: cli.flags.rebaseFailed } : {}
15978
16030
  },
@@ -16054,8 +16106,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
16054
16106
  const showSetup = isRunCommand ? false : shouldShowSetup({
16055
16107
  cliInput: cli.input,
16056
16108
  setupComplete: globalConfig.setupComplete,
16057
- globalConfigExists: fs9.existsSync(
16058
- path9.join(os5.homedir(), ".config", "athena", "config.json")
16109
+ globalConfigExists: fs10.existsSync(
16110
+ path10.join(os5.homedir(), ".config", "athena", "config.json")
16059
16111
  )
16060
16112
  });
16061
16113
  let runtimeConfig;
@@ -1,6 +1,6 @@
1
- import "./chunk-DRPOYSOH.js";
2
- import "./chunk-36375KNH.js";
1
+ import "./chunk-Q4I2HYJ5.js";
2
+ import "./chunk-DAQU2MPK.js";
3
3
  import "./chunk-PQIB66SG.js";
4
- import "./chunk-HVMTDYND.js";
4
+ import "./chunk-2CJ6YKTL.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-DRPOYSOH.js";
2
- import "./chunk-36375KNH.js";
1
+ import "./chunk-Q4I2HYJ5.js";
2
+ import "./chunk-DAQU2MPK.js";
3
3
  import "./chunk-PQIB66SG.js";
4
- import "./chunk-HVMTDYND.js";
4
+ import "./chunk-2CJ6YKTL.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.0",
20
+ "version": "0.7.2",
21
21
  "license": "MIT",
22
22
  "bin": {
23
23
  "athena": "dist/cli.js",