umbrella-context 0.1.2 → 0.1.32

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 (68) hide show
  1. package/bin/um.js +2 -0
  2. package/dist/adapters/byterover-context-runtime-store.d.ts +15 -0
  3. package/dist/adapters/byterover-context-runtime-store.js +57 -0
  4. package/dist/adapters/byterover-runtime-bridge.d.ts +218 -0
  5. package/dist/adapters/byterover-runtime-bridge.js +343 -0
  6. package/dist/adapters/byterover-transport-task-store.d.ts +13 -0
  7. package/dist/adapters/byterover-transport-task-store.js +50 -0
  8. package/dist/adapters/umbrella-onboarding.d.ts +27 -0
  9. package/dist/adapters/umbrella-onboarding.js +79 -0
  10. package/dist/adapters/umbrella-provider-runtime.d.ts +38 -0
  11. package/dist/adapters/umbrella-provider-runtime.js +199 -0
  12. package/dist/adapters/vendor-byterover.d.ts +4 -0
  13. package/dist/adapters/vendor-byterover.js +19 -0
  14. package/dist/commands/activity.d.ts +2 -0
  15. package/dist/commands/activity.js +82 -0
  16. package/dist/commands/bridge.d.ts +2 -0
  17. package/dist/commands/bridge.js +40 -0
  18. package/dist/commands/catalog.d.ts +34 -0
  19. package/dist/commands/catalog.js +234 -0
  20. package/dist/commands/connect.js +14 -14
  21. package/dist/commands/connectors.d.ts +24 -0
  22. package/dist/commands/connectors.js +626 -0
  23. package/dist/commands/curate.d.ts +1 -0
  24. package/dist/commands/curate.js +48 -3
  25. package/dist/commands/debug.d.ts +2 -0
  26. package/dist/commands/debug.js +55 -0
  27. package/dist/commands/fix.js +54 -0
  28. package/dist/commands/hub.d.ts +22 -0
  29. package/dist/commands/hub.js +487 -0
  30. package/dist/commands/interactive.d.ts +2 -0
  31. package/dist/commands/interactive.js +970 -62
  32. package/dist/commands/locations.d.ts +1 -0
  33. package/dist/commands/locations.js +15 -12
  34. package/dist/commands/logout.d.ts +2 -0
  35. package/dist/commands/logout.js +34 -0
  36. package/dist/commands/model.d.ts +11 -0
  37. package/dist/commands/model.js +225 -0
  38. package/dist/commands/providers.d.ts +17 -0
  39. package/dist/commands/providers.js +379 -0
  40. package/dist/commands/pull.js +60 -1
  41. package/dist/commands/push.js +62 -2
  42. package/dist/commands/reset.d.ts +2 -0
  43. package/dist/commands/reset.js +35 -0
  44. package/dist/commands/restart.d.ts +2 -0
  45. package/dist/commands/restart.js +21 -0
  46. package/dist/commands/search.js +65 -1
  47. package/dist/commands/session.d.ts +2 -0
  48. package/dist/commands/session.js +241 -0
  49. package/dist/commands/setup.js +58 -56
  50. package/dist/commands/space.d.ts +12 -0
  51. package/dist/commands/space.js +138 -42
  52. package/dist/commands/status.d.ts +29 -0
  53. package/dist/commands/status.js +120 -19
  54. package/dist/commands/tasks.d.ts +2 -0
  55. package/dist/commands/tasks.js +95 -0
  56. package/dist/commands/transport.d.ts +2 -0
  57. package/dist/commands/transport.js +88 -0
  58. package/dist/commands/tree.d.ts +2 -0
  59. package/dist/commands/tree.js +98 -0
  60. package/dist/commands/tui.d.ts +2 -0
  61. package/dist/commands/tui.js +1273 -0
  62. package/dist/config.d.ts +23 -0
  63. package/dist/config.js +69 -0
  64. package/dist/index.js +41 -5
  65. package/dist/repo-state.d.ts +227 -1
  66. package/dist/repo-state.js +920 -4
  67. package/dist/umbrella.js +29 -5
  68. package/package.json +11 -3
