@higherdev/cli 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +84 -217
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -482,43 +482,11 @@ var init_lifecycle = __esm({
482
482
  }
483
483
  });
484
484
 
485
- // ../../packages/db/src/commands/execute.ts
486
- function ownerTicketCommand(opts) {
487
- return {
488
- commandId: opts.operationId,
489
- idempotencyKey: `owner-ticket:${opts.workspaceId}:${opts.ticketId}:${opts.operationId}`,
490
- actor: { type: "human", id: opts.actorEmail.trim().toLowerCase() },
491
- workspaceId: opts.workspaceId,
492
- target: { type: "ticket", id: opts.ticketId },
493
- expected: { status: opts.expectedStatus, version: opts.expectedVersion },
494
- intent: opts.intent
495
- };
496
- }
497
- function isLifecycleResult(value) {
498
- return Boolean(
499
- value && typeof value === "object" && !Array.isArray(value) && (value.outcome === "accepted" || value.outcome === "rejected")
500
- );
501
- }
502
- async function applyOwnerTicketCommand(db, command) {
503
- const { data, error } = await db.rpc("apply_owner_ticket_command", {
504
- p_command: command
505
- });
506
- if (error) throw error;
507
- if (!isLifecycleResult(data)) throw new Error("Owner ticket command returned an invalid receipt.");
508
- return data;
509
- }
510
- var init_execute = __esm({
511
- "../../packages/db/src/commands/execute.ts"() {
512
- "use strict";
513
- }
514
- });
515
-
516
485
  // ../../packages/db/src/commands/index.ts
517
486
  var init_commands = __esm({
518
487
  "../../packages/db/src/commands/index.ts"() {
519
488
  "use strict";
520
489
  init_lifecycle();
521
- init_execute();
522
490
  }
523
491
  });
524
492
 
