@higherdev/cli 0.21.0 → 0.22.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/api.js +3 -0
- package/dist/index.js +11 -2
- package/dist/out.js +1 -1
- package/dist/tui/App.js +15 -1
- package/dist/tui/Help.js +1 -0
- package/dist/tui/chat-view.js +18 -0
- package/dist/tui/data.js +4 -1
- package/dist/tui/parse.js +3 -0
- package/dist/tui/ticket-view.js +1 -1
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -58,6 +58,9 @@ export async function queueTicket(key, config = loadConfig()) {
|
|
|
58
58
|
export async function cancelTicket(key, config = loadConfig()) {
|
|
59
59
|
return request(config, "POST", `/api/w/${config.slug}/tickets/${encodeURIComponent(key)}/cancel`);
|
|
60
60
|
}
|
|
61
|
+
export async function mergeTicket(key, reason, config = loadConfig()) {
|
|
62
|
+
return request(config, "POST", `/api/w/${config.slug}/tickets/${encodeURIComponent(key)}/merge`, { reason: reason ?? "", by: "owner" }, true);
|
|
63
|
+
}
|
|
61
64
|
export async function listTicketRunEvents(key, afterAt, afterId, config = loadConfig()) {
|
|
62
65
|
const query = new URLSearchParams();
|
|
63
66
|
if (afterAt)
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { approveEpic, answerDecision, cancelTicket, createEpic, deleteAgent, deleteEpic, getStatus, getRoadmap, getWorkspace, listAgents, listEpics, listHostEnv, listMessages, listWorkspaceEnv, listTicketRunEvents, listTickets, listWorkspaces, postMessage, queueTicket, removeWorkspaceEnv, removeHostEnv, setPaused, setWorkspaceEnv, setHostEnv, showTicket, updateAgent, updateEpic, updateCaps, } from "./api.js";
|
|
4
|
+
import { approveEpic, answerDecision, cancelTicket, mergeTicket, createEpic, deleteAgent, deleteEpic, getStatus, getRoadmap, getWorkspace, listAgents, listEpics, listHostEnv, listMessages, listWorkspaceEnv, listTicketRunEvents, listTickets, listWorkspaces, postMessage, queueTicket, removeWorkspaceEnv, removeHostEnv, setPaused, setWorkspaceEnv, setHostEnv, showTicket, updateAgent, updateEpic, updateCaps, } from "./api.js";
|
|
5
5
|
import { formatDrainStatus, hostRoll, initHost, parseHostFlags, parseHostRollFlags } from "./host.js";
|
|
6
6
|
import { login, parseLoginFlags } from "./login.js";
|
|
7
7
|
import { loadConfig } from "./config.js";
|
|
@@ -139,7 +139,16 @@ async function cmdTicket(argv, deps = {}) {
|
|
|
139
139
|
console.log(`${c.bold(ticket.key)} ${statusChip(ticket.status)}`);
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
|
-
|
|
142
|
+
if (action === "merge") {
|
|
143
|
+
const parsed = flags(rest);
|
|
144
|
+
const key = parsed.rest[0];
|
|
145
|
+
if (!key || parsed.rest.length !== 1)
|
|
146
|
+
fail("usage: hd ticket merge KEY [--reason TEXT]");
|
|
147
|
+
const { ticket } = await mergeTicket(key.toUpperCase(), parsed.opts.reason);
|
|
148
|
+
console.log(`${c.bold(ticket.key)} ${statusChip(ticket.status)}`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
fail("usage: hd ticket list | show KEY | new --title TITLE | queue KEY | cancel KEY | merge KEY");
|
|
143
152
|
}
|
|
144
153
|
async function cmdEpic(argv) {
|
|
145
154
|
const [action, ...rest] = argv;
|
package/dist/out.js
CHANGED
|
@@ -64,7 +64,7 @@ export function usage() {
|
|
|
64
64
|
return [
|
|
65
65
|
c.bold("Usage"),
|
|
66
66
|
` ${c.blue("hd status")} workspace overview`,
|
|
67
|
-
` ${c.blue("hd ticket list | show KEY [--json] | new [PATH] | queue | cancel")} ticket operations`,
|
|
67
|
+
` ${c.blue("hd ticket list | show KEY [--json] | new [PATH] | queue | cancel | merge")} ticket operations`,
|
|
68
68
|
` ${c.blue("hd epic new PATH | list | approve | rm")} epic operations`,
|
|
69
69
|
` ${c.blue("hd roadmap [--json]")} ordered workspace roadmap`,
|
|
70
70
|
` ${c.blue("hd plan")} use /architect in the TUI`,
|
package/dist/tui/App.js
CHANGED
|
@@ -24,7 +24,7 @@ import { alertOnce } from "./alert.js";
|
|
|
24
24
|
import { bubbleRows } from "./height.js";
|
|
25
25
|
import { planLayout, splitPanels } from "./layout.js";
|
|
26
26
|
import { parseLine } from "./parse.js";
|
|
27
|
-
import { configuredSlugs, acknowledgeInbox, approveEpic, cancelTicket, createEpicFromFile, decisionOptions, deleteAgent, deleteEpic, followChat, loadChatMessages, loadLiveEvents, listWorkspaceEnv, loadTicketDetail, POLL_MS, pollSnapshot, postAgentMessage, postTicketMessage, queueTicket, resolveDecision, selectDecision, setWorkspacePaused, switchWorkspace, updateAgent, updateProviderCap, updateWorkspace, } from "./data.js";
|
|
27
|
+
import { configuredSlugs, acknowledgeInbox, approveEpic, cancelTicket, mergeTicket, createEpicFromFile, decisionOptions, deleteAgent, deleteEpic, followChat, loadChatMessages, loadLiveEvents, listWorkspaceEnv, loadTicketDetail, POLL_MS, pollSnapshot, postAgentMessage, postTicketMessage, queueTicket, resolveDecision, selectDecision, setWorkspacePaused, switchWorkspace, updateAgent, updateProviderCap, updateWorkspace, } from "./data.js";
|
|
28
28
|
import { inputActive, NO_REPLY_NOTE, promptPlaceholder, REPLY_WAIT_MS } from "./chat-wait.js";
|
|
29
29
|
import { answeredLine, decisionHeaderIndex, decisionIdAt, moveDecisionFocus, nextUnanswered, resolveDecisionAnswer, } from "./decide-nav.js";
|
|
30
30
|
import { EARLIER_PAGE } from "./inbox.js";
|
|
@@ -693,6 +693,20 @@ export function App({ initial }) {
|
|
|
693
693
|
setBusy(false);
|
|
694
694
|
}
|
|
695
695
|
return;
|
|
696
|
+
case "merge":
|
|
697
|
+
setBusy(true);
|
|
698
|
+
try {
|
|
699
|
+
const { ticket: merged } = await mergeTicket(config, action.key);
|
|
700
|
+
say("system", `${merged.key} approved over review.`);
|
|
701
|
+
await refresh();
|
|
702
|
+
}
|
|
703
|
+
catch (error) {
|
|
704
|
+
setNotice(error instanceof Error ? error.message : String(error));
|
|
705
|
+
}
|
|
706
|
+
finally {
|
|
707
|
+
setBusy(false);
|
|
708
|
+
}
|
|
709
|
+
return;
|
|
696
710
|
case "agent-add":
|
|
697
711
|
setBusy(true);
|
|
698
712
|
try {
|
package/dist/tui/Help.js
CHANGED
|
@@ -11,6 +11,7 @@ export const COMMANDS = [
|
|
|
11
11
|
{ name: "/ticket", args: "HD-12 | new [PATH.md]", help: "open a full ticket view or create one" },
|
|
12
12
|
{ name: "/queue", args: "HD-12", help: "queue a complete ticket now" },
|
|
13
13
|
{ name: "/cancel", args: "HD-12", help: "cancel a ticket" },
|
|
14
|
+
{ name: "/merge", args: "HD-12", help: "approve a reviewed PR over the reviewer's objections" },
|
|
14
15
|
{ name: "/msg", args: "HD-12 TEXT", help: "message a ticket's builder" },
|
|
15
16
|
{ name: "/logs", args: "[raw] [HD-12]", help: "filter activity; raw reveals event JSON" },
|
|
16
17
|
{ name: "/epic", args: "new PATH | approve ID | rm ID", help: "create, approve, or remove a draft epic" },
|
package/dist/tui/chat-view.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { narrateEvents } from "./narrate.js";
|
|
2
2
|
import { wrapLines } from "./ticket-view.js";
|
|
3
|
+
export function chatInstructionEffects(body) {
|
|
4
|
+
const parts = [];
|
|
5
|
+
const answered = body.match(/Answered decision[^\n.;]*/i);
|
|
6
|
+
const queued = body.match(/HD-\d+ is queued/i);
|
|
7
|
+
const cap = body.match(/attempt cap is \d+/i);
|
|
8
|
+
if (answered)
|
|
9
|
+
parts.push(answered[0].trim().replace(/[.;]+$/, ""));
|
|
10
|
+
if (queued)
|
|
11
|
+
parts.push(queued[0].trim().replace(/[.;]+$/, ""));
|
|
12
|
+
if (cap)
|
|
13
|
+
parts.push(cap[0].trim().replace(/[.;]+$/, ""));
|
|
14
|
+
return parts.length ? parts.join("; ") : null;
|
|
15
|
+
}
|
|
3
16
|
/** Last 50 exchanges (a human turn and an agent turn) when rehydrating. */
|
|
4
17
|
export const CHAT_EXCHANGE_LIMIT = 50;
|
|
5
18
|
export const CHAT_MESSAGE_LIMIT = CHAT_EXCHANGE_LIMIT * 2;
|
|
@@ -96,6 +109,11 @@ export function chatViewLines(thread, width, label, now = Date.now()) {
|
|
|
96
109
|
lines.push({ key: `${turn.id}:body:${index}`, kind: "body", text });
|
|
97
110
|
});
|
|
98
111
|
}
|
|
112
|
+
if (turn.speaker !== "you") {
|
|
113
|
+
const effects = chatInstructionEffects(turn.body);
|
|
114
|
+
if (effects)
|
|
115
|
+
lines.push({ key: `${turn.id}:effects`, kind: "status", text: effects });
|
|
116
|
+
}
|
|
99
117
|
}
|
|
100
118
|
if (thread.pending) {
|
|
101
119
|
const live = pendingActivity([], { started_at: thread.pending.startedAt, status: thread.pending.status }, label, now);
|
package/dist/tui/data.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { approveEpic as approveEpicNow, answerDecision, cancelTicket as cancelTicketNow, createAgent as postAgent, createEpic as postEpic, deleteEpic as removeEpic, getStatus, getRoadmap, getRun, getWorkspace, listAgents, listFeed, listMessages, markMessagesDelivered, listWorkspaceEnv as getWorkspaceEnv, listTicketRunEvents, listTickets, listWorkspaces, postMessage as sendMessage, queueTicket as queueTicketNow, setPaused as setPausedNow, showTicket, updateAgent as patchAgent, updateCaps, updateWorkspace as patchWorkspace, deleteAgent as removeAgent, } from "../api.js";
|
|
1
|
+
import { approveEpic as approveEpicNow, answerDecision, cancelTicket as cancelTicketNow, createAgent as postAgent, createEpic as postEpic, deleteEpic as removeEpic, getStatus, getRoadmap, getRun, getWorkspace, listAgents, listFeed, listMessages, markMessagesDelivered, listWorkspaceEnv as getWorkspaceEnv, listTicketRunEvents, listTickets, listWorkspaces, postMessage as sendMessage, mergeTicket as mergeTicketNow, queueTicket as queueTicketNow, setPaused as setPausedNow, showTicket, updateAgent as patchAgent, updateCaps, updateWorkspace as patchWorkspace, deleteAgent as removeAgent, } from "../api.js";
|
|
2
2
|
import { loadConfig, switchWorkspace as selectWorkspace, } from "../config.js";
|
|
3
3
|
import { readEpicSpec } from "../epics.js";
|
|
4
4
|
import { EARLIER_PAGE } from "./inbox.js";
|
|
@@ -188,6 +188,9 @@ export async function queueTicket(config, key) {
|
|
|
188
188
|
export async function cancelTicket(config, key) {
|
|
189
189
|
return cancelTicketNow(key, config);
|
|
190
190
|
}
|
|
191
|
+
export async function mergeTicket(config, key, reason) {
|
|
192
|
+
return mergeTicketNow(key, reason, config);
|
|
193
|
+
}
|
|
191
194
|
export async function setWorkspacePaused(config, paused) {
|
|
192
195
|
return setPausedNow(paused, config);
|
|
193
196
|
}
|
package/dist/tui/parse.js
CHANGED
|
@@ -80,6 +80,9 @@ export function parseLine(raw) {
|
|
|
80
80
|
case "cancel":
|
|
81
81
|
return argument ? { kind: "cancel", key: argument.toUpperCase() }
|
|
82
82
|
: { kind: "unknown", command: "cancel needs a key" };
|
|
83
|
+
case "merge":
|
|
84
|
+
return argument ? { kind: "merge", key: argument.toUpperCase() }
|
|
85
|
+
: { kind: "unknown", command: "merge needs a key" };
|
|
83
86
|
case "decide": {
|
|
84
87
|
const dismiss = /(^|\s)--(skip|dismiss)(\s|$)/.test(argument);
|
|
85
88
|
const words = argument.replace(/(^|\s)--(skip|dismiss)(\s|$)/g, " ").trim().split(/\s+/).filter(Boolean);
|
package/dist/tui/ticket-view.js
CHANGED
|
@@ -130,7 +130,7 @@ export function ticketViewLines(ticket, width, collapsed = []) {
|
|
|
130
130
|
pushWrapped(lines, "timeline", " ", event.headline, width);
|
|
131
131
|
lines.push({
|
|
132
132
|
key: `${event.id}:meta`, kind: "line", section: "timeline",
|
|
133
|
-
text: ` ${event.outcome} · ${event.kind}/${event.provider} · ${elapsedLabel(event.elapsed_ms)}`,
|
|
133
|
+
text: ` ${event.outcome} · ${event.kind}/${event.provider ?? "owner"} · ${elapsedLabel(event.elapsed_ms)}`,
|
|
134
134
|
});
|
|
135
135
|
if (event.send_back_reason) {
|
|
136
136
|
pushWrapped(lines, "timeline", " sent back because: ", event.send_back_reason, width);
|