@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 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(async (role, text) => {
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
- try {
224
- await postAgentMessage(role, text, config);
225
- setMessages((prior) => prior.map((message) => message.id === id
226
- ? { ...message, steps: ["· queued, it answers on the next tick"] }
227
- : message));
228
- const { waitForReply } = await import("./data.js");
229
- const reply = await waitForReply(config, role, since, 180_000);
230
- setMessages((prior) => prior.map((message) => message.id === id
231
- ? { ...message, body: reply ?? "No reply yet. It will land in /inbox.", pending: false, steps: [] }
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 } : message));
237
- }
238
- finally {
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
- await askAgent(mode, text);
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: !busy, placeholder: busy ? "working…" : "message, or /help", prompt: _jsx(Text, { color: mode === "browse" ? UI.dim : UI.cream, children: mode === "browse" ? "> " : `${mode}> ` }), color: UI.text, onCancel: () => {
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@higherdev/cli",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "hd": "dist/index.js"