@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.
- package/dist/index.js +84 -217
- 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
|
|
1090
|
+
const requestId = ticketId ?? globalThis.crypto.randomUUID();
|
|
1124
1091
|
const args = {
|
|
1125
|
-
p_ticket_id:
|
|
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 ?? (
|
|
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
|
-
|
|
2007
|
-
${howToFix(connection) ?? ""}`.trim()
|
|
1962
|
+
fail(`${connectionSummary(connection)}
|
|
1963
|
+
${howToFix(connection) ?? ""}`.trim());
|
|
2008
1964
|
}
|
|
2009
1965
|
if (connection.state === "not_installed") {
|
|
2010
|
-
|
|
2011
|
-
${howToFix(connection) ?? ""}`.trim()
|
|
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
|
-
|
|
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
|
-
|
|
2023
|
-
${howToFix(connected) ?? ""}`.trim()
|
|
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
|
|
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.
|
|
3553
|
-
if (patch.acceptance !== void 0) fields.
|
|
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.
|
|
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.
|
|
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
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
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
|
|
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
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
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
|
|
3544
|
+
async function takeOver(ctx, ticket) {
|
|
3606
3545
|
if (ticket.human_owner) fail(`${ticket.key} is already owned by ${ticket.human_owner}.`);
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
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
|
|
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
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
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
|
-
}
|
|
4385
|
-
return { key: updated.key, status: updated.status
|
|
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
|
|
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
|
|
4424
|
+
schema: z2.object({ key }),
|
|
4494
4425
|
async run(ctx, args) {
|
|
4495
|
-
const
|
|
4496
|
-
|
|
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
|
|
4434
|
+
schema: z2.object({ key }),
|
|
4505
4435
|
async run(ctx, args) {
|
|
4506
|
-
const
|
|
4507
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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(
|
|
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 &&
|
|
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").
|
|
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
|
|
6571
|
-
|
|
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").
|
|
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
|
|
6578
|
-
|
|
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").
|
|
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
|
|
6585
|
-
|
|
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").
|
|
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
|
|
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").
|
|
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
|
|
6600
|
-
|
|
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").
|
|
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
|
|
6607
|
-
|
|
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.
|
|
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 (
|
|
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) },
|