umbrella-context 0.1.2 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/catalog.d.ts +33 -0
- package/dist/commands/catalog.js +211 -0
- package/dist/commands/connect.js +14 -14
- package/dist/commands/connectors.d.ts +15 -0
- package/dist/commands/connectors.js +620 -0
- package/dist/commands/curate.d.ts +1 -0
- package/dist/commands/curate.js +39 -3
- package/dist/commands/debug.d.ts +2 -0
- package/dist/commands/debug.js +55 -0
- package/dist/commands/hub.d.ts +20 -0
- package/dist/commands/hub.js +429 -0
- package/dist/commands/interactive.d.ts +2 -0
- package/dist/commands/interactive.js +918 -62
- package/dist/commands/locations.d.ts +1 -0
- package/dist/commands/locations.js +15 -12
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +34 -0
- package/dist/commands/model.d.ts +11 -0
- package/dist/commands/model.js +211 -0
- package/dist/commands/providers.d.ts +17 -0
- package/dist/commands/providers.js +344 -0
- package/dist/commands/pull.js +10 -1
- package/dist/commands/push.js +18 -1
- package/dist/commands/restart.d.ts +2 -0
- package/dist/commands/restart.js +21 -0
- package/dist/commands/search.js +19 -1
- package/dist/commands/session.d.ts +2 -0
- package/dist/commands/session.js +128 -0
- package/dist/commands/space.d.ts +1 -0
- package/dist/commands/space.js +81 -63
- package/dist/commands/status.d.ts +25 -0
- package/dist/commands/status.js +104 -16
- package/dist/config.d.ts +23 -0
- package/dist/config.js +69 -0
- package/dist/index.js +26 -4
- package/dist/repo-state.d.ts +84 -0
- package/dist/repo-state.js +419 -3
- package/package.json +1 -1
|
@@ -1,96 +1,945 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import prompts from "prompts";
|
|
3
3
|
import { configManager } from "../config.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { pullCommandAction } from "./pull.js";
|
|
4
|
+
import { ensureSessionState, getConnectorRuns, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, recordSessionEvent, recordSessionCommand, recordSessionCuration, recordSessionQuery, setSessionPanel, resetSessionState, setSessionSummary, } from "../repo-state.js";
|
|
5
|
+
import { createContextSpace, getCompanyContextSummary, toContextSpaces } from "../umbrella.js";
|
|
6
|
+
import { parseInteractiveInput, renderCommandList, resolveCommand } from "./catalog.js";
|
|
8
7
|
import { searchCommandAction } from "./search.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
8
|
+
import { curateCommandAction } from "./curate.js";
|
|
9
|
+
import { spaceCommandAction } from "./space.js";
|
|
10
|
+
import { getProviderReadinessSummary, providersCommandAction } from "./providers.js";
|
|
11
|
+
import { getModelsForActiveProvider, modelCommandAction } from "./model.js";
|
|
12
|
+
import { hubCommandAction } from "./hub.js";
|
|
13
|
+
import { connectorsCommandAction } from "./connectors.js";
|
|
14
|
+
import { getStatusSnapshot } from "./status.js";
|
|
15
|
+
import { debugCommandAction } from "./debug.js";
|
|
16
|
+
import { logoutCommandAction } from "./logout.js";
|
|
17
|
+
import { restartCommandAction } from "./restart.js";
|
|
18
|
+
function divider() {
|
|
19
|
+
return chalk.gray("----------------------------------------------------------------");
|
|
20
|
+
}
|
|
21
|
+
function labelValue(label, value) {
|
|
22
|
+
return ` ${chalk.cyan(label.padEnd(18))} ${value}`;
|
|
23
|
+
}
|
|
24
|
+
function printWelcomeHeader(providerLabel, modelLabel) {
|
|
25
|
+
const timeLabel = new Date().toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
|
|
26
|
+
console.log(chalk.cyan("UMBRELLA CONTEXT") + chalk.gray(` ${timeLabel}`));
|
|
27
|
+
console.log(divider());
|
|
28
|
+
console.log(chalk.bold(" Command-first local Context for this repo"));
|
|
29
|
+
console.log(chalk.gray(` Runtime: ${providerLabel}${modelLabel ? ` (${modelLabel})` : ""}`));
|
|
30
|
+
console.log(chalk.gray(" Save to .um first. Push only when you want the team to share it."));
|
|
31
|
+
console.log(divider());
|
|
32
|
+
console.log(` ${chalk.yellow("/home")} Current repo and sync overview`);
|
|
33
|
+
console.log(` ${chalk.yellow("/query")} Ask Context a question`);
|
|
34
|
+
console.log(` ${chalk.yellow("/curate")} Save a reusable note locally`);
|
|
35
|
+
console.log(` ${chalk.yellow("/recent")} Show recent local activity`);
|
|
36
|
+
console.log(` ${chalk.yellow("/timeline")} Show the recent session event timeline`);
|
|
37
|
+
console.log(` ${chalk.yellow("/session")} Current terminal session summary`);
|
|
38
|
+
console.log(` ${chalk.yellow("/panel")} Show the active panel and focus`);
|
|
39
|
+
console.log(` ${chalk.yellow("/continue")} Re-open the last active panel`);
|
|
40
|
+
console.log(` ${chalk.yellow("/status")} Inspect sync, provider, and MCP state`);
|
|
41
|
+
console.log(` ${chalk.yellow("/restart")} Refresh this live terminal session`);
|
|
42
|
+
console.log(` ${chalk.yellow("/debug")} Show config paths and local runtime details`);
|
|
43
|
+
console.log(` ${chalk.yellow("/logout")} Disconnect this device from Umbrella Context`);
|
|
44
|
+
console.log(` ${chalk.yellow("/")} Full command list`);
|
|
45
|
+
console.log(divider());
|
|
46
|
+
console.log(` ${chalk.gray("Shortcuts:")} type plain text to query, or start with ${chalk.yellow("+")} to curate quickly.`);
|
|
47
|
+
console.log("");
|
|
48
|
+
}
|
|
49
|
+
async function printRuntimeSnapshot() {
|
|
11
50
|
const config = configManager.config;
|
|
12
|
-
if (!config)
|
|
13
|
-
|
|
51
|
+
if (!config)
|
|
52
|
+
return;
|
|
53
|
+
const activeProvider = configManager.providers.find((entry) => entry.id === config.activeProvider) ?? null;
|
|
54
|
+
const providerLabel = activeProvider ? activeProvider.name : "No provider connected";
|
|
55
|
+
const modelLabel = config.activeModel ?? "";
|
|
56
|
+
printWelcomeHeader(providerLabel, modelLabel);
|
|
57
|
+
}
|
|
58
|
+
async function printHomeView(config) {
|
|
59
|
+
await setSessionPanel("home", config.projectName);
|
|
60
|
+
const snapshot = await getStatusSnapshot();
|
|
61
|
+
const pending = await getPendingMemories();
|
|
62
|
+
const hubEntries = await getInstalledHubEntries();
|
|
63
|
+
const connectors = await getInstalledConnectors();
|
|
64
|
+
const connectorRuns = await getConnectorRuns();
|
|
65
|
+
const session = await ensureSessionState();
|
|
66
|
+
const latestConnectorRun = connectorRuns[0] ?? null;
|
|
67
|
+
console.log(chalk.bold(` ${config.companyName} / ${config.projectName}`));
|
|
68
|
+
console.log(divider());
|
|
69
|
+
console.log(chalk.bold(" Sync"));
|
|
70
|
+
console.log(labelValue("Pending drafts", String(snapshot.pendingCount)));
|
|
71
|
+
console.log(labelValue("Pulled context", String(snapshot.pulledContextCount)));
|
|
72
|
+
console.log(labelValue("Known fixes", String(snapshot.pulledFixCount)));
|
|
73
|
+
console.log("");
|
|
74
|
+
console.log(chalk.bold(" Repo"));
|
|
75
|
+
console.log(labelValue("Hub installs", String(hubEntries.length)));
|
|
76
|
+
console.log(labelValue("Connectors", String(connectors.length)));
|
|
77
|
+
console.log(labelValue("Last connector run", latestConnectorRun ? `${latestConnectorRun.connectorName} (${latestConnectorRun.status})` : "Never"));
|
|
78
|
+
console.log("");
|
|
79
|
+
console.log(chalk.bold(" Runtime"));
|
|
80
|
+
console.log(labelValue("Provider", snapshot.providerLabel));
|
|
81
|
+
console.log(labelValue("Model", snapshot.modelLabel));
|
|
82
|
+
console.log(labelValue("Repo MCP", snapshot.mcpConfigured ? "Ready" : "Needs setup"));
|
|
83
|
+
console.log(labelValue("Current panel", session.currentPanel ?? "Home"));
|
|
84
|
+
console.log(labelValue("Current focus", session.currentFocus ?? "None"));
|
|
85
|
+
console.log(labelValue("Latest event", session.events?.[0]?.title ?? "Nothing yet"));
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log(chalk.bold(" Suggested next steps"));
|
|
88
|
+
if (snapshot.nextSteps.length > 0) {
|
|
89
|
+
snapshot.nextSteps.forEach((step) => console.log(` - ${step}`));
|
|
90
|
+
}
|
|
91
|
+
else if (pending.length > 0) {
|
|
92
|
+
console.log(` - ${chalk.yellow("/push")} to share ${pending.length} local draft${pending.length === 1 ? "" : "s"}`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.log(` - ${chalk.yellow('/curate "What you learned"')} to stage a new local note`);
|
|
96
|
+
console.log(` - ${chalk.yellow("/query What do we already know about this?")} to search local and server memory`);
|
|
97
|
+
}
|
|
98
|
+
console.log(` - ${chalk.yellow("/recent")} to inspect the latest repo activity`);
|
|
99
|
+
console.log("");
|
|
100
|
+
}
|
|
101
|
+
function printSessionHint(title, lines) {
|
|
102
|
+
console.log("");
|
|
103
|
+
console.log(chalk.bold(` ${title}`));
|
|
104
|
+
lines.forEach((line) => console.log(` - ${line}`));
|
|
105
|
+
console.log("");
|
|
106
|
+
}
|
|
107
|
+
function printRecentSection(title, items, render) {
|
|
108
|
+
console.log(chalk.bold(` ${title}`));
|
|
109
|
+
if (items.length === 0) {
|
|
110
|
+
console.log(chalk.gray(" Nothing recent yet."));
|
|
111
|
+
console.log("");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
items.forEach((item, index) => {
|
|
115
|
+
console.log(render(item, index));
|
|
116
|
+
});
|
|
117
|
+
console.log("");
|
|
118
|
+
}
|
|
119
|
+
async function printSessionView() {
|
|
120
|
+
await setSessionPanel("session", "summary");
|
|
121
|
+
const session = await ensureSessionState();
|
|
122
|
+
console.log("");
|
|
123
|
+
console.log(chalk.bold(" Current Session"));
|
|
124
|
+
console.log(divider());
|
|
125
|
+
console.log(labelValue("Session ID", session.id));
|
|
126
|
+
console.log(labelValue("Started", session.startedAt));
|
|
127
|
+
console.log(labelValue("Updated", session.updatedAt));
|
|
128
|
+
console.log(labelValue("Commands", String(session.commandHistory.length)));
|
|
129
|
+
console.log(labelValue("Queries", String(session.recentQueries.length)));
|
|
130
|
+
console.log(labelValue("Curations", String(session.recentCurations.length)));
|
|
131
|
+
console.log(labelValue("Last summary", session.lastSummary ?? "Nothing recorded yet"));
|
|
132
|
+
console.log(labelValue("Current panel", session.currentPanel ?? "None"));
|
|
133
|
+
console.log(labelValue("Current focus", session.currentFocus ?? "None"));
|
|
134
|
+
console.log(labelValue("Events", String(session.events?.length ?? 0)));
|
|
135
|
+
console.log("");
|
|
136
|
+
if (session.panelHistory.length > 0) {
|
|
137
|
+
console.log(chalk.bold(" Panel history"));
|
|
138
|
+
session.panelHistory.slice(0, 5).forEach((entry, index) => console.log(` ${index + 1}. ${entry}`));
|
|
139
|
+
console.log("");
|
|
140
|
+
}
|
|
141
|
+
if (session.recentQueries.length > 0) {
|
|
142
|
+
console.log(chalk.bold(" Recent Queries"));
|
|
143
|
+
session.recentQueries.slice(0, 3).forEach((entry, index) => console.log(` ${index + 1}. ${entry}`));
|
|
144
|
+
console.log("");
|
|
145
|
+
}
|
|
146
|
+
if (session.recentCurations.length > 0) {
|
|
147
|
+
console.log(chalk.bold(" Recent Curations"));
|
|
148
|
+
session.recentCurations.slice(0, 3).forEach((entry, index) => console.log(` ${index + 1}. ${entry}`));
|
|
149
|
+
console.log("");
|
|
150
|
+
}
|
|
151
|
+
if (session.recentProviderIds.length > 0 || session.recentModels.length > 0) {
|
|
152
|
+
console.log(chalk.bold(" Runtime recents"));
|
|
153
|
+
if (session.recentProviderIds.length > 0) {
|
|
154
|
+
const names = session.recentProviderIds
|
|
155
|
+
.map((id) => configManager.providers.find((entry) => entry.id === id)?.name ?? id)
|
|
156
|
+
.join(", ");
|
|
157
|
+
console.log(` Providers: ${names}`);
|
|
158
|
+
}
|
|
159
|
+
if (session.recentModels.length > 0) {
|
|
160
|
+
console.log(` Models: ${session.recentModels.join(", ")}`);
|
|
161
|
+
}
|
|
162
|
+
console.log("");
|
|
163
|
+
}
|
|
164
|
+
if (session.recentHubSlugs.length > 0 || session.recentConnectorSources.length > 0) {
|
|
165
|
+
console.log(chalk.bold(" Ecosystem recents"));
|
|
166
|
+
if (session.recentHubSlugs.length > 0) {
|
|
167
|
+
console.log(` Hub: ${session.recentHubSlugs.join(", ")}`);
|
|
168
|
+
}
|
|
169
|
+
if (session.recentConnectorSources.length > 0) {
|
|
170
|
+
console.log(` Connectors: ${session.recentConnectorSources.join(", ")}`);
|
|
171
|
+
}
|
|
172
|
+
console.log("");
|
|
173
|
+
}
|
|
174
|
+
if ((session.events?.length ?? 0) > 0) {
|
|
175
|
+
console.log(chalk.bold(" Recent timeline"));
|
|
176
|
+
session.events.slice(0, 5).forEach((entry, index) => {
|
|
177
|
+
console.log(` ${index + 1}. [${entry.kind}] ${entry.title}`);
|
|
178
|
+
if (entry.detail) {
|
|
179
|
+
console.log(chalk.gray(` ${entry.detail}`));
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
console.log("");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function printProviderOverview() {
|
|
186
|
+
const readiness = getProviderReadinessSummary();
|
|
187
|
+
const providerCount = configManager.providers.length;
|
|
188
|
+
console.log("");
|
|
189
|
+
console.log(chalk.bold(" Provider Runtime"));
|
|
190
|
+
console.log(divider());
|
|
191
|
+
console.log(labelValue("Connected", String(providerCount)));
|
|
192
|
+
console.log(labelValue("Active provider", readiness.activeProvider?.name ?? "Not connected"));
|
|
193
|
+
console.log(labelValue("Active model", readiness.activeModel ?? "Not selected"));
|
|
194
|
+
console.log(labelValue("Runtime state", !readiness.providerReady
|
|
195
|
+
? "Needs provider"
|
|
196
|
+
: !readiness.modelReady
|
|
197
|
+
? "Needs model"
|
|
198
|
+
: "Ready"));
|
|
199
|
+
console.log("");
|
|
200
|
+
}
|
|
201
|
+
async function printModelOverview() {
|
|
202
|
+
const readiness = getProviderReadinessSummary();
|
|
203
|
+
const models = getModelsForActiveProvider();
|
|
204
|
+
console.log("");
|
|
205
|
+
console.log(chalk.bold(" Model Runtime"));
|
|
206
|
+
console.log(divider());
|
|
207
|
+
console.log(labelValue("Provider", readiness.activeProvider?.name ?? "Not connected"));
|
|
208
|
+
console.log(labelValue("Active model", readiness.activeModel ?? "Not selected"));
|
|
209
|
+
console.log(labelValue("Available models", String(models.length)));
|
|
210
|
+
if (models.length > 0) {
|
|
211
|
+
console.log("");
|
|
212
|
+
console.log(chalk.bold(" Available Models"));
|
|
213
|
+
models.forEach((model, index) => {
|
|
214
|
+
const active = readiness.activeModel === model ? " (active)" : "";
|
|
215
|
+
console.log(` ${index + 1}. ${model}${active}`);
|
|
216
|
+
});
|
|
217
|
+
console.log("");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function printHubOverview() {
|
|
221
|
+
const installed = await getInstalledHubEntries();
|
|
222
|
+
console.log("");
|
|
223
|
+
console.log(chalk.bold(" Hub Overview"));
|
|
224
|
+
console.log(divider());
|
|
225
|
+
console.log(labelValue("Catalog entries", "3"));
|
|
226
|
+
console.log(labelValue("Installed", String(installed.length)));
|
|
227
|
+
if (installed.length > 0) {
|
|
228
|
+
console.log("");
|
|
229
|
+
console.log(chalk.bold(" Installed Bundles"));
|
|
230
|
+
installed.slice(0, 5).forEach((entry, index) => {
|
|
231
|
+
console.log(` ${index + 1}. ${entry.name} [${entry.type}]`);
|
|
232
|
+
});
|
|
233
|
+
console.log("");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function printConnectorOverview() {
|
|
237
|
+
const connectors = await getInstalledConnectors();
|
|
238
|
+
const connectorRuns = await getConnectorRuns();
|
|
239
|
+
const latest = connectorRuns[0] ?? null;
|
|
240
|
+
console.log("");
|
|
241
|
+
console.log(chalk.bold(" Connector Overview"));
|
|
242
|
+
console.log(divider());
|
|
243
|
+
console.log(labelValue("Installed", String(connectors.length)));
|
|
244
|
+
console.log(labelValue("Last run", latest ? `${latest.connectorName} (${latest.status})` : "Never"));
|
|
245
|
+
if (connectors.length > 0) {
|
|
246
|
+
console.log("");
|
|
247
|
+
console.log(chalk.bold(" Installed Connectors"));
|
|
248
|
+
connectors.forEach((entry, index) => {
|
|
249
|
+
console.log(` ${index + 1}. ${entry.name} [${entry.type}]`);
|
|
250
|
+
});
|
|
251
|
+
console.log("");
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async function printCurrentPanelView() {
|
|
255
|
+
const session = await ensureSessionState();
|
|
256
|
+
const latestPanelEvent = (session.events ?? []).find((entry) => entry.panel === session.currentPanel);
|
|
257
|
+
console.log("");
|
|
258
|
+
console.log(chalk.bold(" Live Panel"));
|
|
259
|
+
console.log(divider());
|
|
260
|
+
console.log(labelValue("Panel", session.currentPanel ?? "None"));
|
|
261
|
+
console.log(labelValue("Focus", session.currentFocus ?? "None"));
|
|
262
|
+
console.log(labelValue("Last panel event", latestPanelEvent?.title ?? "Nothing panel-specific yet"));
|
|
263
|
+
if (session.panelHistory.length > 0) {
|
|
264
|
+
console.log("");
|
|
265
|
+
console.log(chalk.bold(" Recent panels"));
|
|
266
|
+
session.panelHistory.slice(0, 5).forEach((entry, index) => console.log(` ${index + 1}. ${entry}`));
|
|
267
|
+
}
|
|
268
|
+
console.log("");
|
|
269
|
+
}
|
|
270
|
+
async function printTimelineView() {
|
|
271
|
+
await setSessionPanel("timeline", "events");
|
|
272
|
+
const session = await ensureSessionState();
|
|
273
|
+
console.log("");
|
|
274
|
+
console.log(chalk.bold(" Session Timeline"));
|
|
275
|
+
console.log(divider());
|
|
276
|
+
if ((session.events?.length ?? 0) === 0) {
|
|
277
|
+
console.log(chalk.gray(" No session events yet."));
|
|
278
|
+
console.log("");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
session.events.slice(0, 12).forEach((entry, index) => {
|
|
282
|
+
const badge = entry.status === "success" ? chalk.green(`[${entry.kind}]`)
|
|
283
|
+
: entry.status === "warning" ? chalk.yellow(`[${entry.kind}]`)
|
|
284
|
+
: entry.status === "failure" ? chalk.red(`[${entry.kind}]`)
|
|
285
|
+
: chalk.cyan(`[${entry.kind}]`);
|
|
286
|
+
const focusSuffix = entry.focus ? ` -> ${entry.focus}` : "";
|
|
287
|
+
console.log(` ${index + 1}. ${badge} ${entry.title}${focusSuffix}`);
|
|
288
|
+
console.log(chalk.gray(` ${new Date(entry.at).toLocaleString()}`));
|
|
289
|
+
if (entry.detail) {
|
|
290
|
+
console.log(chalk.gray(` ${entry.detail}`));
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
console.log("");
|
|
294
|
+
}
|
|
295
|
+
async function continueLastPanel(config) {
|
|
296
|
+
const session = await ensureSessionState();
|
|
297
|
+
const panel = session.currentPanel;
|
|
298
|
+
const focus = session.currentFocus ?? undefined;
|
|
299
|
+
if (!panel) {
|
|
300
|
+
console.log(chalk.yellow("\n No current panel to continue yet."));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (panel === "home") {
|
|
304
|
+
await printHomeView(config);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (panel === "providers") {
|
|
308
|
+
if (focus) {
|
|
309
|
+
await providersCommandAction("inspect", focus);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
await openProvidersMenu();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (panel === "model") {
|
|
316
|
+
if (focus) {
|
|
317
|
+
await modelCommandAction("inspect", focus);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
await openModelMenu();
|
|
14
321
|
return;
|
|
15
322
|
}
|
|
16
|
-
|
|
17
|
-
|
|
323
|
+
if (panel === "hub") {
|
|
324
|
+
if (focus && !focus.startsWith("registry:") && focus !== "list" && focus !== "installed") {
|
|
325
|
+
await hubCommandAction("inspect", focus);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (focus?.startsWith("registry:")) {
|
|
329
|
+
await hubCommandAction("registry", "list");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
await openHubMenu();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (panel === "connectors") {
|
|
336
|
+
if (focus && focus !== "list") {
|
|
337
|
+
await connectorsCommandAction("inspect", focus);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
await openConnectorsMenu();
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (panel === "space" || panel === "spaces" || panel === "projects") {
|
|
344
|
+
await openSpaceMenu(config);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (panel === "session") {
|
|
348
|
+
await printSessionView();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (panel === "recent") {
|
|
352
|
+
await handleRecent(config);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (panel === "timeline") {
|
|
356
|
+
await printTimelineView();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (panel === "status") {
|
|
360
|
+
await import("./status.js").then((mod) => mod.statusCommandAction());
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
console.log(chalk.yellow(`\n No resume handler yet for panel "${panel}".`));
|
|
364
|
+
}
|
|
365
|
+
async function openSpaceMenu(config) {
|
|
18
366
|
while (true) {
|
|
19
|
-
|
|
20
|
-
|
|
367
|
+
await setSessionPanel("space", config.projectName);
|
|
368
|
+
const summary = await getCompanyContextSummary(config.umbrellaUrl, config.companyId);
|
|
369
|
+
const spaces = toContextSpaces(summary);
|
|
370
|
+
const answer = await prompts({
|
|
371
|
+
type: "select",
|
|
21
372
|
name: "value",
|
|
22
|
-
message:
|
|
373
|
+
message: `Context spaces for ${config.companyName}`,
|
|
374
|
+
choices: [
|
|
375
|
+
{ title: "Overview", description: "See the current company and space summary", value: "overview" },
|
|
376
|
+
{ title: "List spaces", description: "Show all spaces for this company", value: "list" },
|
|
377
|
+
{ title: "Switch space", description: "Move this repo to another space", value: "switch" },
|
|
378
|
+
{ title: "Create space", description: "Add a new team lane like Growth or Engineering", value: "create" },
|
|
379
|
+
{ title: "Back", value: "back" },
|
|
380
|
+
],
|
|
23
381
|
});
|
|
24
|
-
if (!
|
|
382
|
+
if (!answer.value || answer.value === "back")
|
|
383
|
+
return;
|
|
384
|
+
if (answer.value === "overview") {
|
|
385
|
+
await handleSpaces(config);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (answer.value === "list") {
|
|
389
|
+
await spaceCommandAction("list");
|
|
25
390
|
continue;
|
|
26
|
-
const parts = input.value.trim().split(/\s+/);
|
|
27
|
-
const cmd = parts[0].toLowerCase();
|
|
28
|
-
const rest = parts.slice(1).join(" ");
|
|
29
|
-
if (cmd === "exit" || cmd === "quit")
|
|
30
|
-
break;
|
|
31
|
-
if (cmd === "curate") {
|
|
32
|
-
await curateCommandAction(rest);
|
|
33
391
|
}
|
|
34
|
-
|
|
35
|
-
|
|
392
|
+
if (answer.value === "switch") {
|
|
393
|
+
if (spaces.length === 0) {
|
|
394
|
+
console.log(chalk.yellow("\n No spaces found for this company."));
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
await spaceCommandAction("switch");
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (answer.value === "create") {
|
|
401
|
+
const createAnswer = await prompts({
|
|
402
|
+
type: "text",
|
|
403
|
+
name: "name",
|
|
404
|
+
message: "Name the new context space",
|
|
405
|
+
validate: (value) => (value.trim().length > 1 ? true : "Use at least 2 characters."),
|
|
406
|
+
});
|
|
407
|
+
const nextName = String(createAnswer.name ?? "").trim();
|
|
408
|
+
if (!nextName) {
|
|
409
|
+
console.log(chalk.yellow("\n No space name provided."));
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
await createContextSpace(config.umbrellaUrl, config.companyId, nextName);
|
|
413
|
+
await recordSessionEvent({
|
|
414
|
+
kind: "space",
|
|
415
|
+
title: `Created space ${nextName}`,
|
|
416
|
+
detail: `A new Context space was created inside ${config.companyName}.`,
|
|
417
|
+
panel: "space",
|
|
418
|
+
focus: nextName,
|
|
419
|
+
status: "success",
|
|
420
|
+
});
|
|
421
|
+
console.log(chalk.green(`\n Created ${nextName}.`));
|
|
422
|
+
const switchNow = await prompts({
|
|
423
|
+
type: "confirm",
|
|
424
|
+
name: "value",
|
|
425
|
+
message: `Switch this repo to ${nextName} now?`,
|
|
426
|
+
initial: true,
|
|
427
|
+
});
|
|
428
|
+
if (switchNow.value) {
|
|
429
|
+
await spaceCommandAction("switch", nextName);
|
|
430
|
+
}
|
|
431
|
+
continue;
|
|
36
432
|
}
|
|
37
|
-
|
|
38
|
-
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
async function openProvidersMenu() {
|
|
436
|
+
while (true) {
|
|
437
|
+
const readiness = getProviderReadinessSummary();
|
|
438
|
+
const activeProvider = readiness.activeProvider;
|
|
439
|
+
await setSessionPanel("providers", activeProvider?.id ?? null);
|
|
440
|
+
const answer = await prompts({
|
|
441
|
+
type: "select",
|
|
442
|
+
name: "value",
|
|
443
|
+
message: activeProvider ? `Provider flow (active: ${activeProvider.name})` : "Provider flow",
|
|
444
|
+
choices: [
|
|
445
|
+
{ title: "Overview", description: "See provider runtime readiness for this device", value: "overview" },
|
|
446
|
+
{ title: "List providers", description: "See providers already saved on this device", value: "list" },
|
|
447
|
+
{ title: "Inspect provider", description: "Read the details and next steps for one provider", value: "inspect" },
|
|
448
|
+
{ title: "Inspect recent provider", description: "Open the provider you touched most recently", value: "recent" },
|
|
449
|
+
{ title: "Test runtime", description: "Check whether the active provider is actually ready to run", value: "test" },
|
|
450
|
+
{ title: "Connect provider", description: "Add a new provider API key on this device", value: "connect" },
|
|
451
|
+
{ title: "Switch provider", description: "Choose the active provider", value: "switch" },
|
|
452
|
+
{ title: "Disconnect provider", description: "Remove a saved provider from this device", value: "disconnect" },
|
|
453
|
+
{ title: "Choose model", description: "Pick the active model for the current provider", value: "model" },
|
|
454
|
+
{ title: "Back", value: "back" },
|
|
455
|
+
],
|
|
456
|
+
});
|
|
457
|
+
if (!answer.value || answer.value === "back")
|
|
458
|
+
return;
|
|
459
|
+
if (answer.value === "overview") {
|
|
460
|
+
await printProviderOverview();
|
|
461
|
+
continue;
|
|
39
462
|
}
|
|
40
|
-
|
|
41
|
-
await
|
|
463
|
+
if (answer.value === "model") {
|
|
464
|
+
await openModelMenu();
|
|
465
|
+
continue;
|
|
42
466
|
}
|
|
43
|
-
|
|
44
|
-
|
|
467
|
+
const result = await providersCommandAction(answer.value);
|
|
468
|
+
if (result?.activeProvider) {
|
|
469
|
+
const lines = [`Active provider: ${result.activeProvider.name}`];
|
|
470
|
+
if (result.activeProvider.baseUrl) {
|
|
471
|
+
lines.push(`Base URL: ${result.activeProvider.baseUrl}`);
|
|
472
|
+
}
|
|
473
|
+
lines.push(result.needsModelSelection
|
|
474
|
+
? "No model is selected yet, so the runtime is only partially ready."
|
|
475
|
+
: "Provider and model are both ready for terminal work.");
|
|
476
|
+
printSessionHint("Provider Session Summary", lines);
|
|
45
477
|
}
|
|
46
|
-
|
|
47
|
-
await
|
|
478
|
+
if ((answer.value === "connect" || answer.value === "switch") && result?.needsModelSelection) {
|
|
479
|
+
const chooseModel = await prompts({
|
|
480
|
+
type: "confirm",
|
|
481
|
+
name: "value",
|
|
482
|
+
message: "Choose a model now?",
|
|
483
|
+
initial: true,
|
|
484
|
+
});
|
|
485
|
+
if (chooseModel.value) {
|
|
486
|
+
const modelResult = await modelCommandAction("switch");
|
|
487
|
+
if (modelResult?.activeModel) {
|
|
488
|
+
printSessionHint("Runtime Ready", [
|
|
489
|
+
`Provider: ${modelResult.providerName ?? "Unknown"}`,
|
|
490
|
+
`Model: ${modelResult.activeModel}`,
|
|
491
|
+
'You can now type a plain question or use "/query ..." directly.',
|
|
492
|
+
]);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
48
495
|
}
|
|
49
|
-
|
|
50
|
-
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async function openModelMenu() {
|
|
499
|
+
while (true) {
|
|
500
|
+
const currentConfig = configManager.config;
|
|
501
|
+
const provider = configManager.providers.find((entry) => entry.id === currentConfig?.activeProvider) ?? null;
|
|
502
|
+
const models = getModelsForActiveProvider();
|
|
503
|
+
await setSessionPanel("model", currentConfig?.activeModel ?? provider?.id ?? null);
|
|
504
|
+
const answer = await prompts({
|
|
505
|
+
type: "select",
|
|
506
|
+
name: "value",
|
|
507
|
+
message: provider ? `Model flow (${provider.name})` : "Model flow",
|
|
508
|
+
choices: [
|
|
509
|
+
{ title: "Overview", description: "See active provider, model, and available choices", value: "overview" },
|
|
510
|
+
{ title: "List models", description: `See models for the active provider (${models.length})`, value: "list" },
|
|
511
|
+
{ title: "Inspect model", description: "Read details and the next step for a specific model", value: "inspect" },
|
|
512
|
+
{ title: "Inspect recent model", description: "Open the model you touched most recently", value: "recent" },
|
|
513
|
+
{ title: "Show readiness", description: "Check whether the model runtime is fully ready", value: "ready" },
|
|
514
|
+
{ title: "Switch model", description: "Choose the active model", value: "switch" },
|
|
515
|
+
{ title: "Back", value: "back" },
|
|
516
|
+
],
|
|
517
|
+
});
|
|
518
|
+
if (!answer.value || answer.value === "back")
|
|
519
|
+
return;
|
|
520
|
+
if (answer.value === "overview") {
|
|
521
|
+
await printModelOverview();
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const result = await modelCommandAction(answer.value);
|
|
525
|
+
if (result?.action === "switch" && result.activeModel) {
|
|
526
|
+
printSessionHint("Model Session Summary", [
|
|
527
|
+
`Provider: ${result.providerName ?? "Unknown"}`,
|
|
528
|
+
`Active model: ${result.activeModel}`,
|
|
529
|
+
'The runtime is ready. Try a plain question or run "/query ...".',
|
|
530
|
+
]);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function openHubMenu() {
|
|
535
|
+
while (true) {
|
|
536
|
+
const session = await ensureSessionState();
|
|
537
|
+
await setSessionPanel("hub", session.currentFocus);
|
|
538
|
+
const answer = await prompts({
|
|
539
|
+
type: "select",
|
|
540
|
+
name: "value",
|
|
541
|
+
message: "Hub flow",
|
|
542
|
+
choices: [
|
|
543
|
+
{ title: "Overview", description: "See installed bundles and the local hub state", value: "overview" },
|
|
544
|
+
{ title: "Browse catalog", description: "See available bundles, skills, and connector packs", value: "list" },
|
|
545
|
+
{ title: "Install hub entry", description: "Install a bundle into this repo", value: "install" },
|
|
546
|
+
{ title: "Inspect bundle", description: "Pick a hub entry and read what it includes", value: "inspect" },
|
|
547
|
+
{ title: "Inspect recent bundle", description: "Open the last hub entry you touched in this session", value: "recent" },
|
|
548
|
+
{ title: "Installed hub entries", description: "See what this repo already has", value: "installed" },
|
|
549
|
+
{ title: "Registries", description: "See configured hub registries", value: "registry" },
|
|
550
|
+
{ title: "Back", value: "back" },
|
|
551
|
+
],
|
|
552
|
+
});
|
|
553
|
+
if (!answer.value || answer.value === "back")
|
|
554
|
+
return;
|
|
555
|
+
if (answer.value === "overview") {
|
|
556
|
+
await printHubOverview();
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (answer.value === "registry") {
|
|
560
|
+
await hubCommandAction("registry", "list");
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (answer.value === "recent") {
|
|
564
|
+
const recent = session.recentHubSlugs[0];
|
|
565
|
+
if (!recent) {
|
|
566
|
+
console.log(chalk.yellow("\n No recent hub entry in this session yet."));
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
await hubCommandAction("inspect", recent);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (answer.value === "inspect") {
|
|
573
|
+
const inspectAnswer = await prompts({
|
|
574
|
+
type: "select",
|
|
575
|
+
name: "value",
|
|
576
|
+
message: "Choose a hub entry to inspect",
|
|
577
|
+
choices: [
|
|
578
|
+
{ title: "Quick Ping Growth Bundle", value: "quick-ping-growth-bundle" },
|
|
579
|
+
{ title: "Engineering Debug Skill Pack", value: "engineering-debug-skill-pack" },
|
|
580
|
+
{ title: "Umbrella Context MCP Connector", value: "umbrella-context-mcp-connector" },
|
|
581
|
+
],
|
|
582
|
+
});
|
|
583
|
+
if (!inspectAnswer.value) {
|
|
584
|
+
console.log(chalk.yellow("\n No hub entry selected."));
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
await hubCommandAction("inspect", inspectAnswer.value);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
const result = await hubCommandAction(answer.value);
|
|
591
|
+
if (result?.action === "install" && result.installedEntry) {
|
|
592
|
+
const lines = [`Installed: ${result.installedEntry.name}`];
|
|
593
|
+
if (result.assetPath)
|
|
594
|
+
lines.push(`Asset file: ${result.assetPath}`);
|
|
595
|
+
if (result.filePaths && result.filePaths.length > 0) {
|
|
596
|
+
lines.push(`${result.filePaths.length} repo file${result.filePaths.length === 1 ? "" : "s"} added or refreshed`);
|
|
597
|
+
}
|
|
598
|
+
if (result.recommendedConnector) {
|
|
599
|
+
lines.push(`Recommended connector: ${result.recommendedConnector}`);
|
|
600
|
+
}
|
|
601
|
+
printSessionHint("Hub Install Summary", lines);
|
|
602
|
+
const next = await prompts({
|
|
603
|
+
type: "confirm",
|
|
604
|
+
name: "value",
|
|
605
|
+
message: "Open connectors now to run anything that bundle installed?",
|
|
606
|
+
initial: true,
|
|
607
|
+
});
|
|
608
|
+
if (next.value) {
|
|
609
|
+
await openConnectorsMenu();
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
async function openConnectorsMenu() {
|
|
615
|
+
while (true) {
|
|
616
|
+
const session = await ensureSessionState();
|
|
617
|
+
await setSessionPanel("connectors", session.currentFocus);
|
|
618
|
+
const answer = await prompts({
|
|
619
|
+
type: "select",
|
|
620
|
+
name: "value",
|
|
621
|
+
message: "Connector flow",
|
|
622
|
+
choices: [
|
|
623
|
+
{ title: "Overview", description: "See installed connectors and the latest run state", value: "overview" },
|
|
624
|
+
{ title: "List connectors", description: "See repo connectors already installed", value: "list" },
|
|
625
|
+
{ title: "Inspect connector", description: "Read connector details and recommended next steps", value: "inspect" },
|
|
626
|
+
{ title: "Inspect recent connector", description: "Open the connector you touched most recently", value: "recent" },
|
|
627
|
+
{ title: "View outputs", description: "See connector-written files and the latest run summary", value: "outputs" },
|
|
628
|
+
{ title: "Install connector", description: "Add a connector into this repo", value: "install" },
|
|
629
|
+
{ title: "Run connector", description: "Execute a connector inside this repo", value: "run" },
|
|
630
|
+
{ title: "Back", value: "back" },
|
|
631
|
+
],
|
|
632
|
+
});
|
|
633
|
+
if (!answer.value || answer.value === "back")
|
|
634
|
+
return;
|
|
635
|
+
if (answer.value === "overview") {
|
|
636
|
+
await printConnectorOverview();
|
|
637
|
+
continue;
|
|
51
638
|
}
|
|
52
|
-
|
|
53
|
-
|
|
639
|
+
let target;
|
|
640
|
+
if (answer.value === "run" || answer.value === "inspect" || answer.value === "outputs") {
|
|
641
|
+
const installed = await getInstalledConnectors();
|
|
642
|
+
if (installed.length === 0) {
|
|
643
|
+
console.log(chalk.yellow("\n No connectors installed in this repo yet."));
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
const runAnswer = await prompts({
|
|
647
|
+
type: "select",
|
|
648
|
+
name: "value",
|
|
649
|
+
message: answer.value === "outputs" ? "Choose a connector to inspect outputs for" : "Choose a connector to run",
|
|
650
|
+
choices: installed.map((entry) => ({
|
|
651
|
+
title: `${entry.name} (${entry.type})`,
|
|
652
|
+
value: entry.source,
|
|
653
|
+
})),
|
|
654
|
+
});
|
|
655
|
+
target = runAnswer.value;
|
|
656
|
+
if (!target) {
|
|
657
|
+
console.log(chalk.yellow("\n No connector selected."));
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
54
660
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
661
|
+
const result = await connectorsCommandAction(answer.value, target);
|
|
662
|
+
if (result?.action === "install" && result.connectorName) {
|
|
663
|
+
const lines = [`Connector: ${result.connectorName}`];
|
|
664
|
+
if (result.repoConfigPath)
|
|
665
|
+
lines.push(`Repo MCP wiring: ${result.repoConfigPath}`);
|
|
666
|
+
if (result.filePaths && result.filePaths.length > 0) {
|
|
667
|
+
lines.push(`${result.filePaths.length} connector file${result.filePaths.length === 1 ? "" : "s"} added or refreshed`);
|
|
668
|
+
}
|
|
669
|
+
lines.push('Running the connector will verify repo wiring and save a local run report in ".um".');
|
|
670
|
+
printSessionHint("Connector Install Summary", lines);
|
|
671
|
+
const runNow = await prompts({
|
|
672
|
+
type: "confirm",
|
|
673
|
+
name: "value",
|
|
674
|
+
message: "Run a connector now?",
|
|
675
|
+
initial: true,
|
|
676
|
+
});
|
|
677
|
+
if (runNow.value) {
|
|
678
|
+
const runResult = await connectorsCommandAction("run");
|
|
679
|
+
if (runResult?.connectorName) {
|
|
680
|
+
const lines = [
|
|
681
|
+
`Connector: ${runResult.connectorName}`,
|
|
682
|
+
`Status: ${runResult.status ?? "unknown"}`,
|
|
683
|
+
];
|
|
684
|
+
if (runResult.summary)
|
|
685
|
+
lines.push(runResult.summary);
|
|
686
|
+
if (runResult.stagedMemoryId)
|
|
687
|
+
lines.push(`Staged local draft: ${runResult.stagedMemoryId}`);
|
|
688
|
+
if (runResult.reportPath)
|
|
689
|
+
lines.push(`Run report: ${runResult.reportPath}`);
|
|
690
|
+
if (runResult.status === "success")
|
|
691
|
+
lines.push('Next move: run "/status" to review the updated repo state.');
|
|
692
|
+
printSessionHint("Connector Run Summary", lines);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
67
695
|
}
|
|
68
|
-
else {
|
|
69
|
-
|
|
696
|
+
else if (result?.action === "run" && result.connectorName) {
|
|
697
|
+
const lines = [
|
|
698
|
+
`Connector: ${result.connectorName}`,
|
|
699
|
+
`Status: ${result.status ?? "unknown"}`,
|
|
700
|
+
];
|
|
701
|
+
if (result.summary)
|
|
702
|
+
lines.push(result.summary);
|
|
703
|
+
if (result.stagedMemoryId)
|
|
704
|
+
lines.push(`Staged local draft: ${result.stagedMemoryId}`);
|
|
705
|
+
if (result.reportPath)
|
|
706
|
+
lines.push(`Run report: ${result.reportPath}`);
|
|
707
|
+
printSessionHint("Connector Run Summary", lines);
|
|
70
708
|
}
|
|
71
709
|
}
|
|
72
710
|
}
|
|
711
|
+
async function maybeHandleNativeMenu(command, args, config) {
|
|
712
|
+
if (args.length > 0)
|
|
713
|
+
return false;
|
|
714
|
+
if (command === "space" || command === "spaces" || command === "projects") {
|
|
715
|
+
await openSpaceMenu(config);
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
if (command === "providers") {
|
|
719
|
+
await openProvidersMenu();
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
if (command === "model") {
|
|
723
|
+
await openModelMenu();
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
if (command === "hub") {
|
|
727
|
+
await openHubMenu();
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
if (command === "connectors") {
|
|
731
|
+
await openConnectorsMenu();
|
|
732
|
+
return true;
|
|
733
|
+
}
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
export async function interactiveCommand(_args) {
|
|
737
|
+
if (!configManager.config) {
|
|
738
|
+
console.log(chalk.red("Not configured. Run: umbrella-context setup"));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const commandHistory = [];
|
|
742
|
+
await ensureSessionState();
|
|
743
|
+
await setSessionPanel("home", configManager.config.projectName);
|
|
744
|
+
await printRuntimeSnapshot();
|
|
745
|
+
await printHomeView(configManager.config);
|
|
746
|
+
console.log(chalk.gray(" Type / to see commands. Use plain text to query or start with + to curate.\n"));
|
|
747
|
+
while (true) {
|
|
748
|
+
const currentConfig = configManager.config;
|
|
749
|
+
if (!currentConfig) {
|
|
750
|
+
console.log(chalk.red("Config was cleared. Run umbrella-context setup again."));
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
const input = await prompts({
|
|
754
|
+
type: "text",
|
|
755
|
+
name: "value",
|
|
756
|
+
message: chalk.cyan(`${currentConfig.companyName.toLowerCase().replace(/\s+/g, "-")}:${currentConfig.projectName.toLowerCase()}`),
|
|
757
|
+
initial: "/",
|
|
758
|
+
});
|
|
759
|
+
if (typeof input.value !== "string")
|
|
760
|
+
break;
|
|
761
|
+
const raw = input.value.trim();
|
|
762
|
+
if (!raw)
|
|
763
|
+
continue;
|
|
764
|
+
if (raw === "/" || raw === "help" || raw === "/help") {
|
|
765
|
+
console.log(chalk.gray(`\n${renderCommandList()}\n`));
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (raw === "exit" || raw === "/exit" || raw === "quit" || raw === "/quit") {
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
if (raw.startsWith("+")) {
|
|
772
|
+
const content = raw.slice(1).trim();
|
|
773
|
+
if (!content) {
|
|
774
|
+
console.log(chalk.yellow("Use + followed by what you learned, for example +We learned the CTA must name the next step."));
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
commandHistory.push(`/curate ${content}`);
|
|
778
|
+
await recordSessionCommand(`/curate ${content}`);
|
|
779
|
+
await recordSessionCuration(content);
|
|
780
|
+
await setSessionSummary(`Curated a new local note: ${content.length > 48 ? `${content.slice(0, 48)}...` : content}`);
|
|
781
|
+
try {
|
|
782
|
+
await curateCommandAction(content);
|
|
783
|
+
}
|
|
784
|
+
catch (err) {
|
|
785
|
+
console.log(chalk.red(`Command failed: ${err.message}`));
|
|
786
|
+
}
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const { command, args } = parseInteractiveInput(raw);
|
|
790
|
+
const spec = resolveCommand(command);
|
|
791
|
+
if (!raw.startsWith("/") && !spec) {
|
|
792
|
+
commandHistory.push(`/query ${raw}`);
|
|
793
|
+
await recordSessionCommand(`/query ${raw}`);
|
|
794
|
+
await recordSessionQuery(raw);
|
|
795
|
+
await setSessionSummary(`Queried Context: ${raw.length > 48 ? `${raw.slice(0, 48)}...` : raw}`);
|
|
796
|
+
try {
|
|
797
|
+
await searchCommandAction(raw);
|
|
798
|
+
}
|
|
799
|
+
catch (err) {
|
|
800
|
+
console.log(chalk.red(`Command failed: ${err.message}`));
|
|
801
|
+
}
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (!spec) {
|
|
805
|
+
console.log(chalk.yellow(`Unknown command: ${raw}. Type / to see available commands.`));
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
commandHistory.push(raw.startsWith("/") ? raw : `/${command} ${args.join(" ")}`.trim());
|
|
809
|
+
await recordSessionCommand(raw.startsWith("/") ? raw : `/${command} ${args.join(" ")}`.trim());
|
|
810
|
+
try {
|
|
811
|
+
const handledByMenu = await maybeHandleNativeMenu(command, args, currentConfig);
|
|
812
|
+
if (handledByMenu) {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
await spec.action(args, {
|
|
816
|
+
config: currentConfig,
|
|
817
|
+
handleFix,
|
|
818
|
+
handleRecord,
|
|
819
|
+
handleSpaces,
|
|
820
|
+
handleHome: async (config) => printHomeView(config),
|
|
821
|
+
handleRecent,
|
|
822
|
+
handleTimeline: async () => printTimelineView(),
|
|
823
|
+
handleHistory: async () => printHistory(commandHistory),
|
|
824
|
+
handleSession: async () => printSessionView(),
|
|
825
|
+
handleNewSession: async () => {
|
|
826
|
+
const next = await resetSessionState();
|
|
827
|
+
commandHistory.length = 0;
|
|
828
|
+
await recordSessionEvent({
|
|
829
|
+
kind: "session",
|
|
830
|
+
title: "Started a fresh shell session",
|
|
831
|
+
detail: `Session ID: ${next.id}`,
|
|
832
|
+
panel: "session",
|
|
833
|
+
focus: next.id,
|
|
834
|
+
status: "info",
|
|
835
|
+
});
|
|
836
|
+
printSessionHint("Fresh Session Started", [
|
|
837
|
+
`Session ID: ${next.id}`,
|
|
838
|
+
"Your repo context and .um data were kept.",
|
|
839
|
+
"Your command history for this shell was reset.",
|
|
840
|
+
]);
|
|
841
|
+
},
|
|
842
|
+
handleRestart: async () => {
|
|
843
|
+
await restartCommandAction();
|
|
844
|
+
commandHistory.length = 0;
|
|
845
|
+
},
|
|
846
|
+
handleDebug: async () => {
|
|
847
|
+
await debugCommandAction();
|
|
848
|
+
},
|
|
849
|
+
handleLogout: async () => {
|
|
850
|
+
await logoutCommandAction();
|
|
851
|
+
commandHistory.length = 0;
|
|
852
|
+
},
|
|
853
|
+
handlePanel: async () => {
|
|
854
|
+
await printCurrentPanelView();
|
|
855
|
+
},
|
|
856
|
+
handleContinue: async () => {
|
|
857
|
+
await continueLastPanel(currentConfig);
|
|
858
|
+
},
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
catch (err) {
|
|
862
|
+
console.log(chalk.red(`Command failed: ${err.message}`));
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
async function handleRecent(_config) {
|
|
867
|
+
await setSessionPanel("recent", "activity");
|
|
868
|
+
const pending = await getPendingMemories();
|
|
869
|
+
const pulled = await getPulledMemories();
|
|
870
|
+
const runs = await getConnectorRuns();
|
|
871
|
+
const session = await ensureSessionState();
|
|
872
|
+
console.log("");
|
|
873
|
+
printRecentSection("Recent session queries", session.recentQueries.slice(0, 3), (item, index) => {
|
|
874
|
+
return ` ${index + 1}. ${item}`;
|
|
875
|
+
});
|
|
876
|
+
printRecentSection("Recent session curations", session.recentCurations.slice(0, 3), (item, index) => {
|
|
877
|
+
return ` ${index + 1}. ${item}`;
|
|
878
|
+
});
|
|
879
|
+
printRecentSection("Recent local drafts", pending.slice(-3).reverse(), (item, index) => {
|
|
880
|
+
const preview = item.content.length > 72 ? `${item.content.slice(0, 72)}...` : item.content;
|
|
881
|
+
return ` ${index + 1}. ${preview} ${chalk.gray(`[${item.source}]`)}`;
|
|
882
|
+
});
|
|
883
|
+
printRecentSection("Recent pulled context", pulled.slice(-3).reverse(), (item, index) => {
|
|
884
|
+
const preview = item.content.length > 72 ? `${item.content.slice(0, 72)}...` : item.content;
|
|
885
|
+
return ` ${index + 1}. ${preview}`;
|
|
886
|
+
});
|
|
887
|
+
printRecentSection("Recent connector runs", runs.slice(0, 3), (item, index) => {
|
|
888
|
+
return ` ${index + 1}. ${item.connectorName} ${chalk.gray(`(${item.status})`)} - ${item.summary}`;
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
async function printHistory(commandHistory) {
|
|
892
|
+
const session = await ensureSessionState();
|
|
893
|
+
console.log("");
|
|
894
|
+
console.log(chalk.bold(" Session command history"));
|
|
895
|
+
const history = session.commandHistory.length > 0 ? session.commandHistory : commandHistory;
|
|
896
|
+
if (history.length === 0) {
|
|
897
|
+
console.log(chalk.gray(" No commands yet."));
|
|
898
|
+
console.log("");
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
history.slice(-10).forEach((entry, index) => {
|
|
902
|
+
console.log(` ${index + 1}. ${entry}`);
|
|
903
|
+
});
|
|
904
|
+
console.log("");
|
|
905
|
+
}
|
|
73
906
|
async function handleFix(error, config) {
|
|
74
907
|
if (!error) {
|
|
75
|
-
console.log(chalk.red("Usage: fix <error>"));
|
|
908
|
+
console.log(chalk.red("Usage: /fix <error>"));
|
|
76
909
|
return;
|
|
77
910
|
}
|
|
78
911
|
try {
|
|
912
|
+
const localFixes = await getPulledFixes();
|
|
913
|
+
const localMatches = localFixes.filter((entry) => [entry.errorSignal, entry.solution].join(" ").toLowerCase().includes(error.toLowerCase()));
|
|
914
|
+
if (localMatches.length > 0) {
|
|
915
|
+
console.log(chalk.cyan("\n Local cached fixes\n"));
|
|
916
|
+
localMatches.forEach((result, index) => {
|
|
917
|
+
const pct = Math.round(result.confidence * 100);
|
|
918
|
+
console.log(chalk.cyan(` ${index + 1}. [${pct}%] ${result.errorSignal}`));
|
|
919
|
+
console.log(` ${result.solution}`);
|
|
920
|
+
});
|
|
921
|
+
console.log("");
|
|
922
|
+
}
|
|
79
923
|
const res = await fetch(`${config.serverUrl}/api/evolutions/search?error=${encodeURIComponent(error)}`, { headers: { Authorization: `Bearer ${config.apiKey}` } });
|
|
80
|
-
if (!res.ok)
|
|
924
|
+
if (!res.ok) {
|
|
925
|
+
console.log(chalk.red("Server search failed."));
|
|
81
926
|
return;
|
|
927
|
+
}
|
|
82
928
|
const data = await res.json();
|
|
83
|
-
if (data.results.length === 0) {
|
|
929
|
+
if (data.results.length === 0 && localMatches.length === 0) {
|
|
84
930
|
console.log(chalk.yellow("No known fixes"));
|
|
85
931
|
return;
|
|
86
932
|
}
|
|
87
|
-
data.results.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
933
|
+
if (data.results.length > 0) {
|
|
934
|
+
console.log(chalk.cyan(" Server results\n"));
|
|
935
|
+
data.results.forEach((result, index) => {
|
|
936
|
+
const pct = Math.round(result.confidence * 100);
|
|
937
|
+
console.log(chalk.cyan(` ${index + 1}. [${pct}%]`));
|
|
938
|
+
console.log(` ${result.solution}`);
|
|
939
|
+
console.log(chalk.gray(` ${result.timesSucceeded}/${result.timesApplied} successes | ID: ${result.id}`));
|
|
940
|
+
console.log("");
|
|
941
|
+
});
|
|
942
|
+
}
|
|
94
943
|
}
|
|
95
944
|
catch (err) {
|
|
96
945
|
console.log(chalk.red(`Error: ${err.message}`));
|
|
@@ -99,7 +948,7 @@ async function handleFix(error, config) {
|
|
|
99
948
|
async function handleRecord(parts, config) {
|
|
100
949
|
const [id, outcome] = parts;
|
|
101
950
|
if (!id || !["success", "failure"].includes(outcome)) {
|
|
102
|
-
console.log(chalk.red("Usage: record <id> <success|failure>"));
|
|
951
|
+
console.log(chalk.red("Usage: /record <id> <success|failure>"));
|
|
103
952
|
return;
|
|
104
953
|
}
|
|
105
954
|
try {
|
|
@@ -111,8 +960,10 @@ async function handleRecord(parts, config) {
|
|
|
111
960
|
},
|
|
112
961
|
body: JSON.stringify({ outcome }),
|
|
113
962
|
});
|
|
114
|
-
if (!res.ok)
|
|
963
|
+
if (!res.ok) {
|
|
964
|
+
console.log(chalk.red("Could not record the fix outcome."));
|
|
115
965
|
return;
|
|
966
|
+
}
|
|
116
967
|
const data = await res.json();
|
|
117
968
|
const pct = Math.round(data.confidence * 100);
|
|
118
969
|
console.log(chalk.green(`Recorded ${outcome} - confidence: ${pct}%`));
|
|
@@ -122,8 +973,12 @@ async function handleRecord(parts, config) {
|
|
|
122
973
|
}
|
|
123
974
|
}
|
|
124
975
|
async function handleSpaces(config) {
|
|
976
|
+
const pending = await getPendingMemories();
|
|
977
|
+
const pulled = await getPulledMemories();
|
|
125
978
|
console.log(chalk.cyan(` Current company: ${config.companyName}`));
|
|
126
979
|
console.log(chalk.cyan(` Current space: ${config.projectName}`));
|
|
980
|
+
console.log(chalk.gray(` Local context notes waiting to push: ${pending.length}`));
|
|
981
|
+
console.log(chalk.gray(` Local pulled snapshot size: ${pulled.length}`));
|
|
127
982
|
if (!config.umbrellaUrl) {
|
|
128
983
|
console.log(chalk.gray(" Run setup again if you want this terminal to list all company spaces."));
|
|
129
984
|
return;
|
|
@@ -137,9 +992,10 @@ async function handleSpaces(config) {
|
|
|
137
992
|
const core = space.isPrimary ? " [core]" : "";
|
|
138
993
|
console.log(chalk.gray(` ${index + 1}. ${space.name}${core}${active}`));
|
|
139
994
|
});
|
|
140
|
-
console.log(chalk.gray('\n
|
|
995
|
+
console.log(chalk.gray('\n Run "/space switch" to move this repo to another space.'));
|
|
141
996
|
}
|
|
142
997
|
catch (err) {
|
|
143
998
|
console.log(chalk.red(` Could not load spaces: ${err.message}`));
|
|
144
999
|
}
|
|
145
1000
|
}
|
|
1001
|
+
export { renderCommandList };
|