@@ -0,0 +1,1273 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Box, render, Text, useApp, useInput } from "ink";
4
+ import TextInput from "ink-text-input";
5
+ import Spinner from "ink-spinner";
6
+ import chalk from "chalk";
7
+ import { getUmbrellaContextRuntimeSummary, useUmbrellaContextRuntimeBridgeStore, hydrateUmbrellaContextRuntimeBridgeFromSnapshot, } from "../adapters/byterover-context-runtime-store.js";
8
+ import { getUmbrellaTransportTaskBridgeSummary, useUmbrellaTransportTaskBridgeStore, hydrateUmbrellaTransportTaskBridgeFromSnapshot, } from "../adapters/byterover-transport-task-store.js";
9
+ import { buildVendorRuntimeBridgeSnapshot, } from "../adapters/byterover-runtime-bridge.js";
10
+ import { addPendingMemory, completeTask, createTask, ensureRepoContext, getContextTreeState, ensureSessionState, getConnectorRuns, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, getRepoContext, getSessionState, getTasks, getTransportState, recordSessionEvent, setSessionPanel, summarizeLocalMemoryMatches, } from "../repo-state.js";
11
+ import { configManager } from "../config.js";
12
+ import { checkUmbrellaOnboardingServer, connectUmbrellaDevice, createUmbrellaOnboardingCompany, createUmbrellaOnboardingSpace, loadUmbrellaOnboardingSpaces, signInUmbrellaOnboarding, } from "../adapters/umbrella-onboarding.js";
13
+ import { buildProviderDraftValue, connectSavedProviderFromDraft, createEmptyProviderConnectDraft, UMBRELLA_PROVIDER_CHOICES, updateProviderDraftValue, } from "../adapters/umbrella-provider-runtime.js";
14
+ import { connectorsCommandAction, listConnectorTemplates } from "./connectors.js";
15
+ import { hubCommandAction, listHubRegistryEntries } from "./hub.js";
16
+ import { modelCommandAction } from "./model.js";
17
+ import { pullCommandAction } from "./pull.js";
18
+ import { pushCommandAction } from "./push.js";
19
+ import { getProviderReadinessSummary, providersCommandAction } from "./providers.js";
20
+ import { spaceCommandAction } from "./space.js";
21
+ const PANELS = [
22
+ { id: "home", title: "Home", description: "Repo status, sync, and next steps" },
23
+ { id: "tasks", title: "Tasks", description: "Live local task activity from .um" },
24
+ { id: "activity", title: "Activity", description: "Recent session and repo events" },
25
+ { id: "providers", title: "Providers", description: "Runtime provider readiness" },
26
+ { id: "model", title: "Models", description: "Current model and available options" },
27
+ { id: "spaces", title: "Spaces", description: "Company lanes like Growth or Engineering" },
28
+ { id: "hub", title: "Hub", description: "Installed bundles and reusable assets" },
29
+ { id: "connectors", title: "Connectors", description: "Repo automations and MCP wiring" },
30
+ { id: "session", title: "Session", description: "Live terminal memory for this repo" },
31
+ ];
32
+ function trimSlash(value) {
33
+ return value.trim().replace(/\/+$/, "");
34
+ }
35
+ function panelIndexFor(id) {
36
+ return Math.max(0, PANELS.findIndex((panel) => panel.id === id));
37
+ }
38
+ function panelForTaskKind(kind) {
39
+ switch (kind) {
40
+ case "provider":
41
+ return "providers";
42
+ case "model":
43
+ return "model";
44
+ case "space":
45
+ return "spaces";
46
+ case "hub":
47
+ return "hub";
48
+ case "connector":
49
+ return "connectors";
50
+ default:
51
+ return "tasks";
52
+ }
53
+ }
54
+ function panelForActivityItem(event) {
55
+ if (event.panel && PANELS.some((panel) => panel.id === event.panel)) {
56
+ return event.panel;
57
+ }
58
+ if (event.kind === "transport" && event.focus) {
59
+ return "tasks";
60
+ }
61
+ if (event.kind === "provider")
62
+ return "providers";
63
+ if (event.kind === "model")
64
+ return "model";
65
+ if (event.kind === "space")
66
+ return "spaces";
67
+ if (event.kind === "hub")
68
+ return "hub";
69
+ if (event.kind === "connector")
70
+ return "connectors";
71
+ return null;
72
+ }
73
+ function buildVendorTasksList(taskStore) {
74
+ return taskStore.recentTaskIds
75
+ .map((taskId) => taskStore.tasks[taskId] ?? null)
76
+ .filter((task) => Boolean(task));
77
+ }
78
+ function buildActivityItems(transportStore, sessionEvents) {
79
+ const transportItems = (transportStore?.events ?? []).map((event) => ({
80
+ id: event.taskId ? `${event.kind}:${event.taskId}:${event.at}` : `${event.kind}:${event.at}`,
81
+ at: new Date(event.at).toISOString(),
82
+ kind: "transport",
83
+ title: `${event.kind}: ${event.title}`,
84
+ detail: event.detail,
85
+ panel: "tasks",
86
+ focus: event.taskId,
87
+ status: event.status,
88
+ }));
89
+ const sessionItems = (sessionEvents ?? []).map((event) => ({
90
+ id: event.id,
91
+ at: event.at,
92
+ kind: event.kind,
93
+ title: event.title,
94
+ detail: event.detail,
95
+ panel: event.panel,
96
+ focus: event.focus,
97
+ status: event.status,
98
+ }));
99
+ return [...transportItems, ...sessionItems]
100
+ .sort((a, b) => new Date(b.at).getTime() - new Date(a.at).getTime())
101
+ .slice(0, 25);
102
+ }
103
+ function formatAgo(value) {
104
+ if (!value)
105
+ return "Never";
106
+ return new Date(value).toLocaleString();
107
+ }
108
+ function StatusLine({ label, value }) {
109
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "cyan", children: label.padEnd(18) }), _jsx(Text, { children: value })] }));
110
+ }
111
+ function Section({ title, children }) {
112
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: "yellow", children: title }), children] }));
113
+ }
114
+ function SpacePanelView(props) {
115
+ const { companyName, currentSpaceName, draftName, mode, onDraftChange, selectedIndex, spaces } = props;
116
+ if (mode === "create") {
117
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Create a New Space" }), _jsx(Text, { color: "gray", children: "Spaces are team lanes inside one company, like Growth, Engineering, or Marketing." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Company", value: companyName }), _jsx(StatusLine, { label: "Current space", value: currentSpaceName })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: "New space name" }), _jsx(TextInput, { value: draftName, onChange: onDraftChange })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { color: "gray", children: "Press Enter to create the new space, or Esc to go back to the list." }) })] }));
118
+ }
119
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Company Spaces" }), _jsx(Text, { color: "gray", children: "This is the live team-lane picker for the current company, similar to ByteRover's space switch flow." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Company", value: companyName }), _jsx(StatusLine, { label: "Current space", value: currentSpaceName }), _jsx(StatusLine, { label: "Available spaces", value: String(spaces.length) })] }), _jsx(Section, { title: "Select a Space", children: spaces.length === 0 ? (_jsx(Text, { color: "yellow", children: "No spaces found yet. Press n to create the first one." })) : spaces.map((space, index) => {
120
+ const highlighted = index === selectedIndex;
121
+ const active = space.name === currentSpaceName;
122
+ return (_jsxs(Text, { color: highlighted ? "yellow" : active ? "green" : "white", children: [highlighted ? ">" : " ", " ", space.name, space.isPrimary ? " (core)" : "", active ? " (active)" : ""] }, space.id));
123
+ }) }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { color: "gray", children: "Up and down move through spaces. Enter or w switches. n creates a new space. i refreshes the list." }) })] }));
124
+ }
125
+ function ProviderPanelView(props) {
126
+ const { activeModel, activeProviderId, connectDraftValue, connectKindIndex, mode, onConnectDraftChange, providers, recentProviderId, readinessState, selectedIndex, } = props;
127
+ if (mode === "connect-kind") {
128
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Connect a Provider" }), _jsx(Text, { color: "gray", children: "Pick the model company you want this device to use for Context work." }), _jsx(Section, { title: "Provider Types", children: UMBRELLA_PROVIDER_CHOICES.map((choice, index) => (_jsxs(Text, { color: index === connectKindIndex ? "yellow" : "white", children: [index === connectKindIndex ? ">" : " ", " ", choice.name] }, choice.kind))) }), _jsx(Text, { color: "gray", children: "Up and down move through provider types. Enter confirms. Esc goes back." })] }));
129
+ }
130
+ if (mode === "connect-label" || mode === "connect-key" || mode === "connect-base-url") {
131
+ const label = mode === "connect-label"
132
+ ? "Provider label on this device"
133
+ : mode === "connect-key"
134
+ ? "Provider API key"
135
+ : "Base URL for the compatible API";
136
+ const help = mode === "connect-label"
137
+ ? "Give this provider a simple name you will recognize later."
138
+ : mode === "connect-key"
139
+ ? "This key stays on this device and is used for terminal Context work."
140
+ : "This is only needed for OpenAI-compatible providers.";
141
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Connect a Provider" }), _jsx(Text, { color: "gray", children: help }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: label }), _jsx(TextInput, { value: connectDraftValue, onChange: onConnectDraftChange })] }), _jsx(Text, { color: "gray", children: "Press Enter to continue. Esc goes back one step." })] }));
142
+ }
143
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Provider Runtime" }), _jsx(Text, { color: "gray", children: "This is the device-side runtime picker, closer to ByteRover's provider flow." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Connected providers", value: String(providers.length) }), _jsx(StatusLine, { label: "Active provider", value: providers.find((entry) => entry.id === activeProviderId)?.name ?? "Not connected" }), _jsx(StatusLine, { label: "Active model", value: activeModel ?? "Not selected" }), _jsx(StatusLine, { label: "State", value: readinessState })] }), _jsx(Section, { title: "Select a Provider", children: providers.length === 0 ? (_jsx(Text, { color: "yellow", children: "No providers are connected yet. Press c to start the live connect flow." })) : providers.map((provider, index) => {
144
+ const highlighted = index === selectedIndex;
145
+ const active = provider.id === activeProviderId;
146
+ const recent = provider.id === recentProviderId;
147
+ return (_jsxs(Text, { color: highlighted ? "yellow" : active ? "green" : "white", children: [highlighted ? ">" : " ", " ", provider.name, " (", provider.kind, ")", active ? " (active)" : "", recent ? " (recent)" : ""] }, provider.id));
148
+ }) }), _jsx(Text, { color: "gray", children: "Up and down move through providers. Enter or w makes one active. c connects a new provider. d disconnects the highlighted one." }), _jsx(Text, { color: "gray", children: "t tests the runtime, i inspects the recent provider, g runs the full guided setup, and m jumps to models." })] }));
149
+ }
150
+ function ModelPanelView(props) {
151
+ const { activeModel, providerName, recentModel, selectedIndex, models } = props;
152
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Model Runtime" }), _jsx(Text, { color: "gray", children: "This is the live model picker for the active provider, closer to ByteRover's model flow." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Provider", value: providerName }), _jsx(StatusLine, { label: "Active model", value: activeModel ?? "Not selected" }), _jsx(StatusLine, { label: "Available models", value: String(models.length) })] }), _jsx(Section, { title: "Select a Model", children: models.length === 0 ? (_jsx(Text, { color: "yellow", children: "No models are available for the active provider yet." })) : models.map((model, index) => {
153
+ const highlighted = index === selectedIndex;
154
+ const active = model === activeModel;
155
+ const recent = model === recentModel;
156
+ return (_jsxs(Text, { color: highlighted ? "yellow" : active ? "green" : "white", children: [highlighted ? ">" : " ", " ", model, active ? " (active)" : "", recent ? " (recent)" : ""] }, model));
157
+ }) }), _jsx(Text, { color: "gray", children: "Up and down move through models. Enter or w makes one active. i inspects the recent model. t checks runtime readiness." })] }));
158
+ }
159
+ function HubPanelView(props) {
160
+ const { installedSlugs, recentSlug, registryEntries, selectedIndex } = props;
161
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Hub" }), _jsx(Text, { color: "gray", children: "This is the live Hub picker for reusable bundles, skills, and connector packs in this repo." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Available entries", value: String(registryEntries.length) }), _jsx(StatusLine, { label: "Installed entries", value: String(installedSlugs.length) })] }), _jsx(Section, { title: "Browse Hub Registry", children: registryEntries.map((entry, index) => {
162
+ const highlighted = index === selectedIndex;
163
+ const installed = installedSlugs.includes(entry.slug);
164
+ const recent = recentSlug === entry.slug;
165
+ return (_jsxs(Text, { color: highlighted ? "yellow" : installed ? "green" : "white", children: [highlighted ? ">" : " ", " ", entry.name, " [", entry.type, "]", installed ? " (installed)" : "", recent ? " (recent)" : ""] }, entry.slug));
166
+ }) }), _jsx(Section, { title: "Selected Entry", children: registryEntries[selectedIndex] ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: registryEntries[selectedIndex].description }), _jsxs(Text, { color: "gray", children: ["Includes: ", registryEntries[selectedIndex].includes.join(", ")] }), _jsxs(Text, { color: "gray", children: ["Next: ", registryEntries[selectedIndex].nextSteps[0] ?? "Review and install"] })] })) : (_jsx(Text, { color: "yellow", children: "No Hub entries are available right now." })) }), _jsx(Text, { color: "gray", children: "Up and down move through hub entries. Enter or n installs the selected entry. i inspects it. b opens the guided browse flow. l refreshes installed items." })] }));
167
+ }
168
+ function ConnectorsPanelView(props) {
169
+ const { installedSources, recentSource, recentRunSource, registryEntries, selectedIndex } = props;
170
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Connectors" }), _jsx(Text, { color: "gray", children: "This is the live repo connector picker for MCP wiring, hooks, rules, and skill bridges." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Available connectors", value: String(registryEntries.length) }), _jsx(StatusLine, { label: "Installed connectors", value: String(installedSources.length) })] }), _jsx(Section, { title: "Browse Connector Registry", children: registryEntries.map((entry, index) => {
171
+ const highlighted = index === selectedIndex;
172
+ const installed = installedSources.includes(entry.key);
173
+ const recent = recentSource === entry.key;
174
+ const recentRun = recentRunSource === entry.key;
175
+ return (_jsxs(Text, { color: highlighted ? "yellow" : installed ? "green" : "white", children: [highlighted ? ">" : " ", " ", entry.name, " [", entry.type, "]", installed ? " (installed)" : "", recent ? " (recent)" : "", recentRun ? " (ran)" : ""] }, entry.key));
176
+ }) }), _jsx(Section, { title: "Selected Connector", children: registryEntries[selectedIndex] ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: registryEntries[selectedIndex].description }), _jsxs(Text, { color: "gray", children: ["Best for: ", registryEntries[selectedIndex].type, " workflows in this repo."] }), _jsx(Text, { color: "gray", children: "Next: install it, then run it to apply repo changes or stage local Context output." })] })) : (_jsx(Text, { color: "yellow", children: "No connector templates are available right now." })) }), _jsx(Text, { color: "gray", children: "Up and down move through connectors. Enter or i installs the selected connector. r runs it. o opens its outputs. v inspects the recent connector." })] }));
177
+ }
178
+ function TasksPanelView(props) {
179
+ const { mode, selectedIndex, tasks, transportStatus, queueLength, activeTaskId } = props;
180
+ const selectedTask = tasks[selectedIndex] ?? null;
181
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Live Local Tasks" }), _jsx(Text, { color: "gray", children: "This is the live task queue for this repo, backed by the local .um runtime." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Transport", value: transportStatus }), _jsx(StatusLine, { label: "Queue", value: String(queueLength) }), _jsx(StatusLine, { label: "Active task", value: activeTaskId ?? "None" })] }), _jsx(Section, { title: "Browse Tasks", children: tasks.length === 0 ? (_jsx(Text, { color: "yellow", children: "No local tasks yet. Ask a question, curate a note, or run push/pull." })) : tasks.slice(0, 12).map((task, index) => {
182
+ const highlighted = index === selectedIndex;
183
+ const active = task.taskId === activeTaskId;
184
+ return (_jsxs(Text, { color: highlighted ? "yellow" : active ? "green" : "white", children: [highlighted ? ">" : " ", " [", task.type, "] ", task.content, " (", task.status, ")", active ? " (active)" : ""] }, task.taskId));
185
+ }) }), _jsx(Section, { title: "Selected Task", children: selectedTask ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: selectedTask.content ?? selectedTask.result ?? "No extra detail recorded." }), _jsxs(Text, { color: "gray", children: ["Updated: ", new Date(selectedTask.completedAt ?? selectedTask.startedAt ?? selectedTask.createdAt).toLocaleString()] }), _jsxs(Text, { color: "gray", children: ["Related panel: ", panelForTaskKind(selectedTask.type)] }), _jsxs(Text, { color: "gray", children: ["Reasoning: ", String(selectedTask.reasoningContents?.length ?? 0), " | Tool calls: ", String(selectedTask.toolCalls?.length ?? 0)] }), mode === "inspect" ? (_jsx(_Fragment, { children: _jsxs(Section, { title: "Deep Inspect", children: [_jsxs(Text, { color: "gray", children: ["Result: ", selectedTask.result ?? "No final result yet."] }), _jsxs(Text, { color: "gray", children: ["Streaming: ", selectedTask.streamingContent ?? "No streaming content captured."] }), _jsxs(Text, { color: "gray", children: ["Focus: ", selectedTask.input ?? "None", " | Session: ", selectedTask.sessionId ?? "None"] }), selectedTask.reasoningContents?.length ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: "yellow", children: "Reasoning Trail" }), selectedTask.reasoningContents.slice(0, 5).map((item, index) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [index + 1, ". ", item.content] }), _jsxs(Text, { color: "gray", children: [new Date(item.timestamp).toLocaleString(), item.isThinking ? " | thinking" : ""] })] }, `${selectedTask.taskId}-reasoning-${index}`)))] })) : null, selectedTask.toolCalls?.length ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: "yellow", children: "Tool Calls" }), selectedTask.toolCalls.slice(0, 4).map((call, index) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [index + 1, ". ", call.toolName, " (", call.status, ")"] }), _jsx(Text, { color: "gray", children: new Date(call.timestamp).toLocaleString() }), _jsxs(Text, { color: "gray", children: ["Args: ", JSON.stringify(call.args)] }), _jsxs(Text, { color: "gray", children: ["Result: ", call.result !== undefined ? JSON.stringify(call.result) : "None"] }), call.error ? _jsxs(Text, { color: "red", children: ["Error: ", call.error] }) : null] }, `${selectedTask.taskId}-tool-${index}`)))] })) : null] }) })) : null] })) : (_jsx(Text, { color: "gray", children: "Choose a task to inspect it." })) }), _jsx(Text, { color: "gray", children: mode === "inspect" ? "Esc goes back to the task list. o opens the related panel. r refreshes the runtime." : "Up and down move through tasks. Enter or i inspects the selected task. o opens the related panel. r refreshes the runtime." })] }));
186
+ }
187
+ function ActivityPanelView(props) {
188
+ const { activity, mode, selectedIndex } = props;
189
+ const selectedEvent = activity[selectedIndex] ?? null;
190
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Session and Repo Activity" }), _jsx(Text, { color: "gray", children: "This is the live event feed for the current repo and terminal session." }), _jsx(Section, { title: "Browse Activity", children: activity.length === 0 ? (_jsx(Text, { color: "yellow", children: "No activity recorded yet." })) : activity.slice(0, 12).map((event, index) => {
191
+ const highlighted = index === selectedIndex;
192
+ return (_jsxs(Text, { color: highlighted ? "yellow" : event.status === "success" ? "green" : event.status === "failure" ? "red" : event.status === "warning" ? "yellow" : "white", children: [highlighted ? ">" : " ", " [", event.kind, "] ", event.title] }, event.id));
193
+ }) }), _jsx(Section, { title: "Selected Event", children: selectedEvent ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: selectedEvent.detail ?? "No extra detail recorded." }), _jsxs(Text, { color: "gray", children: ["When: ", new Date(selectedEvent.at).toLocaleString()] }), _jsxs(Text, { color: "gray", children: ["Panel: ", panelForActivityItem(selectedEvent) ?? "None", " | Focus: ", selectedEvent.focus ?? "None"] }), mode === "inspect" ? (_jsxs(Section, { title: "Deep Inspect", children: [_jsxs(Text, { color: "gray", children: ["Kind: ", selectedEvent.kind] }), _jsxs(Text, { color: "gray", children: ["Status: ", selectedEvent.status] }), _jsxs(Text, { color: "gray", children: ["Suggested panel: ", panelForActivityItem(selectedEvent) ?? "None"] }), _jsx(Text, { color: "gray", children: "Open the related panel with Enter or o." })] })) : null] })) : (_jsx(Text, { color: "gray", children: "Choose an event to view its detail." })) }), _jsx(Text, { color: "gray", children: mode === "inspect" ? "Esc goes back to the event list. Enter or o jumps to the related panel. r refreshes the runtime." : "Up and down move through events. Enter or o jumps to the related panel when one exists. r refreshes the runtime." })] }));
194
+ }
195
+ function PanelShell({ activePanel, children, footer, message, selectedPanelIndex, }) {
196
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "UMBRELLA CONTEXT TUI" }), _jsx(Text, { color: "gray", children: " pane-based repo Context terminal" })] }), _jsxs(Box, { children: [_jsx(Box, { flexDirection: "column", width: 26, marginRight: 2, borderStyle: "round", paddingX: 1, paddingY: 1, children: PANELS.map((panel, index) => {
197
+ const active = panel.id === activePanel;
198
+ const selected = index === selectedPanelIndex;
199
+ return (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: active ? "green" : selected ? "yellow" : "white", children: [active ? "*" : selected ? ">" : " ", " ", panel.title] }) }, panel.id));
200
+ }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", paddingX: 1, paddingY: 1, children: children })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [message ? _jsx(Text, { color: "green", children: message }) : null, _jsx(Text, { color: "gray", children: footer })] })] }));
201
+ }
202
+ function SetupView(props) {
203
+ const { busyLabel, companies, config, inputValue, message, onInputChange, selectedCompanyIndex, selectedSpaceIndex, serverUrl, spaces, step, } = props;
204
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: config ? "Connected Device" : "Connect This Device" }), _jsx(Text, { color: "gray", children: "Sign into Umbrella, choose a company, then choose the team space for this repo." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Server", value: serverUrl || "Not set" }), _jsx(StatusLine, { label: "Step", value: step }), config ? _jsx(StatusLine, { label: "Current link", value: `${config.companyName} / ${config.projectName}` }) : null] }), busyLabel ? (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "cyan", children: [_jsx(Spinner, { type: "dots" }), " ", busyLabel] }) })) : null, message ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "green", children: message }) })) : null, (step === "server" || step === "email" || step === "password" || step === "company-create" || step === "space-create") ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: step === "server" ? "Server URL" :
205
+ step === "email" ? "Umbrella email" :
206
+ step === "password" ? "Umbrella password" :
207
+ step === "company-create" ? "New company name" :
208
+ "New space name" }), _jsx(TextInput, { value: inputValue, onChange: onInputChange })] })) : null, step === "company" ? (_jsxs(Section, { title: "Companies", children: [companies.map((company, index) => (_jsxs(Text, { color: index === selectedCompanyIndex ? "yellow" : "white", children: [index === selectedCompanyIndex ? ">" : " ", " ", company.name, " (", company.issuePrefix, ")"] }, company.id))), _jsxs(Text, { color: selectedCompanyIndex === companies.length ? "yellow" : "white", children: [selectedCompanyIndex === companies.length ? ">" : " ", " + Create new company"] })] })) : null, step === "space" ? (_jsxs(Section, { title: "Spaces", children: [spaces.map((space, index) => (_jsxs(Text, { color: index === selectedSpaceIndex ? "yellow" : "white", children: [index === selectedSpaceIndex ? ">" : " ", " ", space.name, space.isPrimary ? " (core)" : ""] }, space.id))), _jsxs(Text, { color: selectedSpaceIndex === spaces.length ? "yellow" : "white", children: [selectedSpaceIndex === spaces.length ? ">" : " ", " + Create new space"] })] })) : null, _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Enter confirms the current step. Up and down move through companies or spaces." }), _jsx(Text, { color: "gray", children: "Esc leaves setup. This terminal saves the final link into your device config and the repo's .um folder." })] })] }));
209
+ }
210
+ function App() {
211
+ const { exit } = useApp();
212
+ const vendorTaskStore = useUmbrellaTransportTaskBridgeStore((state) => state.taskStore);
213
+ const vendorTransportStore = useUmbrellaTransportTaskBridgeStore((state) => state.transportStore);
214
+ const vendorTreeStore = useUmbrellaContextRuntimeBridgeStore((state) => state.treeStore);
215
+ const [selectedPanelIndex, setSelectedPanelIndex] = useState(0);
216
+ const [activePanel, setActivePanel] = useState(configManager.config ? "home" : "setup");
217
+ const [refreshTick, setRefreshTick] = useState(0);
218
+ const [message, setMessage] = useState(null);
219
+ const [busyLabel, setBusyLabel] = useState(null);
220
+ const [inputMode, setInputMode] = useState(null);
221
+ const [inputValue, setInputValue] = useState("");
222
+ const [queryResult, setQueryResult] = useState(null);
223
+ const [refreshState, setRefreshState] = useState(null);
224
+ const [setupStep, setSetupStep] = useState(configManager.config ? "done" : "server");
225
+ const [serverUrl, setServerUrl] = useState(configManager.config?.umbrellaUrl ?? configManager.config?.serverUrl ?? "http://127.0.0.1:3100");
226
+ const [setupEmail, setSetupEmail] = useState("");
227
+ const [setupCookie, setSetupCookie] = useState(null);
228
+ const [companies, setCompanies] = useState([]);
229
+ const [selectedCompanyIndex, setSelectedCompanyIndex] = useState(0);
230
+ const [spaces, setSpaces] = useState([]);
231
+ const [selectedSpaceIndex, setSelectedSpaceIndex] = useState(0);
232
+ const [selectedCompany, setSelectedCompany] = useState(null);
233
+ const [spacePanelMode, setSpacePanelMode] = useState("browse");
234
+ const [spacePanelDraftName, setSpacePanelDraftName] = useState("");
235
+ const [spacePanelSelectedIndex, setSpacePanelSelectedIndex] = useState(0);
236
+ const [providerPanelMode, setProviderPanelMode] = useState("browse");
237
+ const [providerPanelSelectedIndex, setProviderPanelSelectedIndex] = useState(0);
238
+ const [providerPanelConnectKindIndex, setProviderPanelConnectKindIndex] = useState(0);
239
+ const [providerPanelConnectDraft, setProviderPanelConnectDraft] = useState(createEmptyProviderConnectDraft);
240
+ const [providerPanelRecentId, setProviderPanelRecentId] = useState(null);
241
+ const [modelPanelMode, setModelPanelMode] = useState("browse");
242
+ const [modelPanelSelectedIndex, setModelPanelSelectedIndex] = useState(0);
243
+ const [modelPanelRecentModel, setModelPanelRecentModel] = useState(null);
244
+ const [hubPanelMode, setHubPanelMode] = useState("browse");
245
+ const [hubPanelSelectedIndex, setHubPanelSelectedIndex] = useState(0);
246
+ const [connectorsPanelMode, setConnectorsPanelMode] = useState("browse");
247
+ const [connectorsPanelSelectedIndex, setConnectorsPanelSelectedIndex] = useState(0);
248
+ const [tasksPanelMode, setTasksPanelMode] = useState("browse");
249
+ const [tasksPanelSelectedIndex, setTasksPanelSelectedIndex] = useState(0);
250
+ const [activityPanelMode, setActivityPanelMode] = useState("browse");
251
+ const [activityPanelSelectedIndex, setActivityPanelSelectedIndex] = useState(0);
252
+ async function refreshAll() {
253
+ try {
254
+ if (configManager.config) {
255
+ await ensureRepoContext(configManager.config);
256
+ }
257
+ const vendorSnapshot = await buildVendorRuntimeBridgeSnapshot();
258
+ hydrateUmbrellaTransportTaskBridgeFromSnapshot(vendorSnapshot);
259
+ hydrateUmbrellaContextRuntimeBridgeFromSnapshot(vendorSnapshot);
260
+ const [transport, treeState, pending, pulled, fixes, connectors, hubEntries, connectorRuns, session, repoContext, tasks] = await Promise.all([
261
+ getTransportState(),
262
+ getContextTreeState(),
263
+ getPendingMemories(),
264
+ getPulledMemories(),
265
+ getPulledFixes(),
266
+ getInstalledConnectors(),
267
+ getInstalledHubEntries(),
268
+ getConnectorRuns(),
269
+ ensureSessionState(),
270
+ getRepoContext(),
271
+ getTasks(),
272
+ ]);
273
+ setRefreshState({
274
+ transport,
275
+ treeState,
276
+ pending,
277
+ pulled,
278
+ fixes,
279
+ connectors,
280
+ hubEntries,
281
+ connectorRuns,
282
+ session,
283
+ repoContext,
284
+ tasks,
285
+ activity: buildActivityItems(vendorSnapshot.stores.transport, session.events ?? []),
286
+ });
287
+ }
288
+ catch {
289
+ setRefreshState(null);
290
+ }
291
+ }
292
+ useEffect(() => {
293
+ void refreshAll();
294
+ }, [refreshTick]);
295
+ useEffect(() => {
296
+ void setSessionPanel(activePanel, configManager.config?.projectName ?? null);
297
+ }, [activePanel]);
298
+ useEffect(() => {
299
+ if (activePanel !== "spaces" || busyLabel)
300
+ return;
301
+ const activeIndex = Math.max(0, vendorSpaces.findIndex((space) => space.id === (vendorSpaceStore?.activeSpaceId ?? configManager.config?.projectId)));
302
+ setSpacePanelSelectedIndex(activeIndex === -1 ? 0 : activeIndex);
303
+ }, [activePanel, busyLabel, vendorSpaces, vendorSpaceStore]);
304
+ useEffect(() => {
305
+ if (activePanel !== "providers" || busyLabel)
306
+ return;
307
+ const activeIndex = Math.max(0, vendorProviders.findIndex((provider) => provider.id === (vendorProviderStore?.activeProviderId ?? configManager.config?.activeProvider)));
308
+ setProviderPanelSelectedIndex(activeIndex === -1 ? 0 : activeIndex);
309
+ void (async () => {
310
+ const session = await getSessionState();
311
+ setProviderPanelRecentId(session?.recentProviderIds?.[0] ?? null);
312
+ })();
313
+ }, [activePanel, refreshTick, busyLabel, vendorProviders, vendorProviderStore]);
314
+ useEffect(() => {
315
+ if (activePanel !== "model" || busyLabel)
316
+ return;
317
+ const activeIndex = Math.max(0, vendorModels.findIndex((model) => model === (vendorModelStore?.activeModel ?? configManager.config?.activeModel)));
318
+ setModelPanelSelectedIndex(activeIndex === -1 ? 0 : activeIndex);
319
+ void (async () => {
320
+ const session = await getSessionState();
321
+ setModelPanelRecentModel(session?.recentModels?.[0] ?? vendorModelStore?.activeModel ?? configManager.config?.activeModel ?? null);
322
+ })();
323
+ }, [activePanel, refreshTick, busyLabel, vendorModels, vendorModelStore]);
324
+ useEffect(() => {
325
+ if (activePanel !== "hub" || busyLabel)
326
+ return;
327
+ const registryEntries = listHubRegistryEntries();
328
+ const recentSlug = vendorHubStore?.recentSlug ?? null;
329
+ const recentIndex = registryEntries.findIndex((entry) => entry.slug === recentSlug);
330
+ setHubPanelSelectedIndex(recentIndex >= 0 ? recentIndex : 0);
331
+ setHubPanelMode("browse");
332
+ }, [activePanel, refreshTick, busyLabel, vendorHubStore?.recentSlug]);
333
+ useEffect(() => {
334
+ if (activePanel !== "connectors" || busyLabel)
335
+ return;
336
+ const registryEntries = listConnectorTemplates();
337
+ const recentSource = vendorConnectorsStore?.recentSource ?? null;
338
+ const recentIndex = registryEntries.findIndex((entry) => entry.key === recentSource);
339
+ setConnectorsPanelSelectedIndex(recentIndex >= 0 ? recentIndex : 0);
340
+ setConnectorsPanelMode("browse");
341
+ }, [activePanel, refreshTick, busyLabel, vendorConnectorsStore?.recentSource]);
342
+ useEffect(() => {
343
+ if (activePanel !== "tasks" || busyLabel || !refreshState)
344
+ return;
345
+ const activeTaskId = vendorTransportStore?.queue[0]?.id ??
346
+ vendorTransportStore?.queue.find((item) => item.status === "running")?.id ??
347
+ null;
348
+ const activeIndex = Math.max(0, vendorTasks.findIndex((task) => task.taskId === activeTaskId));
349
+ setTasksPanelSelectedIndex(activeIndex === -1 ? 0 : activeIndex);
350
+ setTasksPanelMode("browse");
351
+ }, [activePanel, busyLabel, refreshState, vendorTasks, vendorTransportStore]);
352
+ useEffect(() => {
353
+ if (activePanel !== "activity" || busyLabel || !refreshState)
354
+ return;
355
+ setActivityPanelSelectedIndex(0);
356
+ setActivityPanelMode("browse");
357
+ }, [activePanel, busyLabel, refreshState, vendorActivity]);
358
+ const readiness = useMemo(() => getProviderReadinessSummary(), [refreshTick, refreshState]);
359
+ const vendorTransportSummary = getUmbrellaTransportTaskBridgeSummary();
360
+ const vendorContextSummary = getUmbrellaContextRuntimeSummary();
361
+ const vendorProviderStore = useUmbrellaContextRuntimeBridgeStore((state) => state.providerStore);
362
+ const vendorModelStore = useUmbrellaContextRuntimeBridgeStore((state) => state.modelStore);
363
+ const vendorSpaceStore = useUmbrellaContextRuntimeBridgeStore((state) => state.spaceStore);
364
+ const vendorHubStore = useUmbrellaContextRuntimeBridgeStore((state) => state.hubStore);
365
+ const vendorConnectorsStore = useUmbrellaContextRuntimeBridgeStore((state) => state.connectorsStore);
366
+ const vendorTasks = useMemo(() => buildVendorTasksList(vendorTaskStore), [vendorTaskStore]);
367
+ const vendorActivity = useMemo(() => buildActivityItems(vendorTransportStore, refreshState?.session?.events ?? []), [vendorTransportStore, refreshState?.session?.events]);
368
+ const vendorProviders = useMemo(() => vendorProviderStore?.providers ?? [], [vendorProviderStore]);
369
+ const vendorModels = useMemo(() => (vendorModelStore?.models ?? []).map((model) => model.label), [vendorModelStore]);
370
+ const vendorSpaces = useMemo(() => (vendorSpaceStore?.spaces ?? []).map((space) => ({
371
+ id: space.id,
372
+ name: space.name,
373
+ isPrimary: space.isPrimary,
374
+ })), [vendorSpaceStore]);
375
+ const vendorRecentTransportEvents = useMemo(() => (vendorTransportStore?.events ?? []).slice(0, 5), [vendorTransportStore]);
376
+ const vendorRootTreeNodes = useMemo(() => (vendorTreeStore?.nodes ?? []).filter((node) => node.depth <= 1).slice(0, 8), [vendorTreeStore]);
377
+ async function runPanelAction(busyText, action, successMessage) {
378
+ setBusyLabel(busyText);
379
+ setMessage(null);
380
+ try {
381
+ await action();
382
+ if (successMessage) {
383
+ setMessage(successMessage);
384
+ }
385
+ }
386
+ catch (error) {
387
+ setMessage(error?.message ? `Action failed: ${error.message}` : "Action failed.");
388
+ }
389
+ finally {
390
+ setBusyLabel(null);
391
+ setRefreshTick((value) => value + 1);
392
+ }
393
+ }
394
+ async function runQuery(query) {
395
+ const config = configManager.config;
396
+ if (!config) {
397
+ setMessage("Run setup first so this device knows which company and space to use.");
398
+ return;
399
+ }
400
+ setBusyLabel("Searching local and shared Context...");
401
+ const task = await createTask({
402
+ kind: "query",
403
+ title: `Query: ${query}`,
404
+ detail: "Searching local .um notes and shared server memory.",
405
+ status: "running",
406
+ panel: "query",
407
+ focus: query,
408
+ });
409
+ try {
410
+ const localMatches = summarizeLocalMemoryMatches([...(await getPendingMemories()), ...(await getPulledMemories())], query);
411
+ const res = await fetch(`${config.serverUrl}/api/memories/search?query=${encodeURIComponent(query)}`, {
412
+ headers: { Authorization: `Bearer ${config.apiKey}` },
413
+ });
414
+ if (!res.ok) {
415
+ const err = await res.text();
416
+ await completeTask(task.id, "failed", err || "Search failed.");
417
+ setMessage(`Query failed: ${err || "Search failed."}`);
418
+ return;
419
+ }
420
+ const data = await res.json();
421
+ setQueryResult({
422
+ query,
423
+ localMatches,
424
+ remoteMatches: (data.results ?? []).map((entry) => ({
425
+ id: entry.id,
426
+ content: entry.content,
427
+ tags: entry.tags ?? [],
428
+ source: entry.source ?? "server",
429
+ systemType: entry.systemType ?? "system1_knowledge",
430
+ createdAt: entry.createdAt ?? new Date().toISOString(),
431
+ })),
432
+ });
433
+ await recordSessionEvent({
434
+ kind: "query",
435
+ title: `Queried Context for "${query}"`,
436
+ detail: `Found ${localMatches.length} local match${localMatches.length === 1 ? "" : "es"} and ${(data.results ?? []).length} shared match${(data.results ?? []).length === 1 ? "" : "es"}.`,
437
+ panel: "query",
438
+ focus: query,
439
+ status: "success",
440
+ });
441
+ await completeTask(task.id, "completed", "Query finished.");
442
+ setMessage(`Query finished for "${query}".`);
443
+ setActivePanel("tasks");
444
+ setSelectedPanelIndex(panelIndexFor("tasks"));
445
+ setRefreshTick((value) => value + 1);
446
+ }
447
+ finally {
448
+ setBusyLabel(null);
449
+ }
450
+ }
451
+ async function runCurate(content) {
452
+ const config = configManager.config;
453
+ if (!config) {
454
+ setMessage("Run setup first so this repo can save Context into .um.");
455
+ return;
456
+ }
457
+ setBusyLabel("Saving local Context note...");
458
+ const task = await createTask({
459
+ kind: "curate",
460
+ title: "Curate local note",
461
+ detail: content,
462
+ status: "running",
463
+ panel: "curate",
464
+ focus: content,
465
+ });
466
+ try {
467
+ await ensureRepoContext(config);
468
+ const entry = await addPendingMemory({
469
+ content,
470
+ tags: [],
471
+ source: "cli",
472
+ systemType: "system1_knowledge",
473
+ });
474
+ await recordSessionEvent({
475
+ kind: "curate",
476
+ title: "Saved a new local Context note",
477
+ detail: `Draft ${entry.id} was added to .um for ${config.companyName} / ${config.projectName}.`,
478
+ panel: "curate",
479
+ focus: entry.id,
480
+ status: "success",
481
+ });
482
+ await completeTask(task.id, "completed", `Saved local draft ${entry.id}.`);
483
+ setMessage(`Saved local Context note for ${config.companyName} / ${config.projectName}.`);
484
+ setActivePanel("tasks");
485
+ setSelectedPanelIndex(panelIndexFor("tasks"));
486
+ setRefreshTick((value) => value + 1);
487
+ }
488
+ finally {
489
+ setBusyLabel(null);
490
+ }
491
+ }
492
+ async function submitSetupText() {
493
+ const nextValue = inputValue.trim();
494
+ if (!nextValue && setupStep !== "password")
495
+ return;
496
+ if (setupStep === "server") {
497
+ const nextServer = trimSlash(nextValue || serverUrl);
498
+ setServerUrl(nextServer);
499
+ setInputValue("");
500
+ setBusyLabel("Checking Umbrella server...");
501
+ try {
502
+ const serverCheck = await checkUmbrellaOnboardingServer(nextServer);
503
+ if (serverCheck.deploymentMode === "authenticated") {
504
+ setSetupStep("email");
505
+ }
506
+ else {
507
+ setCompanies(serverCheck.companies);
508
+ setSelectedCompanyIndex(0);
509
+ setSetupStep("company");
510
+ }
511
+ }
512
+ catch (error) {
513
+ setMessage(`Could not reach Umbrella: ${error.message}`);
514
+ }
515
+ finally {
516
+ setBusyLabel(null);
517
+ }
518
+ return;
519
+ }
520
+ if (setupStep === "email") {
521
+ setSetupEmail(nextValue);
522
+ setInputValue("");
523
+ setSetupStep("password");
524
+ return;
525
+ }
526
+ if (setupStep === "password") {
527
+ setInputValue("");
528
+ setBusyLabel("Signing into Umbrella...");
529
+ try {
530
+ const signIn = await signInUmbrellaOnboarding(serverUrl, setupEmail, nextValue);
531
+ setSetupCookie(signIn.cookie);
532
+ setCompanies(signIn.companies);
533
+ setSelectedCompanyIndex(0);
534
+ setSetupStep("company");
535
+ }
536
+ catch (error) {
537
+ setMessage(`Sign-in failed: ${error.message}`);
538
+ }
539
+ finally {
540
+ setBusyLabel(null);
541
+ }
542
+ return;
543
+ }
544
+ if (setupStep === "company-create") {
545
+ setBusyLabel("Creating company...");
546
+ try {
547
+ const created = await createUmbrellaOnboardingCompany(serverUrl, nextValue, setupCookie);
548
+ const company = created.company;
549
+ const nextCompanies = [...companies, company];
550
+ setCompanies(nextCompanies);
551
+ setSelectedCompany(company);
552
+ setSpaces(created.spaces);
553
+ setSelectedSpaceIndex(0);
554
+ setInputValue("");
555
+ setSetupStep("space");
556
+ }
557
+ catch (error) {
558
+ setMessage(`Could not create company: ${error.message}`);
559
+ }
560
+ finally {
561
+ setBusyLabel(null);
562
+ }
563
+ return;
564
+ }
565
+ if (setupStep === "space-create" && selectedCompany) {
566
+ setBusyLabel("Creating space...");
567
+ try {
568
+ const created = await createUmbrellaOnboardingSpace(serverUrl, selectedCompany.id, nextValue, setupCookie);
569
+ const nextSpaces = created.spaces;
570
+ setSpaces(nextSpaces);
571
+ setSelectedSpaceIndex(Math.max(0, nextSpaces.findIndex((space) => space.id === created.activeSpaceId)));
572
+ setInputValue("");
573
+ setSetupStep("space");
574
+ }
575
+ catch (error) {
576
+ setMessage(`Could not create space: ${error.message}`);
577
+ }
578
+ finally {
579
+ setBusyLabel(null);
580
+ }
581
+ }
582
+ }
583
+ async function confirmSetupSelection() {
584
+ if (setupStep === "company") {
585
+ if (selectedCompanyIndex === companies.length) {
586
+ setInputValue("");
587
+ setSetupStep("company-create");
588
+ return;
589
+ }
590
+ const company = companies[selectedCompanyIndex] ?? null;
591
+ if (!company)
592
+ return;
593
+ setSelectedCompany(company);
594
+ setBusyLabel("Loading spaces...");
595
+ try {
596
+ const nextSpaces = await loadUmbrellaOnboardingSpaces(serverUrl, company.id, setupCookie);
597
+ setSpaces(nextSpaces);
598
+ setSelectedSpaceIndex(0);
599
+ setSetupStep("space");
600
+ }
601
+ catch (error) {
602
+ setMessage(`Could not load spaces: ${error.message}`);
603
+ }
604
+ finally {
605
+ setBusyLabel(null);
606
+ }
607
+ return;
608
+ }
609
+ if (setupStep === "space" && selectedCompany) {
610
+ if (selectedSpaceIndex === spaces.length) {
611
+ setInputValue("");
612
+ setSetupStep("space-create");
613
+ return;
614
+ }
615
+ const space = spaces[selectedSpaceIndex] ?? null;
616
+ if (!space)
617
+ return;
618
+ setBusyLabel("Saving device link...");
619
+ try {
620
+ const setup = await connectUmbrellaDevice(serverUrl, selectedCompany.id, space.id, setupCookie);
621
+ await recordSessionEvent({
622
+ kind: "session",
623
+ title: `Connected this device to ${setup.companyName} / ${setup.activeSpaceName}`,
624
+ detail: "Setup completed inside the TUI.",
625
+ panel: "setup",
626
+ focus: setup.activeSpaceName,
627
+ status: "success",
628
+ });
629
+ setSetupStep("done");
630
+ setMessage(`Connected to ${setup.companyName} / ${setup.activeSpaceName}.`);
631
+ setActivePanel("home");
632
+ setSelectedPanelIndex(panelIndexFor("home"));
633
+ setRefreshTick((value) => value + 1);
634
+ }
635
+ catch (error) {
636
+ setMessage(`Could not finish setup: ${error.message}`);
637
+ }
638
+ finally {
639
+ setBusyLabel(null);
640
+ }
641
+ }
642
+ }
643
+ useInput((input, key) => {
644
+ if (busyLabel)
645
+ return;
646
+ if (inputMode) {
647
+ if (key.return) {
648
+ const nextValue = inputValue.trim();
649
+ if (!nextValue) {
650
+ setInputMode(null);
651
+ setInputValue("");
652
+ return;
653
+ }
654
+ if (inputMode === "query") {
655
+ void runQuery(nextValue);
656
+ }
657
+ else {
658
+ void runCurate(nextValue);
659
+ }
660
+ setInputMode(null);
661
+ setInputValue("");
662
+ return;
663
+ }
664
+ if (key.escape) {
665
+ setInputMode(null);
666
+ setInputValue("");
667
+ }
668
+ return;
669
+ }
670
+ if (activePanel === "setup" && setupStep !== "done") {
671
+ if (key.return) {
672
+ if (setupStep === "company" || setupStep === "space") {
673
+ void confirmSetupSelection();
674
+ }
675
+ else {
676
+ void submitSetupText();
677
+ }
678
+ return;
679
+ }
680
+ if (key.escape) {
681
+ if (configManager.config) {
682
+ setActivePanel("home");
683
+ setSelectedPanelIndex(panelIndexFor("home"));
684
+ }
685
+ return;
686
+ }
687
+ if (key.upArrow && setupStep === "company") {
688
+ setSelectedCompanyIndex((value) => Math.max(0, value - 1));
689
+ return;
690
+ }
691
+ if (key.downArrow && setupStep === "company") {
692
+ setSelectedCompanyIndex((value) => Math.min(companies.length, value + 1));
693
+ return;
694
+ }
695
+ if (key.upArrow && setupStep === "space") {
696
+ setSelectedSpaceIndex((value) => Math.max(0, value - 1));
697
+ return;
698
+ }
699
+ if (key.downArrow && setupStep === "space") {
700
+ setSelectedSpaceIndex((value) => Math.min(spaces.length, value + 1));
701
+ return;
702
+ }
703
+ return;
704
+ }
705
+ if (activePanel === "spaces" && spacePanelMode === "create") {
706
+ if (key.return) {
707
+ const nextName = spacePanelDraftName.trim();
708
+ if (!nextName) {
709
+ setMessage("Type a name for the new space first.");
710
+ return;
711
+ }
712
+ void runPanelAction("Creating a new space...", async () => {
713
+ await spaceCommandAction("create", nextName);
714
+ setSpacePanelDraftName("");
715
+ setSpacePanelMode("browse");
716
+ }, "Space creation finished.");
717
+ return;
718
+ }
719
+ if (key.escape) {
720
+ setSpacePanelDraftName("");
721
+ setSpacePanelMode("browse");
722
+ }
723
+ return;
724
+ }
725
+ if (activePanel === "providers" && providerPanelMode !== "browse") {
726
+ if (providerPanelMode === "connect-kind") {
727
+ if (key.upArrow || input === "k") {
728
+ setProviderPanelConnectKindIndex((value) => Math.max(0, value - 1));
729
+ return;
730
+ }
731
+ if (key.downArrow || input === "j") {
732
+ setProviderPanelConnectKindIndex((value) => Math.min(UMBRELLA_PROVIDER_CHOICES.length - 1, value + 1));
733
+ return;
734
+ }
735
+ if (key.return) {
736
+ const choice = UMBRELLA_PROVIDER_CHOICES[providerPanelConnectKindIndex];
737
+ if (!choice)
738
+ return;
739
+ setProviderPanelConnectDraft((value) => ({ ...value, kind: choice.kind, label: "" }));
740
+ setProviderPanelMode("connect-label");
741
+ return;
742
+ }
743
+ if (key.escape) {
744
+ setProviderPanelConnectDraft(createEmptyProviderConnectDraft());
745
+ setProviderPanelMode("browse");
746
+ return;
747
+ }
748
+ return;
749
+ }
750
+ if (providerPanelMode === "connect-label") {
751
+ if (key.return) {
752
+ const nextLabel = providerPanelConnectDraft.label.trim();
753
+ if (!nextLabel) {
754
+ setMessage("Type a provider label first.");
755
+ return;
756
+ }
757
+ setProviderPanelConnectDraft((value) => ({ ...value, label: nextLabel }));
758
+ setProviderPanelMode("connect-key");
759
+ return;
760
+ }
761
+ if (key.escape) {
762
+ setProviderPanelConnectDraft((value) => ({ ...value, label: "", kind: value.kind }));
763
+ setProviderPanelMode("connect-kind");
764
+ }
765
+ return;
766
+ }
767
+ if (providerPanelMode === "connect-key") {
768
+ if (key.return) {
769
+ const nextKey = providerPanelConnectDraft.apiKey.trim();
770
+ if (!nextKey) {
771
+ setMessage("Paste an API key first.");
772
+ return;
773
+ }
774
+ setProviderPanelConnectDraft((value) => ({ ...value, apiKey: nextKey }));
775
+ if (providerPanelConnectDraft.kind === "openai-compatible") {
776
+ setProviderPanelMode("connect-base-url");
777
+ }
778
+ else {
779
+ void runPanelAction("Connecting provider...", async () => {
780
+ const provider = await connectSavedProviderFromDraft({
781
+ ...providerPanelConnectDraft,
782
+ apiKey: nextKey,
783
+ });
784
+ setProviderPanelRecentId(provider.id);
785
+ setProviderPanelMode("browse");
786
+ setProviderPanelConnectDraft(createEmptyProviderConnectDraft());
787
+ }, "Provider connected.");
788
+ }
789
+ return;
790
+ }
791
+ if (key.escape) {
792
+ setProviderPanelConnectDraft((value) => ({ ...value, apiKey: "" }));
793
+ setProviderPanelMode("connect-label");
794
+ }
795
+ return;
796
+ }
797
+ if (providerPanelMode === "connect-base-url") {
798
+ if (key.return) {
799
+ const nextBaseUrl = providerPanelConnectDraft.baseUrl.trim();
800
+ if (!nextBaseUrl) {
801
+ setMessage("Type the compatible base URL first.");
802
+ return;
803
+ }
804
+ void runPanelAction("Connecting provider...", async () => {
805
+ const provider = await connectSavedProviderFromDraft({
806
+ ...providerPanelConnectDraft,
807
+ baseUrl: nextBaseUrl,
808
+ });
809
+ setProviderPanelRecentId(provider.id);
810
+ setProviderPanelMode("browse");
811
+ setProviderPanelConnectDraft(createEmptyProviderConnectDraft());
812
+ }, "Provider connected.");
813
+ return;
814
+ }
815
+ if (key.escape) {
816
+ setProviderPanelConnectDraft((value) => ({ ...value, baseUrl: "" }));
817
+ setProviderPanelMode("connect-key");
818
+ }
819
+ return;
820
+ }
821
+ }
822
+ if (input === "q") {
823
+ exit();
824
+ return;
825
+ }
826
+ const providerPanelOwnsArrows = activePanel === "providers";
827
+ const modelPanelOwnsArrows = activePanel === "model";
828
+ const spacePanelOwnsArrows = activePanel === "spaces";
829
+ const hubPanelOwnsArrows = activePanel === "hub";
830
+ const connectorsPanelOwnsArrows = activePanel === "connectors";
831
+ const tasksPanelOwnsArrows = activePanel === "tasks";
832
+ const activityPanelOwnsArrows = activePanel === "activity";
833
+ if (key.upArrow || input === "k") {
834
+ if (!providerPanelOwnsArrows && !modelPanelOwnsArrows && !spacePanelOwnsArrows && !hubPanelOwnsArrows && !connectorsPanelOwnsArrows && !tasksPanelOwnsArrows && !activityPanelOwnsArrows) {
835
+ const nextIndex = Math.max(0, selectedPanelIndex - 1);
836
+ setSelectedPanelIndex(nextIndex);
837
+ setActivePanel(PANELS[nextIndex]?.id ?? "home");
838
+ return;
839
+ }
840
+ }
841
+ if (key.downArrow || input === "j") {
842
+ if (!providerPanelOwnsArrows && !modelPanelOwnsArrows && !spacePanelOwnsArrows && !hubPanelOwnsArrows && !connectorsPanelOwnsArrows && !tasksPanelOwnsArrows && !activityPanelOwnsArrows) {
843
+ const nextIndex = Math.min(PANELS.length - 1, selectedPanelIndex + 1);
844
+ setSelectedPanelIndex(nextIndex);
845
+ setActivePanel(PANELS[nextIndex]?.id ?? "home");
846
+ return;
847
+ }
848
+ }
849
+ if (key.tab) {
850
+ const nextIndex = (selectedPanelIndex + 1) % PANELS.length;
851
+ setSelectedPanelIndex(nextIndex);
852
+ setActivePanel(PANELS[nextIndex]?.id ?? "home");
853
+ return;
854
+ }
855
+ if (input === "?") {
856
+ setInputMode("query");
857
+ setInputValue("");
858
+ return;
859
+ }
860
+ if (input === "+") {
861
+ setInputMode("curate");
862
+ setInputValue("");
863
+ return;
864
+ }
865
+ if (input === "s") {
866
+ setActivePanel("setup");
867
+ setSelectedPanelIndex(0);
868
+ return;
869
+ }
870
+ if (activePanel === "providers") {
871
+ if (key.upArrow || input === "k") {
872
+ setProviderPanelSelectedIndex((value) => Math.max(0, value - 1));
873
+ return;
874
+ }
875
+ if (key.downArrow || input === "j") {
876
+ setProviderPanelSelectedIndex((value) => Math.min(Math.max(0, vendorProviders.length - 1), value + 1));
877
+ return;
878
+ }
879
+ if (key.return || input === "w") {
880
+ const provider = vendorProviders[providerPanelSelectedIndex];
881
+ if (!provider)
882
+ return;
883
+ void runPanelAction("Switching provider...", async () => {
884
+ await providersCommandAction("switch", provider.id);
885
+ setProviderPanelRecentId(provider.id);
886
+ }, "Provider switch finished.");
887
+ return;
888
+ }
889
+ if (input === "c") {
890
+ setProviderPanelConnectKindIndex(0);
891
+ setProviderPanelConnectDraft(createEmptyProviderConnectDraft());
892
+ setProviderPanelMode("connect-kind");
893
+ return;
894
+ }
895
+ if (input === "d") {
896
+ const provider = vendorProviders[providerPanelSelectedIndex];
897
+ if (!provider)
898
+ return;
899
+ void runPanelAction("Disconnecting provider...", async () => {
900
+ await providersCommandAction("disconnect", provider.id);
901
+ }, "Provider disconnect finished.");
902
+ return;
903
+ }
904
+ if (input === "t") {
905
+ void runPanelAction("Testing provider runtime...", async () => {
906
+ await providersCommandAction("test");
907
+ }, "Provider runtime check finished.");
908
+ return;
909
+ }
910
+ if (input === "i") {
911
+ void runPanelAction("Inspecting provider...", async () => {
912
+ await providersCommandAction("recent");
913
+ }, "Provider inspection finished.");
914
+ return;
915
+ }
916
+ if (input === "g") {
917
+ void runPanelAction("Running guided provider setup...", async () => {
918
+ await providersCommandAction("setup");
919
+ }, "Provider setup flow finished.");
920
+ return;
921
+ }
922
+ if (input === "m") {
923
+ setActivePanel("model");
924
+ setSelectedPanelIndex(panelIndexFor("model"));
925
+ return;
926
+ }
927
+ }
928
+ if (activePanel === "model") {
929
+ if (key.upArrow || input === "k") {
930
+ setModelPanelSelectedIndex((value) => Math.max(0, value - 1));
931
+ return;
932
+ }
933
+ if (key.downArrow || input === "j") {
934
+ setModelPanelSelectedIndex((value) => Math.min(Math.max(0, vendorModels.length - 1), value + 1));
935
+ return;
936
+ }
937
+ if (key.return || input === "w") {
938
+ const selectedModel = vendorModels[modelPanelSelectedIndex];
939
+ if (!selectedModel)
940
+ return;
941
+ void runPanelAction("Switching model...", async () => {
942
+ await modelCommandAction("switch", selectedModel);
943
+ setModelPanelRecentModel(selectedModel);
944
+ }, "Model switch finished.");
945
+ return;
946
+ }
947
+ if (input === "i") {
948
+ void runPanelAction("Inspecting model...", async () => {
949
+ await modelCommandAction("recent");
950
+ }, "Model inspection finished.");
951
+ return;
952
+ }
953
+ if (input === "t") {
954
+ void runPanelAction("Checking model readiness...", async () => {
955
+ await modelCommandAction("ready");
956
+ }, "Model readiness check finished.");
957
+ return;
958
+ }
959
+ }
960
+ if (activePanel === "spaces") {
961
+ if (key.upArrow || input === "k") {
962
+ setSpacePanelSelectedIndex((value) => Math.max(0, value - 1));
963
+ return;
964
+ }
965
+ if (key.downArrow || input === "j") {
966
+ setSpacePanelSelectedIndex((value) => Math.min(Math.max(0, vendorSpaces.length - 1), value + 1));
967
+ return;
968
+ }
969
+ if (key.return) {
970
+ const selectedSpace = vendorSpaces[spacePanelSelectedIndex];
971
+ if (!selectedSpace)
972
+ return;
973
+ void runPanelAction("Switching space...", async () => {
974
+ await spaceCommandAction("switch", selectedSpace.id);
975
+ }, "Space switch finished.");
976
+ return;
977
+ }
978
+ if (input === "w") {
979
+ const selectedSpace = vendorSpaces[spacePanelSelectedIndex];
980
+ if (!selectedSpace)
981
+ return;
982
+ void runPanelAction("Switching space...", async () => {
983
+ await spaceCommandAction("switch", selectedSpace.id);
984
+ }, "Space switch finished.");
985
+ return;
986
+ }
987
+ if (input === "n") {
988
+ setSpacePanelDraftName("");
989
+ setSpacePanelMode("create");
990
+ return;
991
+ }
992
+ if (input === "i") {
993
+ void runPanelAction("Refreshing space list...", async () => {
994
+ await buildVendorRuntimeBridgeSnapshot();
995
+ }, "Space list refreshed.");
996
+ return;
997
+ }
998
+ }
999
+ if (activePanel === "tasks") {
1000
+ if (key.upArrow || input === "k") {
1001
+ if (tasksPanelMode === "inspect")
1002
+ return;
1003
+ setTasksPanelSelectedIndex((value) => Math.max(0, value - 1));
1004
+ return;
1005
+ }
1006
+ if (key.downArrow || input === "j") {
1007
+ if (tasksPanelMode === "inspect")
1008
+ return;
1009
+ setTasksPanelSelectedIndex((value) => Math.min(Math.max(0, vendorTasks.length - 1), value + 1));
1010
+ return;
1011
+ }
1012
+ if (key.return || input === "i") {
1013
+ const task = vendorTasks[tasksPanelSelectedIndex];
1014
+ if (!task)
1015
+ return;
1016
+ setTasksPanelMode("inspect");
1017
+ return;
1018
+ }
1019
+ if (input === "o") {
1020
+ const task = vendorTasks[tasksPanelSelectedIndex];
1021
+ if (!task)
1022
+ return;
1023
+ const nextPanel = panelForTaskKind(task.type);
1024
+ setActivePanel(nextPanel);
1025
+ setSelectedPanelIndex(panelIndexFor(nextPanel));
1026
+ return;
1027
+ }
1028
+ if (key.escape && tasksPanelMode === "inspect") {
1029
+ setTasksPanelMode("browse");
1030
+ return;
1031
+ }
1032
+ }
1033
+ if (activePanel === "activity") {
1034
+ if (key.upArrow || input === "k") {
1035
+ if (activityPanelMode === "inspect")
1036
+ return;
1037
+ setActivityPanelSelectedIndex((value) => Math.max(0, value - 1));
1038
+ return;
1039
+ }
1040
+ if (key.downArrow || input === "j") {
1041
+ if (activityPanelMode === "inspect")
1042
+ return;
1043
+ setActivityPanelSelectedIndex((value) => Math.min(Math.max(0, vendorActivity.length - 1), value + 1));
1044
+ return;
1045
+ }
1046
+ if (key.return || input === "o") {
1047
+ const event = vendorActivity[activityPanelSelectedIndex];
1048
+ if (!event)
1049
+ return;
1050
+ if (input !== "o" && key.return && activityPanelMode === "browse") {
1051
+ setActivityPanelMode("inspect");
1052
+ return;
1053
+ }
1054
+ const nextPanel = panelForActivityItem(event);
1055
+ if (!nextPanel)
1056
+ return;
1057
+ setActivePanel(nextPanel);
1058
+ setSelectedPanelIndex(panelIndexFor(nextPanel));
1059
+ return;
1060
+ }
1061
+ if (key.escape && activityPanelMode === "inspect") {
1062
+ setActivityPanelMode("browse");
1063
+ return;
1064
+ }
1065
+ if (input === "i") {
1066
+ setActivityPanelMode("inspect");
1067
+ return;
1068
+ }
1069
+ }
1070
+ if (activePanel === "hub") {
1071
+ const registryEntries = listHubRegistryEntries();
1072
+ if (key.upArrow || input === "k") {
1073
+ setHubPanelSelectedIndex((value) => Math.max(0, value - 1));
1074
+ return;
1075
+ }
1076
+ if (key.downArrow || input === "j") {
1077
+ setHubPanelSelectedIndex((value) => Math.min(Math.max(0, registryEntries.length - 1), value + 1));
1078
+ return;
1079
+ }
1080
+ if (key.return || input === "n") {
1081
+ const entry = registryEntries[hubPanelSelectedIndex];
1082
+ if (!entry)
1083
+ return;
1084
+ void runPanelAction("Installing hub entry...", async () => {
1085
+ await hubCommandAction("install", entry.slug);
1086
+ }, "Hub install finished.");
1087
+ return;
1088
+ }
1089
+ if (input === "b") {
1090
+ void runPanelAction("Browsing hub entries...", async () => {
1091
+ await hubCommandAction("browse");
1092
+ }, "Hub browse flow finished.");
1093
+ return;
1094
+ }
1095
+ if (input === "i") {
1096
+ const entry = registryEntries[hubPanelSelectedIndex] ??
1097
+ registryEntries.find((candidate) => candidate.slug === vendorHubStore?.recentSlug);
1098
+ void runPanelAction("Inspecting hub entry...", async () => {
1099
+ await hubCommandAction("inspect", entry?.slug);
1100
+ }, "Hub inspection finished.");
1101
+ return;
1102
+ }
1103
+ if (input === "l") {
1104
+ void runPanelAction("Listing installed hub entries...", async () => {
1105
+ await hubCommandAction("installed");
1106
+ }, "Hub inventory refreshed.");
1107
+ return;
1108
+ }
1109
+ }
1110
+ if (activePanel === "connectors") {
1111
+ const registryEntries = listConnectorTemplates();
1112
+ if (key.upArrow || input === "k") {
1113
+ setConnectorsPanelSelectedIndex((value) => Math.max(0, value - 1));
1114
+ return;
1115
+ }
1116
+ if (key.downArrow || input === "j") {
1117
+ setConnectorsPanelSelectedIndex((value) => Math.min(Math.max(0, registryEntries.length - 1), value + 1));
1118
+ return;
1119
+ }
1120
+ if (key.return || input === "i") {
1121
+ const entry = registryEntries[connectorsPanelSelectedIndex];
1122
+ if (!entry)
1123
+ return;
1124
+ void runPanelAction("Installing connector...", async () => {
1125
+ await connectorsCommandAction("install", entry.key);
1126
+ }, "Connector install finished.");
1127
+ return;
1128
+ }
1129
+ if (input === "r") {
1130
+ const entry = registryEntries[connectorsPanelSelectedIndex];
1131
+ if (!entry)
1132
+ return;
1133
+ void runPanelAction("Running connector...", async () => {
1134
+ await connectorsCommandAction("run", entry.key);
1135
+ }, "Connector run finished.");
1136
+ return;
1137
+ }
1138
+ if (input === "o") {
1139
+ const entry = registryEntries[connectorsPanelSelectedIndex] ??
1140
+ registryEntries.find((candidate) => candidate.key === vendorConnectorsStore?.recentRunSource);
1141
+ void runPanelAction("Opening connector outputs...", async () => {
1142
+ await connectorsCommandAction("outputs", entry?.key);
1143
+ }, "Connector outputs refreshed.");
1144
+ return;
1145
+ }
1146
+ if (input === "v") {
1147
+ const entry = registryEntries[connectorsPanelSelectedIndex] ??
1148
+ registryEntries.find((candidate) => candidate.key === vendorConnectorsStore?.recentSource);
1149
+ void runPanelAction("Inspecting connector...", async () => {
1150
+ await connectorsCommandAction("inspect", entry?.key);
1151
+ }, "Connector inspection finished.");
1152
+ return;
1153
+ }
1154
+ }
1155
+ if (input === "r") {
1156
+ setRefreshTick((value) => value + 1);
1157
+ setMessage("Refreshed the local TUI view.");
1158
+ return;
1159
+ }
1160
+ if (input === "p" && configManager.config) {
1161
+ setBusyLabel("Pushing local Context to the server...");
1162
+ void pushCommandAction().finally(() => {
1163
+ setBusyLabel(null);
1164
+ setMessage("Finished push.");
1165
+ setRefreshTick((value) => value + 1);
1166
+ });
1167
+ return;
1168
+ }
1169
+ if (input === "l" && configManager.config) {
1170
+ setBusyLabel("Pulling the latest shared Context...");
1171
+ void pullCommandAction().finally(() => {
1172
+ setBusyLabel(null);
1173
+ setMessage("Finished pull.");
1174
+ setRefreshTick((value) => value + 1);
1175
+ });
1176
+ return;
1177
+ }
1178
+ });
1179
+ const footer = inputMode
1180
+ ? inputMode === "query"
1181
+ ? "Query mode: type your question, Enter to search, Esc to cancel"
1182
+ : "Curate mode: type what you learned, Enter to save locally, Esc to cancel"
1183
+ : activePanel === "setup" && setupStep !== "done"
1184
+ ? "Setup mode: Enter confirms, Up/Down move through companies or spaces, Esc leaves setup"
1185
+ : activePanel === "providers"
1186
+ ? providerPanelMode === "browse"
1187
+ ? "Providers: Up/Down choose | Enter or w switch | c connect | d disconnect | t test | i inspect recent | g guided setup | m model | ? query | + curate | q quit"
1188
+ : providerPanelMode === "connect-kind"
1189
+ ? "Providers connect: Up/Down choose provider type | Enter confirm | Esc back"
1190
+ : "Providers connect: type value | Enter continue | Esc back"
1191
+ : activePanel === "model"
1192
+ ? modelPanelMode === "browse"
1193
+ ? "Model: Up/Down choose | Enter or w switch | i inspect recent | t readiness | ? query | + curate | q quit"
1194
+ : "Model runtime panel"
1195
+ : activePanel === "spaces"
1196
+ ? "Spaces: i list | w switch | n create | ? query | + curate | q quit"
1197
+ : activePanel === "hub"
1198
+ ? hubPanelMode === "browse"
1199
+ ? "Hub: Up/Down choose | Enter or n install | i inspect | b guided browse | l refresh | ? query | + curate | q quit"
1200
+ : "Hub live panel"
1201
+ : activePanel === "connectors"
1202
+ ? connectorsPanelMode === "browse"
1203
+ ? "Connectors: Up/Down choose | Enter or i install | r run | o outputs | v inspect | ? query | + curate | q quit"
1204
+ : "Connectors live panel"
1205
+ : activePanel === "tasks"
1206
+ ? tasksPanelMode === "browse"
1207
+ ? "Tasks: Up/Down choose | Enter or i inspect | o open related panel | r refresh | ? query | + curate | q quit"
1208
+ : "Tasks inspect: Esc back | o open related panel | r refresh | ? query | + curate | q quit"
1209
+ : activePanel === "activity"
1210
+ ? activityPanelMode === "browse"
1211
+ ? "Activity: Up/Down choose | Enter or i inspect | o open related panel | r refresh | ? query | + curate | q quit"
1212
+ : "Activity inspect: Esc back | Enter or o open related panel | r refresh | ? query | + curate | q quit"
1213
+ : "Up/Down or j/k move panels | Tab cycles | ? query | + curate | p push | l pull | r refresh | s setup | q quit";
1214
+ const config = configManager.config;
1215
+ let mainContent = null;
1216
+ if (activePanel === "setup") {
1217
+ mainContent = (_jsx(SetupView, { busyLabel: busyLabel, companies: companies, config: config, inputValue: inputValue, message: message, onInputChange: setInputValue, selectedCompanyIndex: selectedCompanyIndex, selectedSpaceIndex: selectedSpaceIndex, serverUrl: serverUrl, spaces: spaces, step: setupStep }));
1218
+ }
1219
+ else if (!config || !refreshState) {
1220
+ mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: "This device is not linked yet." }), _jsx(Text, { color: "gray", children: "Move to Setup and connect the repo to a company and space first." })] }));
1221
+ }
1222
+ else if (activePanel === "home") {
1223
+ mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [config.companyName, " / ", config.projectName] }), _jsx(Text, { color: "gray", children: "This is the repo-level Context home for the current company space." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Section, { title: "Sync", children: [_jsx(StatusLine, { label: "Pending drafts", value: String(refreshState.pending.length) }), _jsx(StatusLine, { label: "Pulled context", value: String(refreshState.pulled.length) }), _jsx(StatusLine, { label: "Known fixes", value: String(refreshState.fixes.length) }), _jsx(StatusLine, { label: "Last push", value: formatAgo(refreshState.repoContext.state?.lastPushAt) }), _jsx(StatusLine, { label: "Last pull", value: formatAgo(refreshState.repoContext.state?.lastPullAt) })] }), _jsxs(Section, { title: "Runtime", children: [_jsx(StatusLine, { label: "Provider", value: readiness.activeProvider?.name ?? "Not connected" }), _jsx(StatusLine, { label: "Model", value: readiness.activeModel ?? "Not selected" }), _jsx(StatusLine, { label: "Transport", value: vendorTransportStore?.status ?? refreshState.transport.status }), _jsx(StatusLine, { label: "Repo .um", value: refreshState.repoContext.umDir })] }), _jsxs(Section, { title: "Context Tree", children: [_jsx(StatusLine, { label: "Summary", value: vendorTreeStore?.summaryHandle ?? refreshState.treeState.summaryHandle }), _jsx(StatusLine, { label: "Nodes", value: String(vendorTreeStore?.nodes.length ?? refreshState.treeState.nodes.length) }), _jsx(StatusLine, { label: "Runtime provider", value: vendorTreeStore?.runtime.provider ?? "No provider" }), _jsx(StatusLine, { label: "Runtime model", value: vendorTreeStore?.runtime.model ?? "No model" })] }), queryResult ? (_jsxs(Section, { title: `Latest Query: ${queryResult.query}`, children: [_jsxs(Text, { children: ["Local matches: ", String(queryResult.localMatches.length)] }), _jsxs(Text, { children: ["Shared matches: ", String(queryResult.remoteMatches.length)] })] })) : null] })] }));
1224
+ }
1225
+ else if (activePanel === "tasks") {
1226
+ mainContent = (_jsx(TasksPanelView, { mode: tasksPanelMode, selectedIndex: tasksPanelSelectedIndex, tasks: vendorTasks, transportStatus: vendorTransportStore?.status ?? vendorTransportSummary.transportStatus, queueLength: vendorTransportStore?.queue.length ?? vendorTransportSummary.queueDepth, activeTaskId: vendorTransportStore?.queue[0]?.id ??
1227
+ vendorTransportStore?.queue.find((item) => item.status === "running")?.id ??
1228
+ vendorTransportSummary.activeTaskId }));
1229
+ }
1230
+ else if (activePanel === "activity") {
1231
+ mainContent = (_jsx(ActivityPanelView, { activity: vendorActivity, mode: activityPanelMode, selectedIndex: activityPanelSelectedIndex }));
1232
+ }
1233
+ else if (activePanel === "providers") {
1234
+ mainContent = (_jsx(ProviderPanelView, { activeModel: readiness.activeModel, activeProviderId: vendorProviderStore?.activeProviderId ?? config.activeProvider, connectDraftValue: buildProviderDraftValue(providerPanelConnectDraft, providerPanelMode === "browse" || providerPanelMode === "connect-kind" ? "connect-label" : providerPanelMode), connectKindIndex: providerPanelConnectKindIndex, mode: providerPanelMode, onConnectDraftChange: (value) => {
1235
+ if (providerPanelMode === "browse" || providerPanelMode === "connect-kind") {
1236
+ return;
1237
+ }
1238
+ setProviderPanelConnectDraft((current) => updateProviderDraftValue(current, providerPanelMode, value));
1239
+ }, providers: vendorProviders, recentProviderId: providerPanelRecentId, readinessState: !readiness.providerReady ? "Needs provider" : !readiness.modelReady ? "Needs model" : "Ready", selectedIndex: providerPanelSelectedIndex }));
1240
+ }
1241
+ else if (activePanel === "model") {
1242
+ mainContent = (_jsx(ModelPanelView, { activeModel: vendorModelStore?.activeModel ?? readiness.activeModel, providerName: vendorProviders.find((provider) => provider.id === (vendorProviderStore?.activeProviderId ?? config.activeProvider))?.name ?? readiness.activeProvider?.name ?? "Not connected", recentModel: modelPanelRecentModel, selectedIndex: modelPanelSelectedIndex, models: vendorModels }));
1243
+ }
1244
+ else if (activePanel === "spaces") {
1245
+ mainContent = (_jsx(SpacePanelView, { companyName: vendorContextSummary.companyName ?? config.companyName, currentSpaceName: vendorContextSummary.spaceName ?? config.projectName, draftName: spacePanelDraftName, mode: spacePanelMode, onDraftChange: setSpacePanelDraftName, selectedIndex: spacePanelSelectedIndex, spaces: vendorSpaces }));
1246
+ }
1247
+ else if (activePanel === "hub") {
1248
+ const registryEntries = listHubRegistryEntries();
1249
+ mainContent = (_jsx(HubPanelView, { installedSlugs: vendorHubStore?.installedSlugs ?? [], recentSlug: vendorHubStore?.recentSlug ?? null, registryEntries: registryEntries, selectedIndex: hubPanelSelectedIndex }));
1250
+ }
1251
+ else if (activePanel === "connectors") {
1252
+ const registryEntries = listConnectorTemplates();
1253
+ mainContent = (_jsx(ConnectorsPanelView, { installedSources: vendorConnectorsStore?.installedSources ?? [], recentSource: vendorConnectorsStore?.recentSource ?? null, recentRunSource: vendorConnectorsStore?.recentRunSource ?? null, registryEntries: registryEntries, selectedIndex: connectorsPanelSelectedIndex }));
1254
+ }
1255
+ else {
1256
+ mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Session Memory" }), _jsx(Text, { color: "gray", children: "This is the live terminal memory saved inside the repo, plus the vendor-style runtime stores hydrated from the same Context engine." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Session ID", value: refreshState.session?.id ?? "None" }), _jsx(StatusLine, { label: "Current panel", value: refreshState.session?.currentPanel ?? "None" }), _jsx(StatusLine, { label: "Current focus", value: refreshState.session?.currentFocus ?? "None" }), _jsx(StatusLine, { label: "Commands", value: String(refreshState.session?.commandHistory.length ?? 0) }), _jsx(StatusLine, { label: "Events", value: String(refreshState.session?.events.length ?? 0) }), _jsx(StatusLine, { label: "Transport", value: vendorTransportStore?.status ?? refreshState.transport.status }), _jsx(StatusLine, { label: "Tree summary", value: vendorTreeStore?.summaryHandle ?? refreshState.treeState.summaryHandle })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Section, { title: "Transport and Tasks", children: [_jsx(StatusLine, { label: "Queue depth", value: String(vendorTransportStore?.queue.length ?? 0) }), _jsx(StatusLine, { label: "Recent tasks", value: String(vendorTasks.length) }), _jsx(StatusLine, { label: "Active task", value: vendorTransportSummary.activeTaskId ?? "None" }), _jsx(StatusLine, { label: "Started tasks", value: String(vendorTransportSummary.startedTasks) }), vendorRecentTransportEvents.length ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: "yellow", children: "Recent transport events" }), vendorRecentTransportEvents.map((event) => (_jsxs(Text, { color: "gray", children: [new Date(event.at).toLocaleString(), " | ", event.kind, " | ", event.title] }, `${event.kind}-${event.at}-${event.taskId ?? "no-task"}`)))] })) : (_jsx(Text, { color: "gray", children: "No transport events yet." }))] }), _jsxs(Section, { title: "Context Runtime Tree", children: [_jsx(StatusLine, { label: "Nodes", value: String(vendorTreeStore?.nodes.length ?? 0) }), _jsx(StatusLine, { label: "Pending drafts", value: String(vendorTreeStore?.stats.pendingDrafts ?? 0) }), _jsx(StatusLine, { label: "Pulled context", value: String(vendorTreeStore?.stats.pulledContext ?? 0) }), _jsx(StatusLine, { label: "Known fixes", value: String(vendorTreeStore?.stats.pulledFixes ?? 0) }), _jsx(StatusLine, { label: "Runtime transport", value: vendorTreeStore?.runtime.transportStatus ?? "idle" }), vendorRootTreeNodes.length ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: "yellow", children: "Top runtime nodes" }), vendorRootTreeNodes.map((node) => (_jsx(Text, { color: "gray", children: `${" ".repeat(node.depth)}- ${node.label}${node.detail ? ` | ${node.detail}` : ""}` }, node.id)))] })) : (_jsx(Text, { color: "gray", children: "The context tree has not been built yet." }))] }), _jsxs(Section, { title: "Session Continuity", children: [_jsx(StatusLine, { label: "Panel history", value: refreshState.session?.panelHistory.join(" -> ") || "None" }), _jsx(StatusLine, { label: "Recent providers", value: refreshState.session?.recentProviderIds.join(", ") || "None" }), _jsx(StatusLine, { label: "Recent models", value: refreshState.session?.recentModels.join(", ") || "None" }), _jsx(StatusLine, { label: "Recent hub items", value: refreshState.session?.recentHubSlugs.join(", ") || "None" }), _jsx(StatusLine, { label: "Recent connectors", value: refreshState.session?.recentConnectorSources.join(", ") || "None" })] }), _jsxs(Section, { title: "Vendor Bridge Runtime", children: [_jsx(StatusLine, { label: "Hydrated", value: vendorTransportSummary.isHydrated ? "Yes" : "No" }), _jsx(StatusLine, { label: "Generated", value: formatAgo(vendorTransportSummary.generatedAt) }), _jsx(StatusLine, { label: "Bridge queue", value: String(vendorTransportSummary.queueDepth) }), _jsx(StatusLine, { label: "Bridge tasks", value: String(vendorTransportSummary.recentTaskCount) }), _jsx(StatusLine, { label: "Bridge company", value: vendorContextSummary.companyName ?? "Unlinked company" }), _jsx(StatusLine, { label: "Bridge space", value: vendorContextSummary.spaceName ?? "Unlinked space" }), _jsx(StatusLine, { label: "Bridge provider", value: vendorContextSummary.providerName ?? "No provider" }), _jsx(StatusLine, { label: "Bridge model", value: vendorContextSummary.modelName ?? "No model" }), _jsx(StatusLine, { label: "Tree nodes", value: String(vendorContextSummary.treeNodes) }), _jsx(StatusLine, { label: "Runtime status", value: vendorContextSummary.runtimeStatus })] })] })] }));
1257
+ }
1258
+ return (_jsxs(PanelShell, { activePanel: activePanel, footer: footer, message: busyLabel ? `${busyLabel}` : message, selectedPanelIndex: selectedPanelIndex, children: [mainContent, inputMode ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: inputMode === "query" ? "Ask Context" : "Save a local Context note" }), _jsx(TextInput, { value: inputValue, onChange: setInputValue })] })) : null] }));
1259
+ }
1260
+ export async function runTui() {
1261
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
1262
+ console.log(chalk.yellow("The pane-based TUI needs a real interactive terminal."));
1263
+ console.log(chalk.gray('Run "umbrella-context" or "umbrella-context tui" inside a normal terminal window.'));
1264
+ return;
1265
+ }
1266
+ const app = render(_jsx(App, {}));
1267
+ await app.waitUntilExit();
1268
+ }
1269
+ export function tuiCommand(cli) {
1270
+ cli.command("tui", "Open the pane-based Umbrella Context terminal app").action(async () => {
1271
+ await runTui();
1272
+ });
1273
+ }