@@ -755,7 +723,6 @@ var init_schemas = __esm({
755
723
  api_key_hash: z.string().nullable(),
756
724
  paused: z.boolean(),
757
725
  webhook_secret: z.string().nullable().optional(),
758
- webhook_secret_pending: z.string().nullable().optional(),
759
726
  webhook_last_status: z.string().nullable().optional(),
760
727
  webhook_last_at: isoDateSchema.nullable().optional(),
761
728
  webhook_attempts: z.number().int().nonnegative().optional(),
@@ -1120,9 +1087,9 @@ function uncertainCreateError(error) {
1120
1087
  return Boolean(error && (!error.code || /^PGRST00[0-3]$/.test(error.code)));
1121
1088
  }
1122
1089
  async function createTicketRecord(db, workspaceId, fields, ticketId) {
1123
- const requestId2 = ticketId ?? globalThis.crypto.randomUUID();
1090
+ const requestId = ticketId ?? globalThis.crypto.randomUUID();
1124
1091
  const args = {
1125
- p_ticket_id: requestId2,
1092
+ p_ticket_id: requestId,
1126
1093
  p_workspace_id: workspaceId,
1127
1094
  p_fields: fields
1128
1095
  };
@@ -1964,17 +1931,6 @@ var init_connection = __esm({
1964
1931
  }
1965
1932
  });
1966
1933
 
1967
- // src/tui/capability.ts
1968
- function canLaunchApp(streams = { stdin: process.stdin, stdout: process.stdout }) {
1969
- return !isJsonMode() && streams.stdin.isTTY === true && streams.stdout.isTTY === true;
1970
- }
1971
- var init_capability = __esm({
1972
- "src/tui/capability.ts"() {
1973
- "use strict";
1974
- init_json();
1975
- }
1976
- });
1977
-
1978
1934
  // src/architect/brain.ts
1979
1935
  import { createInterface } from "readline/promises";
1980
1936
  function brainById(id) {
@@ -1996,40 +1952,36 @@ async function confirm(question) {
1996
1952
  async function requireBrain(opts = {}) {
1997
1953
  const config = await loadConfig();
1998
1954
  const chosen = config.brain ?? DEFAULT_BRAIN;
1999
- const interactive = opts.interactive ?? (opts.fatal !== false && process.stdin.isTTY && process.stdout.isTTY);
1955
+ const interactive = opts.interactive ?? (process.stdin.isTTY && process.stdout.isTTY);
2000
1956
  const connection = await codexConnection();
2001
1957
  if (connection.state === "connected") {
2002
1958
  if (!config.brain) await saveConfig({ brain: chosen });
2003
1959
  return chosen;
2004
1960
  }
2005
1961
  if (!interactive) {
2006
- brainFailure(`${connectionSummary(connection)}
2007
- ${howToFix(connection) ?? ""}`.trim(), opts.fatal);
1962
+ fail(`${connectionSummary(connection)}
1963
+ ${howToFix(connection) ?? ""}`.trim());
2008
1964
  }
2009
1965
  if (connection.state === "not_installed") {
2010
- brainFailure(`${connectionSummary(connection)}
2011
- ${howToFix(connection) ?? ""}`.trim(), opts.fatal);
1966
+ fail(`${connectionSummary(connection)}
1967
+ ${howToFix(connection) ?? ""}`.trim());
2012
1968
  }
2013
1969
  const brain = brainById(chosen.id);
2014
1970
  out("");
2015
1971
  out(`${c.bold("HigherDEV needs your ChatGPT subscription to answer.")}`);
2016
1972
  out(c.dim(`${brain?.billing ?? ""}. The sign-in happens in your browser; hd never sees the token.`));
2017
1973
  if (!await confirm(c.dim("Press enter to sign in, or n to cancel: "))) {
2018
- brainFailure("Not connected. Run `hd brain` when you want to.", opts.fatal);
1974
+ fail("Not connected. Run `hd brain` when you want to.");
2019
1975
  }
2020
1976
  const connected = await connectCodex();
2021
1977
  if (connected.state !== "connected") {
2022
- brainFailure(`${connectionSummary(connected)}
2023
- ${howToFix(connected) ?? ""}`.trim(), opts.fatal);
1978
+ fail(`${connectionSummary(connected)}
1979
+ ${howToFix(connected) ?? ""}`.trim());
2024
1980
  }
2025
1981
  out(c.green(connectionSummary(connected)));
2026
1982
  await saveConfig({ brain: chosen });
2027
1983
  return chosen;
2028
1984
  }
2029
- function brainFailure(message, fatal = true) {
2030
- if (fatal) fail(message);
2031
- throw new Error(message);
2032
- }
2033
1985
  async function setBrain(patch) {
2034
1986
  const config = await loadConfig();
2035
1987
  const next = { ...config.brain ?? DEFAULT_BRAIN, ...patch };
@@ -3529,31 +3481,14 @@ async function createTicket(ctx, input) {
3529
3481
  await ctx.audit(ctx.workspace.id, "create", { ticket_key: data.key, subject: "a ticket" });
3530
3482
  return data;
3531
3483
  }
3532
- async function applyOwnerCommand(ctx, ticket, operationId, intent) {
3533
- const result = await applyOwnerTicketCommand(ctx.db, ownerTicketCommand({
3534
- operationId,
3535
- actorEmail: ctx.email,
3536
- workspaceId: ctx.workspace.id,
3537
- ticketId: ticket.id,
3538
- expectedStatus: ticket.status,
3539
- expectedVersion: ticket.lifecycle_version,
3540
- intent
3541
- }));
3542
- if (result.outcome === "rejected") {
3543
- fail(`Ticket command rejected: ${result.code}. Retry with a new --request-id after reloading the ticket.`);
3544
- }
3545
- const { data, error } = await ctx.db.from("tickets").select("*").eq("id", ticket.id).single();
3546
- if (error) fail(error.message);
3547
- return data;
3548
- }
3549
- async function updateTicket(ctx, ticket, patch, operationId) {
3484
+ async function updateTicket(ctx, ticket, patch) {
3550
3485
  const fields = {};
3551
3486
  if (patch.title !== void 0) fields.title = patch.title.trim();
3552
- if (patch.body !== void 0) fields.bodyMd = patch.body;
3553
- if (patch.acceptance !== void 0) fields.acceptanceMd = patch.acceptance;
3487
+ if (patch.body !== void 0) fields.body_md = patch.body;
3488
+ if (patch.acceptance !== void 0) fields.acceptance_md = patch.acceptance;
3554
3489
  if (patch.area !== void 0) fields.area = patch.area;
3555
3490
  if (patch.priority !== void 0) fields.priority = patch.priority;
3556
- if (patch.epic !== void 0) fields.epicId = patch.epic;
3491
+ if (patch.epic !== void 0) fields.epic_id = patch.epic;
3557
3492
  if (patch.host !== void 0) {
3558
3493
  const hostError = await assertHost(ctx.db, patch.host, ctx.workspace.repo);
3559
3494
  if (hostError) fail(hostError);
@@ -3563,7 +3498,9 @@ async function updateTicket(ctx, ticket, patch, operationId) {
3563
3498
  const agentId = await agentIdFor(ctx, patch.agent);
3564
3499
  const assigned = await resolveAssignedBuilder(ctx.db, ctx.workspace.id, agentId, null);
3565
3500
  if (!assigned.ok) fail(assigned.error);
3566
- fields.agentId = assigned.agent?.id ?? null;
3501
+ fields.agent_id = assigned.agent?.id ?? null;
3502
+ fields.provider = assigned.agent?.provider ?? null;
3503
+ fields.provider_pinned = Boolean(assigned.agent);
3567
3504
  }
3568
3505
  if (patch.status !== void 0) {
3569
3506
  const wanted = patch.status.trim();
@@ -3572,19 +3509,19 @@ async function updateTicket(ctx, ticket, patch, operationId) {
3572
3509
  }
3573
3510
  const blockers = await blockerStatuses(ctx.db, ctx.workspace.id, ticket.blocked_by ?? [], ticket.id);
3574
3511
  if (blockers.error) fail(blockers.error);
3512
+ fields.status = resolveTicketStatus(wanted, blockers.statuses);
3575
3513
  }
3576
- if (Object.keys(fields).length === 0 && patch.status === void 0) fail("Nothing to change.");
3577
- if (Object.keys(fields).length === 0) fields.title = ticket.title;
3578
- const requested = patch.status === void 0 ? ticket.status : patch.status.trim();
3579
- return applyOwnerCommand(ctx, ticket, operationId, {
3580
- type: "edit_ticket",
3581
- fields,
3582
- blockedBy: ticket.blocked_by ?? [],
3583
- toStatus: requested,
3584
- reason: `CLI owner edited ${ticket.key}`
3514
+ if (Object.keys(fields).length === 0) fail("Nothing to change.");
3515
+ const { data, error } = await ctx.db.from("tickets").update(fields).eq("id", ticket.id).select("*").single();
3516
+ if (error) fail(error.message);
3517
+ await ctx.audit(ctx.workspace.id, "update", {
3518
+ ticket_key: ticket.key,
3519
+ subject: "a ticket",
3520
+ fields: Object.keys(fields).join(", ")
3585
3521
  });
3522
+ return data;
3586
3523
  }
3587
- async function setBlockedBy(ctx, ticket, keys, mode, operationId) {
3524
+ async function setBlockedBy(ctx, ticket, keys, mode) {
3588
3525
  const ids = await keysToIds(ctx, keys);
3589
3526
  const current = ticket.blocked_by ?? [];
3590
3527
  const next = mode === "set" ? ids : mode === "add" ? [.../* @__PURE__ */ new Set([...current, ...ids])] : current.filter((id) => !ids.includes(id));
@@ -3595,22 +3532,23 @@ async function setBlockedBy(ctx, ticket, keys, mode, operationId) {
3595
3532
  }
3596
3533
  const blockers = await blockerStatuses(ctx.db, ctx.workspace.id, next, ticket.id);
3597
3534
  if (blockers.error) fail(blockers.error);
3598
- return applyOwnerCommand(ctx, ticket, operationId, {
3599
- type: "set_dependencies",
3600
- blockedBy: blockers.ids,
3601
- toStatus: ticket.status,
3602
- reason: `CLI owner changed dependencies for ${ticket.key}`
3535
+ const status = resolveTicketStatus(ticket.status, blockers.statuses);
3536
+ const { data, error } = await ctx.db.from("tickets").update({ blocked_by: blockers.ids, status }).eq("id", ticket.id).select("*").single();
3537
+ if (error) fail(error.message);
3538
+ await ctx.audit(ctx.workspace.id, "update", {
3539
+ ticket_key: ticket.key,
3540
+ subject: "dependencies"
3603
3541
  });
3542
+ return data;
3604
3543
  }
3605
- async function takeOver(ctx, ticket, operationId) {
3544
+ async function takeOver(ctx, ticket) {
3606
3545
  if (ticket.human_owner) fail(`${ticket.key} is already owned by ${ticket.human_owner}.`);
3607
- return applyOwnerCommand(ctx, ticket, operationId, {
3608
- type: "take_over",
3609
- owner: ctx.email,
3610
- reason: `CLI owner took over ${ticket.key}`
3611
- });
3546
+ const { data, error } = await ctx.db.from("tickets").update({ human_owner: ctx.email }).eq("id", ticket.id).select("*").single();
3547
+ if (error) fail(error.message);
3548
+ await ctx.audit(ctx.workspace.id, "takeover", { ticket_key: ticket.key });
3549
+ return data;
3612
3550
  }
3613
- async function handBack(ctx, ticket, operationId) {
3551
+ async function handBack(ctx, ticket) {
3614
3552
  if (!ticket.human_owner) fail(`${ticket.key} is not human-owned.`);
3615
3553
  const blockers = await blockerStatuses(ctx.db, ctx.workspace.id, ticket.blocked_by ?? [], ticket.id);
3616
3554
  if (blockers.error) fail(blockers.error);
@@ -3620,11 +3558,10 @@ async function handBack(ctx, ticket, operationId) {
3620
3558
  assigned: Boolean(ticket.agent_id),
3621
3559
  blockerStatuses: blockers.statuses
3622
3560
  });
3623
- return applyOwnerCommand(ctx, ticket, operationId, {
3624
- type: "hand_back",
3625
- toStatus: status,
3626
- reason: `CLI owner handed back ${ticket.key}`
3627
- });
3561
+ const { data, error } = await ctx.db.from("tickets").update({ human_owner: null, status }).eq("id", ticket.id).select("*").single();
3562
+ if (error) fail(error.message);
3563
+ await ctx.audit(ctx.workspace.id, "hand_back", { ticket_key: ticket.key });
3564
+ return data;
3628
3565
  }
3629
3566
  async function killRun(ctx, runId, ticketKey) {
3630
3567
  const { data, error } = await ctx.db.rpc("request_run_cancellation", {
@@ -4148,7 +4085,6 @@ var init_commands4 = __esm({
4148
4085
  });
4149
4086
 
4150
4087
  // src/architect/tools.ts
4151
- import { randomUUID as randomUUID3 } from "crypto";
4152
4088
  import { z as z2 } from "zod";
4153
4089
  async function ticketOf(ctx, wanted) {
4154
4090
  const board = await loadBoard(ctx);
@@ -4366,12 +4302,10 @@ var init_tools = __esm({
4366
4302
  agent: z2.string().nullable().optional(),
4367
4303
  priority: z2.number().optional(),
4368
4304
  status: z2.string().optional(),
4369
- epic_id: z2.string().nullable().optional(),
4370
- request_id: z2.string().optional()
4305
+ epic_id: z2.string().nullable().optional()
4371
4306
  }),
4372
4307
  async run(ctx, args) {
4373
4308
  const ticket = await ticketOf(ctx, String(args.key));
4374
- const operationId = String(args.request_id ?? randomUUID3());
4375
4309
  const updated = await updateTicket(ctx, ticket, {
4376
4310
  title: args.title,
4377
4311
  body: args.body_md,
@@ -4381,8 +4315,8 @@ var init_tools = __esm({
4381
4315
  priority: args.priority,
4382
4316
  status: args.status,
4383
4317
  epic: args.epic_id
4384
- }, operationId);
4385
- return { key: updated.key, status: updated.status, request_id: operationId };
4318
+ });
4319
+ return { key: updated.key, status: updated.status };
4386
4320
  }
4387
4321
  },
4388
4322
  {
@@ -4392,20 +4326,17 @@ var init_tools = __esm({
4392
4326
  schema: z2.object({
4393
4327
  key,
4394
4328
  blocked_by: z2.array(z2.string()).describe("Ticket keys."),
4395
- mode: z2.enum(["set", "add", "remove"]).optional(),
4396
- request_id: z2.string().optional()
4329
+ mode: z2.enum(["set", "add", "remove"]).optional()
4397
4330
  }),
4398
4331
  async run(ctx, args) {
4399
4332
  const ticket = await ticketOf(ctx, String(args.key));
4400
- const operationId = String(args.request_id ?? randomUUID3());
4401
4333
  const updated = await setBlockedBy(
4402
4334
  ctx,
4403
4335
  ticket,
4404
4336
  args.blocked_by ?? [],
4405
- args.mode ?? "set",
4406
- operationId
4337
+ args.mode ?? "set"
4407
4338
  );
4408
- return { key: updated.key, status: updated.status, blocked_by: updated.blocked_by, request_id: operationId };
4339
+ return { key: updated.key, status: updated.status, blocked_by: updated.blocked_by };
4409
4340
  }
4410
4341
  },
4411
4342
  {
@@ -4490,22 +4421,20 @@ var init_tools = __esm({
4490
4421
  name: "take_over_ticket",
4491
4422
  access: "write",
4492
4423
  description: "Mark a ticket human-owned so the board stops dispatching it.",
4493
- schema: z2.object({ key, request_id: z2.string().optional() }),
4424
+ schema: z2.object({ key }),
4494
4425
  async run(ctx, args) {
4495
- const operationId = String(args.request_id ?? randomUUID3());
4496
- const updated = await takeOver(ctx, await ticketOf(ctx, String(args.key)), operationId);
4497
- return { key: updated.key, human_owner: updated.human_owner, request_id: operationId };
4426
+ const updated = await takeOver(ctx, await ticketOf(ctx, String(args.key)));
4427
+ return { key: updated.key, human_owner: updated.human_owner };
4498
4428
  }
4499
4429
  },
4500
4430
  {
4501
4431
  name: "hand_back_ticket",
4502
4432
  access: "write",
4503
4433
  description: "Give a human-owned ticket back to the board.",
4504
- schema: z2.object({ key, request_id: z2.string().optional() }),
4434
+ schema: z2.object({ key }),
4505
4435
  async run(ctx, args) {
4506
- const operationId = String(args.request_id ?? randomUUID3());
4507
- const updated = await handBack(ctx, await ticketOf(ctx, String(args.key)), operationId);
4508
- return { key: updated.key, status: updated.status, request_id: operationId };
4436
+ const updated = await handBack(ctx, await ticketOf(ctx, String(args.key)));
4437
+ return { key: updated.key, status: updated.status };
4509
4438
  }
4510
4439
  },
4511
4440
  {
@@ -4675,8 +4604,7 @@ function architectInstructions(opts) {
4675
4604
  }
4676
4605
  async function openArchitect(opts) {
4677
4606
  const { ctx } = opts;
4678
- const fatalAuth = opts.fatalAuth ?? true;
4679
- const brain = await requireBrain({ fatal: fatalAuth });
4607
+ const brain = await requireBrain();
4680
4608
  const repo = await repoCheckout(ctx);
4681
4609
  const platformTools = toolsFor(opts.readOnly ? "read" : "all").map((tool) => ({
4682
4610
  name: tool.name,
@@ -4694,8 +4622,7 @@ async function openArchitect(opts) {
4694
4622
  tools: [...platformTools, ...buildRepoTools(repo)],
4695
4623
  sessionId: newSessionId(),
4696
4624
  history: [],
4697
- repo,
4698
- fatalAuth
4625
+ repo
4699
4626
  };
4700
4627
  }
4701
4628
  async function architectTurn(opts) {
@@ -4761,7 +4688,7 @@ async function architectTurn(opts) {
4761
4688
  exit_code: 1,
4762
4689
  summary: message.slice(0, 500)
4763
4690
  }).eq("id", runRow.id);
4764
- if (error instanceof ChatGptAuthError && session.fatalAuth) fail(message);
4691
+ if (error instanceof ChatGptAuthError) fail(message);
4765
4692
  throw error;
4766
4693
  }
4767
4694
  }
@@ -5256,31 +5183,6 @@ var init_parse = __esm({
5256
5183
  }
5257
5184
  });
5258
5185
 
5259
- // src/tui/workspace-load.ts
5260
- var WorkspaceLoads;
5261
- var init_workspace_load = __esm({
5262
- "src/tui/workspace-load.ts"() {
5263
- "use strict";
5264
- WorkspaceLoads = class {
5265
- #workspaceId;
5266
- #generation = 0;
5267
- constructor(workspaceId) {
5268
- this.#workspaceId = workspaceId;
5269
- }
5270
- start(workspaceId) {
5271
- return { workspaceId, generation: this.#generation };
5272
- }
5273
- switchTo(workspaceId) {
5274
- this.#workspaceId = workspaceId;
5275
- this.#generation += 1;
5276
- }
5277
- isCurrent(token) {
5278
- return token.workspaceId === this.#workspaceId && token.generation === this.#generation;
5279
- }
5280
- };
5281
- }
5282
- });
5283
-
5284
5186
  // src/tui/App.tsx
5285
5187
  var App_exports = {};
5286
5188
  __export(App_exports, {
@@ -5316,26 +5218,22 @@ function App({
5316
5218
  const [ready, setReady] = useState3(false);
5317
5219
  const workspaceCtx = useMemo(() => ({ ...ctx, workspace: current }), [ctx, current]);
5318
5220
  const session = useRef(null);
5319
- const workspaceLoads = useRef(new WorkspaceLoads(current.id));
5320
5221
  const history = useRef([]);
5321
5222
  const historyAt = useRef(-1);
5322
5223
  const say = useCallback((speaker, body, steps) => {
5323
5224
  setMessages((prior) => [...prior, { id: nextId(), speaker, body, steps }]);
5324
5225
  }, []);
5325
5226
  const refresh = useCallback(async () => {
5326
- const loadToken = workspaceLoads.current.start(workspaceCtx.workspace.id);
5327
5227
  try {
5328
5228
  const [next, activity, paused] = await Promise.all([
5329
5229
  loadBoard(workspaceCtx),
5330
5230
  loadActivity(workspaceCtx, 20),
5331
5231
  loadGlobalPause(workspaceCtx)
5332
5232
  ]);
5333
- if (!workspaceLoads.current.isCurrent(loadToken)) return;
5334
5233
  setBoard(next);
5335
5234
  setFeed(activity);
5336
5235
  setPausedAll2(paused);
5337
5236
  } catch (error) {
5338
- if (!workspaceLoads.current.isCurrent(loadToken)) return;
5339
5237
  setNotice(error instanceof Error ? error.message : String(error));
5340
5238
  }
5341
5239
  }, [workspaceCtx]);
@@ -5345,13 +5243,6 @@ function App({
5345
5243
  const sub = subscribe({
5346
5244
  db: ctx.db,
5347
5245
  tables: ["tickets", "runs", "messages", "decisions", "agents", "hosts", "provider_pauses", "runtime"],
5348
- filter: {
5349
- tickets: `workspace_id=eq.${current.id}`,
5350
- runs: `workspace_id=eq.${current.id}`,
5351
- messages: `workspace_id=eq.${current.id}`,
5352
- decisions: `workspace_id=eq.${current.id}`,
5353
- agents: `workspace_id=eq.${current.id}`
5354
- },
5355
5246
  onChange: () => repaint.trigger(),
5356
5247
  onState: setLive
5357
5248
  });
@@ -5359,7 +5250,7 @@ function App({
5359
5250
  repaint.cancel();
5360
5251
  void sub.close();
5361
5252
  };
5362
- }, [ctx, current.id, refresh]);
5253
+ }, [ctx, refresh]);
5363
5254
  useEffect3(() => {
5364
5255
  session.current = null;
5365
5256
  }, [current.id]);
@@ -5370,13 +5261,6 @@ function App({
5370
5261
  setNotice(`No workspace ${slug}. You can reach: ${available.map((r) => r.slug).join(", ")}.`);
5371
5262
  return;
5372
5263
  }
5373
- workspaceLoads.current.switchTo(found.id);
5374
- session.current = null;
5375
- setBoard(null);
5376
- setFeed([]);
5377
- setPausedAll2(false);
5378
- setMessages([]);
5379
- setNotice(null);
5380
5264
  setCurrent(found);
5381
5265
  setView("home");
5382
5266
  setTicketKey(null);
@@ -5393,7 +5277,7 @@ function App({
5393
5277
  setMessages((prior) => [...prior, { id, speaker: "architect", body: "", steps, pending: true }]);
5394
5278
  try {
5395
5279
  if (!session.current) {
5396
- session.current = await openArchitect({ ctx: workspaceCtx, readOnly: false, fatalAuth: false });
5280
+ session.current = await openArchitect({ ctx: workspaceCtx, readOnly: false });
5397
5281
  }
5398
5282
  let streamed = "";
5399
5283
  const result = await architectTurn({
@@ -5621,7 +5505,6 @@ var init_App = __esm({
5621
5505
  init_TextInput();
5622
5506
  init_parse();
5623
5507
  init_theme2();
5624
- init_workspace_load();
5625
5508
  messageSeq = 0;
5626
5509
  nextId = () => `m${messageSeq++}`;
5627
5510
  }
@@ -5633,7 +5516,6 @@ __export(launch_exports, {
5633
5516
  launchApp: () => launchApp
5634
5517
  });
5635
5518
  async function launchApp(slug) {
5636
- if (!canLaunchApp()) return;
5637
5519
  const ctx = await requireWorkspace(slug);
5638
5520
  const [{ render }, React7, { App: App2 }] = await Promise.all([
5639
5521
  import("ink"),
@@ -5641,13 +5523,13 @@ async function launchApp(slug) {
5641
5523
  Promise.resolve().then(() => (init_App(), App_exports))
5642
5524
  ]);
5643
5525
  const { data: workspaces } = await ctx.db.from("workspaces").select("*").order("name");
5644
- const config = await loadConfig();
5526
+ const brain = await requireBrain();
5645
5527
  const instance = render(
5646
5528
  React7.createElement(App2, {
5647
5529
  ctx,
5648
5530
  workspace: ctx.workspace,
5649
5531
  workspaces: workspaces ?? [],
5650
- brainLabel: describeBrain(config.brain ?? DEFAULT_BRAIN)
5532
+ brainLabel: describeBrain(brain)
5651
5533
  }),
5652
5534
  { exitOnCtrlC: false }
5653
5535
  );
@@ -5660,9 +5542,7 @@ var init_launch = __esm({
5660
5542
  "src/tui/launch.ts"() {
5661
5543
  "use strict";
5662
5544
  init_context();
5663
- init_config();
5664
5545
  init_brain();
5665
- init_capability();
5666
5546
  }
5667
5547
  });
5668
5548
 
@@ -6175,7 +6055,6 @@ init_theme();
6175
6055
  init_json();
6176
6056
  init_supabase();
6177
6057
  init_connection();
6178
- init_capability();
6179
6058
  import { createInterface as createInterface2 } from "readline/promises";
6180
6059
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6181
6060
  var CODE_RE = /^\d{6}$/;
@@ -6215,7 +6094,7 @@ async function login(opts) {
6215
6094
  if (codex.state !== "connected") {
6216
6095
  out(`${c.dim("codex")} ${c.yellow(connectionSummary(codex))} ${c.dim(howToFix(codex) ?? "")}`);
6217
6096
  }
6218
- if (opts.launch !== false && canLaunchApp()) {
6097
+ if (opts.launch !== false && process.stdin.isTTY && process.stdout.isTTY) {
6219
6098
  const { launchApp: launchApp2 } = await Promise.resolve().then(() => (init_launch(), launch_exports));
6220
6099
  await launchApp2();
6221
6100
  }
@@ -6480,7 +6359,6 @@ init_editor();
6480
6359
  init_epics();
6481
6360
  init_tickets2();
6482
6361
  import { readFile as readFile6 } from "fs/promises";
6483
- import { randomUUID as randomUUID4 } from "crypto";
6484
6362
  function slugOf5(command) {
6485
6363
  return command.optsWithGlobals().workspace;
6486
6364
  }
@@ -6493,9 +6371,6 @@ async function findTicket(ctx, key2) {
6493
6371
  function splitKeys(value) {
6494
6372
  return (value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
6495
6373
  }
6496
- function requestId(command) {
6497
- return String(command.opts().requestId ?? randomUUID4());
6498
- }
6499
6374
  function registerWriteCommands(program) {
6500
6375
  program.command("new").argument("[title...]", "ticket title").description("file a ticket").option("-b, --body <text>", "body markdown").option("-A, --acceptance <text>", "acceptance criteria").option("--area <area>", "area tag, e.g. web/auth").option("-a, --agent <name>", "pin a builder by display name").option("-p, --priority <n>", "priority, higher runs first", "0").option("--host <id>", "run on a specific host").option("-e, --epic <id>", "attach to an epic").option("--block <keys>", "comma-separated keys this depends on").option("-t, --template <id>", "start from a workspace template").option("--edit", "open $EDITOR for the body and acceptance").action(async function(titleWords) {
6501
6376
  const opts = this.opts();
@@ -6541,7 +6416,7 @@ function registerWriteCommands(program) {
6541
6416
  });
6542
6417
  ok(`${c.bold(ticket.key)} ${ticket.title} ${c.dim(`(${ticket.status})`)}`, { ticket });
6543
6418
  });
6544
- program.command("set").argument("<key>", "ticket key").description("change a ticket").option("--title <text>", "new title").option("-b, --body <text>", "new body").option("-A, --acceptance <text>", "new acceptance criteria").option("--area <area>", "new area").option("-a, --agent <name>", "assign a builder, or 'none' to unassign").option("-p, --priority <n>", "new priority").option("--host <id>", "run on a specific host, or 'none'").option("-s, --status <status>", "move to a status").option("-e, --epic <id>", "attach to an epic, or 'none'").option("--edit", "open $EDITOR for the body and acceptance").option("--request-id <uuid>", "stable operation id for safe retry").action(async function(key2) {
6419
+ program.command("set").argument("<key>", "ticket key").description("change a ticket").option("--title <text>", "new title").option("-b, --body <text>", "new body").option("-A, --acceptance <text>", "new acceptance criteria").option("--area <area>", "new area").option("-a, --agent <name>", "assign a builder, or 'none' to unassign").option("-p, --priority <n>", "new priority").option("--host <id>", "run on a specific host, or 'none'").option("-s, --status <status>", "move to a status").option("-e, --epic <id>", "attach to an epic, or 'none'").option("--edit", "open $EDITOR for the body and acceptance").action(async function(key2) {
6545
6420
  const opts = this.opts();
6546
6421
  const ctx = await requireWorkspace(slugOf5(this));
6547
6422
  const ticket = await findTicket(ctx, key2);
@@ -6567,45 +6442,38 @@ function registerWriteCommands(program) {
6567
6442
  patch.body = parsed.body;
6568
6443
  patch.acceptance = parsed.acceptance;
6569
6444
  }
6570
- const operationId = requestId(this);
6571
- const updated = await updateTicket(ctx, ticket, patch, operationId);
6572
- ok(`${c.bold(updated.key)} updated ${c.dim(`(${updated.status})`)} ${c.dim(`request ${operationId}`)}`, { ticket: updated, request_id: operationId });
6445
+ const updated = await updateTicket(ctx, ticket, patch);
6446
+ ok(`${c.bold(updated.key)} updated ${c.dim(`(${updated.status})`)}`, { ticket: updated });
6573
6447
  });
6574
- program.command("block").argument("<key>", "ticket key").argument("<blockers...>", "keys it should wait for").description("make a ticket wait for others").option("--request-id <uuid>", "stable operation id for safe retry").action(async function(key2, blockers) {
6448
+ program.command("block").argument("<key>", "ticket key").argument("<blockers...>", "keys it should wait for").description("make a ticket wait for others").action(async function(key2, blockers) {
6575
6449
  const ctx = await requireWorkspace(slugOf5(this));
6576
6450
  const ticket = await findTicket(ctx, key2);
6577
- const operationId = requestId(this);
6578
- const updated = await setBlockedBy(ctx, ticket, blockers, "add", operationId);
6579
- ok(`${c.bold(updated.key)} is ${updated.status} ${c.dim(`request ${operationId}`)}`, { ticket: updated, request_id: operationId });
6451
+ const updated = await setBlockedBy(ctx, ticket, blockers, "add");
6452
+ ok(`${c.bold(updated.key)} is ${updated.status}`, { ticket: updated });
6580
6453
  });
6581
- program.command("unblock").argument("<key>", "ticket key").argument("[blockers...]", "keys to drop; omit to clear them all").description("drop dependencies").option("--request-id <uuid>", "stable operation id for safe retry").action(async function(key2, blockers) {
6454
+ program.command("unblock").argument("<key>", "ticket key").argument("[blockers...]", "keys to drop; omit to clear them all").description("drop dependencies").action(async function(key2, blockers) {
6582
6455
  const ctx = await requireWorkspace(slugOf5(this));
6583
6456
  const ticket = await findTicket(ctx, key2);
6584
- const operationId = requestId(this);
6585
- const updated = blockers.length ? await setBlockedBy(ctx, ticket, blockers, "remove", operationId) : await setBlockedBy(ctx, ticket, [], "set", operationId);
6586
- ok(`${c.bold(updated.key)} is ${updated.status} ${c.dim(`request ${operationId}`)}`, { ticket: updated, request_id: operationId });
6457
+ const updated = blockers.length ? await setBlockedBy(ctx, ticket, blockers, "remove") : await setBlockedBy(ctx, ticket, [], "set");
6458
+ ok(`${c.bold(updated.key)} is ${updated.status}`, { ticket: updated });
6587
6459
  });
6588
- program.command("take").argument("<key>", "ticket key").description("take a ticket over so the board stops dispatching it").option("--request-id <uuid>", "stable operation id for safe retry").action(async function(key2) {
6460
+ program.command("take").argument("<key>", "ticket key").description("take a ticket over so the board stops dispatching it").action(async function(key2) {
6589
6461
  const ctx = await requireWorkspace(slugOf5(this));
6590
- const operationId = requestId(this);
6591
- const updated = await takeOver(ctx, await findTicket(ctx, key2), operationId);
6462
+ const updated = await takeOver(ctx, await findTicket(ctx, key2));
6592
6463
  ok(`${c.bold(updated.key)} is yours. Hand it back with \`hd handback ${updated.key}\`.`, {
6593
- ticket: updated,
6594
- request_id: operationId
6464
+ ticket: updated
6595
6465
  });
6596
6466
  });
6597
- program.command("handback").argument("<key>", "ticket key").description("give a ticket back to the board").option("--request-id <uuid>", "stable operation id for safe retry").action(async function(key2) {
6467
+ program.command("handback").argument("<key>", "ticket key").description("give a ticket back to the board").action(async function(key2) {
6598
6468
  const ctx = await requireWorkspace(slugOf5(this));
6599
- const operationId = requestId(this);
6600
- const updated = await handBack(ctx, await findTicket(ctx, key2), operationId);
6601
- ok(`${c.bold(updated.key)} handed back ${c.dim(`(${updated.status})`)} ${c.dim(`request ${operationId}`)}`, { ticket: updated, request_id: operationId });
6469
+ const updated = await handBack(ctx, await findTicket(ctx, key2));
6470
+ ok(`${c.bold(updated.key)} handed back ${c.dim(`(${updated.status})`)}`, { ticket: updated });
6602
6471
  });
6603
- program.command("cancel").argument("<key>", "ticket key").description("cancel a ticket").option("--request-id <uuid>", "stable operation id for safe retry").action(async function(key2) {
6472
+ program.command("cancel").argument("<key>", "ticket key").description("cancel a ticket").action(async function(key2) {
6604
6473
  const ctx = await requireWorkspace(slugOf5(this));
6605
6474
  const ticket = await findTicket(ctx, key2);
6606
- const operationId = requestId(this);
6607
- const updated = await updateTicket(ctx, ticket, { status: "cancelled" }, operationId);
6608
- ok(`${c.bold(updated.key)} cancelled ${c.dim(`request ${operationId}`)}`, { ticket: updated, request_id: operationId });
6475
+ const updated = await updateTicket(ctx, ticket, { status: "cancelled" });
6476
+ ok(`${c.bold(updated.key)} cancelled`, { ticket: updated });
6609
6477
  });
6610
6478
  program.command("kill").argument("<target>", "run id prefix, or a ticket key to kill its live run").description("stop a run").action(async function(target) {
6611
6479
  const ctx = await requireWorkspace(slugOf5(this));
@@ -6697,7 +6565,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6697
6565
  import { z as z3 } from "zod";
6698
6566
 
6699
6567
  // src/version.ts
6700
- var VERSION = "0.1.4";
6568
+ var VERSION = "0.2.0";
6701
6569
 
6702
6570
  // src/architect/mcp.ts
6703
6571
  init_tools();
@@ -6953,12 +6821,11 @@ init_queries();
6953
6821
  init_commands2();
6954
6822
  init_views();
6955
6823
  init_theme();
6956
- init_capability();
6957
6824
  function registerDashboard(program) {
6958
6825
  program.action(async function() {
6959
6826
  const slug = this.optsWithGlobals().workspace;
6960
6827
  const ctx = await requireWorkspace(slug);
6961
- if (!canLaunchApp()) {
6828
+ if (isJsonMode() || !process.stdout.isTTY) {
6962
6829
  const [board, pausedAll] = await Promise.all([loadBoard(ctx), loadGlobalPause(ctx)]);
6963
6830
  emit(
6964
6831
  { workspace: ctx.workspace.slug, ...statusPayload(board, pausedAll) },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@higherdev/cli",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "HigherDEV in your terminal. Full control of the board, live.",
6
6
  "bin": {