@higherdev/cli 0.14.2 → 0.14.4
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/README.md +1 -0
- package/dist/index.js +6 -1
- package/dist/out.js +1 -1
- package/dist/tui/App.js +21 -24
- package/dist/tui/chat-wait.js +18 -0
- package/dist/workspace-commands.js +14 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ workspace-map config shapes are migrated automatically when they are read.
|
|
|
35
35
|
| `hd epic rm ID` | Remove a draft epic |
|
|
36
36
|
| `hd plan` | Point to the TUI `/architect` conversation |
|
|
37
37
|
| `hd workspace ls` | List every workspace available to the configured key |
|
|
38
|
+
| `hd workspace use SLUG` | Switch the current workspace |
|
|
38
39
|
| `hd workspace new --name NAME --repo OWNER/NAME [options]` | Preflight GitHub, wire the runner, and create a paused workspace |
|
|
39
40
|
| `hd workspace set [options]` | Update settings; max turns uses `--max-turns KIND=N[,KIND=N...]` |
|
|
40
41
|
| `hd workspace rotate-key` | Rotate the shared API key and save it locally |
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { login, parseLoginFlags } from "./login.js";
|
|
|
7
7
|
import { loadConfig } from "./config.js";
|
|
8
8
|
import { epicProgressRows, readEpicSpec } from "./epics.js";
|
|
9
9
|
import { banner, c, statusChip, table, truncate, usage } from "./out.js";
|
|
10
|
-
import { WORKSPACE_USAGE, workspaceNew, workspaceRotateKey, workspaceSet } from "./workspace-commands.js";
|
|
10
|
+
import { WORKSPACE_USAGE, workspaceNew, workspaceRotateKey, workspaceSet, workspaceUse } from "./workspace-commands.js";
|
|
11
11
|
function fail(message) {
|
|
12
12
|
console.error(message);
|
|
13
13
|
process.exit(1);
|
|
@@ -252,6 +252,11 @@ async function cmdWorkspace(argv, deps = {}) {
|
|
|
252
252
|
])));
|
|
253
253
|
return;
|
|
254
254
|
}
|
|
255
|
+
if (action === "use") {
|
|
256
|
+
const workspace = await workspaceUse(rest);
|
|
257
|
+
console.log(`${workspace.slug} ${workspace.name} ${workspace.repo}`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
255
260
|
if (action === "rotate-key") {
|
|
256
261
|
if (rest.length)
|
|
257
262
|
fail(WORKSPACE_USAGE);
|
package/dist/out.js
CHANGED
|
@@ -67,7 +67,7 @@ export function usage() {
|
|
|
67
67
|
` ${c.blue("hd ticket list | show | new | queue | cancel")} ticket operations`,
|
|
68
68
|
` ${c.blue("hd epic new PATH | list | approve | rm")} epic operations`,
|
|
69
69
|
` ${c.blue("hd plan")} use /architect in the TUI`,
|
|
70
|
-
` ${c.blue("hd workspace ls | new | set | rotate-key")} workspace operations`,
|
|
70
|
+
` ${c.blue("hd workspace ls | use | new | set | rotate-key")} workspace operations`,
|
|
71
71
|
` ${c.blue("hd agents [add | rm | set]")} manage agents`,
|
|
72
72
|
` ${c.blue("hd caps [set PROVIDER N]")} provider concurrency`,
|
|
73
73
|
` ${c.blue("hd env ls | set | rm")} workspace environment`,
|
package/dist/tui/App.js
CHANGED
|
@@ -18,7 +18,8 @@ import { alertOnce } from "./alert.js";
|
|
|
18
18
|
import { bubbleRows } from "./height.js";
|
|
19
19
|
import { planLayout, splitPanels } from "./layout.js";
|
|
20
20
|
import { parseLine } from "./parse.js";
|
|
21
|
-
import { configuredSlugs, acknowledgeInbox, approveEpic, cancelTicket, createAgent, createEpicFromFile, decisionOptions, deleteAgent, loadLiveEvents, listWorkspaceEnv, loadTicketDetail, pollSnapshot, postAgentMessage, queueTicket, resolveDecision, setWorkspacePaused, switchWorkspace, updateAgent, updateProviderCap, updateWorkspace, } from "./data.js";
|
|
21
|
+
import { configuredSlugs, acknowledgeInbox, approveEpic, cancelTicket, createAgent, createEpicFromFile, decisionOptions, deleteAgent, loadLiveEvents, listWorkspaceEnv, loadTicketDetail, pollSnapshot, postAgentMessage, queueTicket, resolveDecision, setWorkspacePaused, switchWorkspace, updateAgent, updateProviderCap, updateWorkspace, waitForReply, } from "./data.js";
|
|
22
|
+
import { inputActive, promptPlaceholder, QUEUED_STEP, REPLY_WAIT_MS, settleChatReply } from "./chat-wait.js";
|
|
22
23
|
import { editFor, editableKeys, nextValue, seedFor, settingsRows } from "./settings-model.js";
|
|
23
24
|
import { appendLines, runLabels, toStreamLines } from "./stream.js";
|
|
24
25
|
import { UI } from "./theme.js";
|
|
@@ -215,30 +216,26 @@ export function App({ initial }) {
|
|
|
215
216
|
setBusy(false);
|
|
216
217
|
}
|
|
217
218
|
}, [config, say]);
|
|
218
|
-
const askAgent = useCallback(
|
|
219
|
-
setBusy(true);
|
|
219
|
+
const askAgent = useCallback((role, text) => {
|
|
220
220
|
const id = nextId();
|
|
221
221
|
setMessages((prior) => [...prior, { id, speaker: role, body: "", pending: true }]);
|
|
222
222
|
const since = new Date().toISOString();
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
setMessages((prior) => prior.map((message) => message.id === id ? { ...message, done: true } : message));
|
|
240
|
-
setBusy(false);
|
|
241
|
-
}
|
|
223
|
+
void (async () => {
|
|
224
|
+
try {
|
|
225
|
+
await postAgentMessage(role, text, config);
|
|
226
|
+
setMessages((prior) => prior.map((message) => message.id === id
|
|
227
|
+
? { ...message, steps: [QUEUED_STEP] }
|
|
228
|
+
: message));
|
|
229
|
+
const reply = await waitForReply(config, role, since, REPLY_WAIT_MS);
|
|
230
|
+
setMessages((prior) => prior.map((message) => message.id === id
|
|
231
|
+
? { ...message, ...settleChatReply(reply) }
|
|
232
|
+
: message));
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
const body = error instanceof Error ? error.message : String(error);
|
|
236
|
+
setMessages((prior) => prior.map((message) => message.id === id ? { ...message, body, pending: false, done: true } : message));
|
|
237
|
+
}
|
|
238
|
+
})();
|
|
242
239
|
}, [config]);
|
|
243
240
|
const run = useCallback(async (raw) => {
|
|
244
241
|
const text = raw.trim();
|
|
@@ -281,7 +278,7 @@ export function App({ initial }) {
|
|
|
281
278
|
if (action.kind === "say") {
|
|
282
279
|
say("you", text);
|
|
283
280
|
if (mode !== "browse")
|
|
284
|
-
|
|
281
|
+
askAgent(mode, text);
|
|
285
282
|
else
|
|
286
283
|
setNotice("Use /architect or /orchestrator before sending a message.");
|
|
287
284
|
return;
|
|
@@ -589,7 +586,7 @@ export function App({ initial }) {
|
|
|
589
586
|
setDraft(next);
|
|
590
587
|
if (editingRef.current)
|
|
591
588
|
setEditing({ key: editingRef.current.key, draft: next });
|
|
592
|
-
}, onSubmit: (value) => void run(value), isActive:
|
|
589
|
+
}, onSubmit: (value) => void run(value), isActive: inputActive({ busy, pendingChats: inFlight.length }), placeholder: promptPlaceholder({ busy, pendingChats: inFlight.length }), prompt: _jsx(Text, { color: mode === "browse" ? UI.dim : UI.cream, children: mode === "browse" ? "> " : `${mode}> ` }), color: UI.text, onCancel: () => {
|
|
593
590
|
if (editing) {
|
|
594
591
|
setEditing(null);
|
|
595
592
|
setDraft("");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** How long the TUI waits for an architect or orchestrator reply. */
|
|
2
|
+
export const REPLY_WAIT_MS = 180_000;
|
|
3
|
+
/** Shown in the pending bubble when the wait expires. The reply can still land in /inbox. */
|
|
4
|
+
export const NO_REPLY_NOTE = "No reply yet. It will land in /inbox.";
|
|
5
|
+
export const QUEUED_STEP = "· queued, it answers on the next tick";
|
|
6
|
+
/**
|
|
7
|
+
* The prompt stays live while a chat reply is pending so navigation, /decide,
|
|
8
|
+
* and a follow-up send still work. `busy` is the only lock.
|
|
9
|
+
*/
|
|
10
|
+
export function inputActive(state) {
|
|
11
|
+
return !state.busy;
|
|
12
|
+
}
|
|
13
|
+
export function promptPlaceholder(state) {
|
|
14
|
+
return state.busy ? "working…" : "message, or /help";
|
|
15
|
+
}
|
|
16
|
+
export function settleChatReply(reply) {
|
|
17
|
+
return { body: reply ?? NO_REPLY_NOTE, pending: false, steps: [], done: true };
|
|
18
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createWorkspace, rotateWorkspaceApiKey, updateWorkspace } from "./api.js";
|
|
2
|
-
import { loadConfig, writeHdConfig } from "./config.js";
|
|
1
|
+
import { createWorkspace, listWorkspaces, rotateWorkspaceApiKey, updateWorkspace } from "./api.js";
|
|
2
|
+
import { loadConfig, switchWorkspace, writeHdConfig } from "./config.js";
|
|
3
3
|
import { promptOnStdin } from "./prompt.js";
|
|
4
4
|
import { defaultGh, HDX_RUNNER_GH_USER, preflightWorkspace } from "./workspace-preflight.js";
|
|
5
|
-
export const WORKSPACE_USAGE = "usage: hd workspace ls | new --name NAME --repo OWNER/NAME [--create|--no-create] [flags] | set [flags] | rotate-key";
|
|
5
|
+
export const WORKSPACE_USAGE = "usage: hd workspace ls | use SLUG | new --name NAME --repo OWNER/NAME [--create|--no-create] [flags] | set [flags] | rotate-key";
|
|
6
6
|
function flags(argv) {
|
|
7
7
|
const opts = {};
|
|
8
8
|
const bools = new Set();
|
|
@@ -154,6 +154,17 @@ export async function workspaceSet(argv) {
|
|
|
154
154
|
throw new Error(WORKSPACE_USAGE);
|
|
155
155
|
return (await updateWorkspace(fields)).workspace;
|
|
156
156
|
}
|
|
157
|
+
export async function workspaceUse(argv) {
|
|
158
|
+
if (argv.length !== 1 || !argv[0])
|
|
159
|
+
throw new Error(WORKSPACE_USAGE);
|
|
160
|
+
const workspaces = await listWorkspaces();
|
|
161
|
+
const selected = workspaces.find((workspace) => workspace.slug === argv[0]);
|
|
162
|
+
if (!selected) {
|
|
163
|
+
throw new Error(`No workspace ${argv[0]}. You can reach: ${workspaces.map((workspace) => workspace.slug).sort().join(", ")}.`);
|
|
164
|
+
}
|
|
165
|
+
switchWorkspace(selected.slug);
|
|
166
|
+
return selected;
|
|
167
|
+
}
|
|
157
168
|
export async function workspaceRotateKey() {
|
|
158
169
|
const config = loadConfig();
|
|
159
170
|
const { api_key } = await rotateWorkspaceApiKey(config);
|