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.
Files changed (38) hide show
  1. package/dist/commands/catalog.d.ts +33 -0
  2. package/dist/commands/catalog.js +211 -0
  3. package/dist/commands/connect.js +14 -14
  4. package/dist/commands/connectors.d.ts +15 -0
  5. package/dist/commands/connectors.js +620 -0
  6. package/dist/commands/curate.d.ts +1 -0
  7. package/dist/commands/curate.js +39 -3
  8. package/dist/commands/debug.d.ts +2 -0
  9. package/dist/commands/debug.js +55 -0
  10. package/dist/commands/hub.d.ts +20 -0
  11. package/dist/commands/hub.js +429 -0
  12. package/dist/commands/interactive.d.ts +2 -0
  13. package/dist/commands/interactive.js +918 -62
  14. package/dist/commands/locations.d.ts +1 -0
  15. package/dist/commands/locations.js +15 -12
  16. package/dist/commands/logout.d.ts +2 -0
  17. package/dist/commands/logout.js +34 -0
  18. package/dist/commands/model.d.ts +11 -0
  19. package/dist/commands/model.js +211 -0
  20. package/dist/commands/providers.d.ts +17 -0
  21. package/dist/commands/providers.js +344 -0
  22. package/dist/commands/pull.js +10 -1
  23. package/dist/commands/push.js +18 -1
  24. package/dist/commands/restart.d.ts +2 -0
  25. package/dist/commands/restart.js +21 -0
  26. package/dist/commands/search.js +19 -1
  27. package/dist/commands/session.d.ts +2 -0
  28. package/dist/commands/session.js +128 -0
  29. package/dist/commands/space.d.ts +1 -0
  30. package/dist/commands/space.js +81 -63
  31. package/dist/commands/status.d.ts +25 -0
  32. package/dist/commands/status.js +104 -16
  33. package/dist/config.d.ts +23 -0
  34. package/dist/config.js +69 -0
  35. package/dist/index.js +26 -4
  36. package/dist/repo-state.d.ts +84 -0
  37. package/dist/repo-state.js +419 -3
  38. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { configManager } from "../config.js";
3
- import { addPendingMemory, ensureRepoContext, getRepoContext } from "../repo-state.js";
3
+ import { addPendingMemory, ensureRepoContext, getPendingMemories, getRepoContext, recordSessionCuration, recordSessionEvent, setSessionPanel } from "../repo-state.js";
4
4
  export async function curateCommandAction(content, opts = {}) {
5
5
  const config = configManager.config;
6
6
  if (!config) {
@@ -12,6 +12,7 @@ export async function curateCommandAction(content, opts = {}) {
12
12
  return;
13
13
  }
14
14
  await ensureRepoContext(config);
15
+ await setSessionPanel("curate", content.trim());
15
16
  const entry = await addPendingMemory({
16
17
  content: content.trim(),
17
18
  tags: opts.tag ? (Array.isArray(opts.tag) ? opts.tag : [opts.tag]) : [],
@@ -19,17 +20,52 @@ export async function curateCommandAction(content, opts = {}) {
19
20
  systemType: opts.type || "system1_knowledge",
20
21
  });
21
22
  const { umDir } = await getRepoContext();
23
+ await recordSessionCuration(content.trim());
24
+ await recordSessionEvent({
25
+ kind: "curate",
26
+ title: "Saved a new local Context note",
27
+ detail: `Draft ${entry.id} was added to .um for ${config.companyName} / ${config.projectName}.`,
28
+ panel: "curate",
29
+ focus: entry.id,
30
+ status: "success",
31
+ });
22
32
  console.log(chalk.green(`\n Saved locally to ${config.companyName} / ${config.projectName}`));
23
33
  console.log(chalk.gray(` Pending entry: ${entry.id}`));
24
34
  console.log(chalk.gray(` Repo context folder: ${umDir}`));
25
35
  console.log(chalk.gray(' Run "umbrella-context push" when you want to sync this to the server.'));
26
36
  }
37
+ export async function curateViewCommandAction() {
38
+ const config = configManager.config;
39
+ if (!config) {
40
+ console.log(chalk.red("Not configured. Run: umbrella-context setup"));
41
+ return;
42
+ }
43
+ await ensureRepoContext(config);
44
+ await setSessionPanel("curate", "view");
45
+ const entries = await getPendingMemories();
46
+ console.log(chalk.bold(`\n Local Curations for ${config.companyName} / ${config.projectName}\n`));
47
+ if (entries.length === 0) {
48
+ console.log(chalk.yellow(" No local curations staged yet."));
49
+ console.log(chalk.gray(' Try: umbrella-context curate "What you learned"'));
50
+ return;
51
+ }
52
+ entries.forEach((entry, index) => {
53
+ const tags = entry.tags.length > 0 ? ` [${entry.tags.join(", ")}]` : "";
54
+ console.log(` ${index + 1}. ${entry.content}${tags}`);
55
+ console.log(chalk.gray(` ${entry.systemType} • ${entry.createdAt} • ${entry.id}`));
56
+ });
57
+ }
27
58
  export function curateCommand(cli) {
28
59
  cli
29
- .command("curate <content>", "Save context locally in this repo so it can be pushed later")
60
+ .command("curate [...content]", "Save context locally in this repo so it can be pushed later")
30
61
  .option("--tag <tag>", "Add tags (repeatable)")
31
62
  .option("--type <type>", "system1_knowledge or system2_reasoning")
32
63
  .action(async (content, opts) => {
33
- await curateCommandAction(content, opts);
64
+ const parts = Array.isArray(content) ? content : [content];
65
+ if (parts.length === 1 && parts[0] === "view") {
66
+ await curateViewCommandAction();
67
+ return;
68
+ }
69
+ await curateCommandAction(parts.join(" "), opts);
34
70
  });
35
71
  }
@@ -0,0 +1,2 @@
1
+ export declare function debugCommandAction(): Promise<void>;
2
+ export declare function debugCommand(cli: any): void;
@@ -0,0 +1,55 @@
1
+ import chalk from "chalk";
2
+ import { configManager } from "../config.js";
3
+ import { ensureSessionState, getConnectorRuns, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, getRepoContext, } from "../repo-state.js";
4
+ export async function debugCommandAction() {
5
+ const config = configManager.config;
6
+ if (!config) {
7
+ console.log(chalk.red("Not configured. Run: umbrella-context setup"));
8
+ return;
9
+ }
10
+ const { repoRoot, state, umDir } = await getRepoContext();
11
+ const session = await ensureSessionState();
12
+ const pending = await getPendingMemories();
13
+ const pulled = await getPulledMemories();
14
+ const fixes = await getPulledFixes();
15
+ const connectors = await getInstalledConnectors();
16
+ const hubEntries = await getInstalledHubEntries();
17
+ const runs = await getConnectorRuns();
18
+ console.log(chalk.bold("\n Umbrella Context Debug\n"));
19
+ console.log(chalk.cyan(" Config"));
20
+ console.log(` Config Path: ${configManager.configPath}`);
21
+ console.log(` Umbrella URL: ${config.umbrellaUrl ?? "Not saved"}`);
22
+ console.log(` Server URL: ${config.serverUrl}`);
23
+ console.log(` Company: ${config.companyName} (${config.companyId})`);
24
+ console.log(` Space: ${config.projectName} (${config.projectId})`);
25
+ console.log(` Workspace: ${config.workspaceName} (${config.workspaceId})`);
26
+ console.log("");
27
+ console.log(chalk.cyan(" Repo State"));
28
+ console.log(` Repo Root: ${repoRoot}`);
29
+ console.log(` .um Folder: ${umDir}`);
30
+ console.log(` Context Initialized: ${state ? "Yes" : "No"}`);
31
+ console.log(` Last Push: ${state?.lastPushAt ?? "Never"}`);
32
+ console.log(` Last Pull: ${state?.lastPullAt ?? "Never"}`);
33
+ console.log("");
34
+ console.log(chalk.cyan(" Local Counts"));
35
+ console.log(` Pending Curations: ${pending.length}`);
36
+ console.log(` Pulled Context Notes: ${pulled.length}`);
37
+ console.log(` Pulled Known Fixes: ${fixes.length}`);
38
+ console.log(` Installed Hub Entries: ${hubEntries.length}`);
39
+ console.log(` Installed Connectors: ${connectors.length}`);
40
+ console.log(` Connector Runs: ${runs.length}`);
41
+ console.log("");
42
+ console.log(chalk.cyan(" Session"));
43
+ console.log(` Session ID: ${session.id}`);
44
+ console.log(` Started: ${session.startedAt}`);
45
+ console.log(` Updated: ${session.updatedAt}`);
46
+ console.log(` Commands: ${session.commandHistory.length}`);
47
+ console.log(` Queries: ${session.recentQueries.length}`);
48
+ console.log(` Curations: ${session.recentCurations.length}`);
49
+ console.log(` Last Summary: ${session.lastSummary ?? "Nothing recorded yet"}`);
50
+ }
51
+ export function debugCommand(cli) {
52
+ cli.command("debug", "Show detailed local runtime paths, counts, and session state").action(async () => {
53
+ await debugCommandAction();
54
+ });
55
+ }
@@ -0,0 +1,20 @@
1
+ type HubEntry = {
2
+ slug: string;
3
+ name: string;
4
+ type: "bundle" | "skill" | "connector";
5
+ description: string;
6
+ includes: string[];
7
+ nextSteps: string[];
8
+ installsConnector?: string;
9
+ };
10
+ export type HubCommandResult = {
11
+ action: "list" | "install" | "inspect" | "installed" | "registry";
12
+ changed: boolean;
13
+ installedEntry?: HubEntry;
14
+ assetPath?: string;
15
+ filePaths?: string[];
16
+ recommendedConnector?: string;
17
+ };
18
+ export declare function hubCommandAction(action: string, subAction?: string): Promise<HubCommandResult | void>;
19
+ export declare function hubCommand(cli: any): void;
20
+ export {};
@@ -0,0 +1,429 @@
1
+ import { randomUUID } from "crypto";
2
+ import chalk from "chalk";
3
+ import prompts from "prompts";
4
+ import { configManager } from "../config.js";
5
+ import { getInstalledHubEntries, getSessionState, recordSessionEvent, recordSessionHubEntry, setInstalledHubEntries, setSessionPanel, writeHubAsset, writeHubAssetFiles } from "../repo-state.js";
6
+ import { connectorsCommandAction } from "./connectors.js";
7
+ const DEFAULT_HUB_ENTRIES = [
8
+ {
9
+ slug: "quick-ping-growth-bundle",
10
+ name: "Quick Ping Growth Bundle",
11
+ type: "bundle",
12
+ description: "Context prompts, SEO helpers, and research habits for the Quick Ping growth team.",
13
+ includes: ["Growth query pack", "Capture template", "Quick Ping growth playbook"],
14
+ nextSteps: [
15
+ "umbrella-context query \"What messaging already worked for Quick Ping?\"",
16
+ "umbrella-context curate \"...\"",
17
+ "umbrella-context push",
18
+ ],
19
+ },
20
+ {
21
+ slug: "engineering-debug-skill-pack",
22
+ name: "Engineering Debug Skill Pack",
23
+ type: "skill",
24
+ description: "A reusable troubleshooting pack for recurring engineering bugs and known-fix workflows.",
25
+ includes: ["Debug checklist", "Root cause template"],
26
+ nextSteps: [
27
+ "umbrella-context fix \"<error signal>\"",
28
+ "umbrella-context record <id> success",
29
+ ],
30
+ },
31
+ {
32
+ slug: "umbrella-context-mcp-connector",
33
+ name: "Umbrella Context MCP Connector",
34
+ type: "connector",
35
+ description: "A ready-made MCP connector bundle for IDE agents that should talk to Context.",
36
+ includes: ["Connector install notes", "Repo MCP bridge wiring"],
37
+ nextSteps: [
38
+ "umbrella-context connectors install codex-mcp",
39
+ "umbrella-context connectors run codex-mcp",
40
+ "umbrella-context status",
41
+ ],
42
+ installsConnector: "codex-mcp",
43
+ },
44
+ ];
45
+ function buildHubAsset(entry) {
46
+ return [
47
+ `# ${entry.name}`,
48
+ "",
49
+ `Type: ${entry.type}`,
50
+ `Slug: ${entry.slug}`,
51
+ "",
52
+ "## Why this exists",
53
+ "",
54
+ entry.description,
55
+ "",
56
+ "## Suggested use",
57
+ "",
58
+ "- Review this file before running related Context workflows.",
59
+ "- Copy the reusable parts into prompts, rules, or team playbooks as needed.",
60
+ "- Keep the repo-specific details nearby so future operators reuse the same pattern.",
61
+ "",
62
+ "## Next commands",
63
+ "",
64
+ ...entry.nextSteps.map((step) => `- \`${step}\``),
65
+ "",
66
+ ].join("\n");
67
+ }
68
+ function buildHubFiles(entry) {
69
+ if (entry.slug === "quick-ping-growth-bundle") {
70
+ return [
71
+ {
72
+ name: "overview.md",
73
+ content: [
74
+ "# Quick Ping Growth Bundle",
75
+ "",
76
+ "Use this bundle when you are working on Quick Ping growth work inside this repo.",
77
+ "",
78
+ "## What is inside",
79
+ "",
80
+ "- Growth context reminders",
81
+ "- Reusable query prompts",
82
+ "- Capture patterns for turning work into shared context",
83
+ "",
84
+ "## Recommended flow",
85
+ "",
86
+ "1. Run `umbrella-context query \"What do we already know about this audience?\"`",
87
+ "2. Do the work.",
88
+ "3. Run `umbrella-context curate \"...\"` with the result.",
89
+ "4. Run `umbrella-context push` when it is worth sharing.",
90
+ "",
91
+ ].join("\n"),
92
+ },
93
+ {
94
+ name: "queries.md",
95
+ content: [
96
+ "# Growth Queries",
97
+ "",
98
+ "- `umbrella-context query \"What messaging already worked for Quick Ping?\"`",
99
+ "- `umbrella-context query \"What objections have job seekers shown before?\"`",
100
+ "- `umbrella-context query \"Which channels have we already tested?\"`",
101
+ "",
102
+ ].join("\n"),
103
+ },
104
+ {
105
+ name: "capture-template.md",
106
+ content: [
107
+ "# Capture Template",
108
+ "",
109
+ "Use this shape when you turn work into shared context:",
110
+ "",
111
+ "```text",
112
+ "We learned that ...",
113
+ "Why it mattered: ...",
114
+ "Where we saw it: ...",
115
+ "What to do next time: ...",
116
+ "```",
117
+ "",
118
+ ].join("\n"),
119
+ },
120
+ ];
121
+ }
122
+ if (entry.slug === "engineering-debug-skill-pack") {
123
+ return [
124
+ {
125
+ name: "debug-checklist.md",
126
+ content: [
127
+ "# Engineering Debug Checklist",
128
+ "",
129
+ "1. Reproduce the problem.",
130
+ "2. Search Context for prior fixes.",
131
+ "3. Capture the exact error signal.",
132
+ "4. Write down the root cause once confirmed.",
133
+ "5. Record the fix outcome with `umbrella-context record`.",
134
+ "",
135
+ ].join("\n"),
136
+ },
137
+ {
138
+ name: "root-cause-template.md",
139
+ content: [
140
+ "# Root Cause Template",
141
+ "",
142
+ "```text",
143
+ "Bug: ...",
144
+ "Signal: ...",
145
+ "Root cause: ...",
146
+ "Fix: ...",
147
+ "How to prevent it next time: ...",
148
+ "```",
149
+ "",
150
+ ].join("\n"),
151
+ },
152
+ ];
153
+ }
154
+ if (entry.slug === "umbrella-context-mcp-connector") {
155
+ return [
156
+ {
157
+ name: "install-next.md",
158
+ content: [
159
+ "# Umbrella Context MCP Connector",
160
+ "",
161
+ "This bundle pairs with the `codex-mcp` connector.",
162
+ "",
163
+ "## Next step",
164
+ "",
165
+ "Run:",
166
+ "",
167
+ "```bash",
168
+ "umbrella-context connectors install codex-mcp",
169
+ "```",
170
+ "",
171
+ "Then verify with:",
172
+ "",
173
+ "```bash",
174
+ "umbrella-context connectors run codex-mcp",
175
+ "```",
176
+ "",
177
+ ].join("\n"),
178
+ },
179
+ ];
180
+ }
181
+ return [];
182
+ }
183
+ function printInstalled(installed) {
184
+ console.log(chalk.bold("\n Installed Hub Entries\n"));
185
+ if (installed.length === 0) {
186
+ console.log(chalk.yellow(" No hub entries installed in this repo yet."));
187
+ return;
188
+ }
189
+ installed.forEach((entry, index) => {
190
+ console.log(` ${index + 1}. ${entry.name} [${entry.type}]`);
191
+ console.log(chalk.gray(` ${entry.description}`));
192
+ console.log(chalk.gray(` Installed from ${entry.registryName} at ${new Date(entry.installedAt).toLocaleString()}`));
193
+ });
194
+ }
195
+ function printRegistryList() {
196
+ console.log(chalk.bold("\n Hub Registries\n"));
197
+ configManager.hubRegistries.forEach((registry, index) => {
198
+ console.log(` ${index + 1}. ${registry.name}`);
199
+ console.log(chalk.gray(` ${registry.url}`));
200
+ });
201
+ }
202
+ export async function hubCommandAction(action, subAction) {
203
+ const normalized = action.toLowerCase();
204
+ await setSessionPanel("hub", subAction ?? normalized);
205
+ if (normalized === "list") {
206
+ await setSessionPanel("hub", "list");
207
+ console.log(chalk.bold("\n Hub Catalog\n"));
208
+ DEFAULT_HUB_ENTRIES.forEach((entry, index) => {
209
+ console.log(` ${index + 1}. ${entry.name} [${entry.type}]`);
210
+ console.log(chalk.gray(` ${entry.description}`));
211
+ });
212
+ const installed = await getInstalledHubEntries();
213
+ if (installed.length > 0) {
214
+ console.log(chalk.gray(`\n Installed in this repo: ${installed.length}`));
215
+ }
216
+ return { action: "list", changed: false };
217
+ }
218
+ if (normalized === "install") {
219
+ const installed = await getInstalledHubEntries();
220
+ let selected = subAction ? DEFAULT_HUB_ENTRIES.find((entry) => entry.slug === subAction) : undefined;
221
+ if (!selected) {
222
+ const answer = await prompts({
223
+ type: "select",
224
+ name: "value",
225
+ message: "Choose a hub entry to install into this repo",
226
+ choices: DEFAULT_HUB_ENTRIES.map((entry) => ({
227
+ title: `${entry.name} (${entry.type})`,
228
+ description: entry.description,
229
+ value: entry.slug,
230
+ })),
231
+ });
232
+ selected = DEFAULT_HUB_ENTRIES.find((entry) => entry.slug === answer.value);
233
+ }
234
+ if (!selected) {
235
+ console.log(chalk.yellow("\n No hub entry selected."));
236
+ return;
237
+ }
238
+ await setSessionPanel("hub", selected.slug);
239
+ await recordSessionHubEntry(selected.slug);
240
+ if (installed.some((entry) => entry.slug === selected.slug)) {
241
+ await recordSessionEvent({
242
+ kind: "hub",
243
+ title: `${selected.name} was already installed`,
244
+ detail: "No repo files changed because this bundle was already present.",
245
+ panel: "hub",
246
+ focus: selected.slug,
247
+ status: "info",
248
+ });
249
+ console.log(chalk.yellow(`\n ${selected.name} is already installed in this repo.`));
250
+ return {
251
+ action: "install",
252
+ changed: false,
253
+ installedEntry: selected,
254
+ };
255
+ }
256
+ const registry = configManager.hubRegistries[0];
257
+ await setInstalledHubEntries([
258
+ ...installed,
259
+ {
260
+ id: randomUUID(),
261
+ slug: selected.slug,
262
+ name: selected.name,
263
+ type: selected.type,
264
+ description: selected.description,
265
+ installedAt: new Date().toISOString(),
266
+ registryName: registry?.name ?? "Umbrella Hub",
267
+ },
268
+ ]);
269
+ const assetPath = await writeHubAsset(selected.slug, buildHubAsset(selected));
270
+ const filePaths = await writeHubAssetFiles(selected.slug, buildHubFiles(selected));
271
+ await recordSessionEvent({
272
+ kind: "hub",
273
+ title: `Installed hub entry ${selected.name}`,
274
+ detail: `${selected.includes.length} packaged item${selected.includes.length === 1 ? "" : "s"} are now available in this repo.`,
275
+ panel: "hub",
276
+ focus: selected.slug,
277
+ status: "success",
278
+ });
279
+ console.log(chalk.green(`\n Installed ${selected.name} into this repo.`));
280
+ console.log(chalk.gray(` Asset written to ${assetPath}`));
281
+ filePaths.forEach((filePath) => {
282
+ console.log(chalk.gray(` Added bundle file ${filePath}`));
283
+ });
284
+ if (selected.slug === "umbrella-context-mcp-connector") {
285
+ console.log(chalk.gray(" Installing the recommended codex-mcp connector next..."));
286
+ await connectorsCommandAction("install", selected.installsConnector ?? "codex-mcp");
287
+ }
288
+ return {
289
+ action: "install",
290
+ changed: true,
291
+ installedEntry: selected,
292
+ assetPath,
293
+ filePaths,
294
+ recommendedConnector: selected.installsConnector,
295
+ };
296
+ }
297
+ if (normalized === "inspect") {
298
+ const target = subAction
299
+ ? DEFAULT_HUB_ENTRIES.find((entry) => entry.slug === subAction)
300
+ : undefined;
301
+ if (!target) {
302
+ console.log(chalk.red("Use: hub inspect <slug>"));
303
+ return;
304
+ }
305
+ const installed = await getInstalledHubEntries();
306
+ const isInstalled = installed.some((entry) => entry.slug === target.slug);
307
+ await setSessionPanel("hub", target.slug);
308
+ await recordSessionHubEntry(target.slug);
309
+ await recordSessionEvent({
310
+ kind: "hub",
311
+ title: `Inspected hub entry ${target.name}`,
312
+ detail: `Viewed the bundle details and next steps for ${target.slug}.`,
313
+ panel: "hub",
314
+ focus: target.slug,
315
+ status: "info",
316
+ });
317
+ console.log(chalk.bold(`\n ${target.name}\n`));
318
+ console.log(` Type: ${target.type}`);
319
+ console.log(` Slug: ${target.slug}`);
320
+ console.log(` Installed: ${isInstalled ? "Yes" : "No"}`);
321
+ console.log(chalk.gray(` ${target.description}`));
322
+ console.log("");
323
+ console.log(chalk.cyan(" Includes"));
324
+ target.includes.forEach((item) => {
325
+ console.log(` - ${item}`);
326
+ });
327
+ console.log("");
328
+ console.log(chalk.cyan(" Next Steps"));
329
+ target.nextSteps.forEach((step) => {
330
+ console.log(` - ${step}`);
331
+ });
332
+ if (target.installsConnector) {
333
+ console.log("");
334
+ console.log(chalk.cyan(" Connector"));
335
+ console.log(` - Installs ${target.installsConnector}`);
336
+ }
337
+ return {
338
+ action: "inspect",
339
+ changed: false,
340
+ installedEntry: target,
341
+ recommendedConnector: target.installsConnector,
342
+ };
343
+ }
344
+ if (normalized === "recent") {
345
+ const session = await getSessionState();
346
+ const slug = session?.recentHubSlugs?.[0];
347
+ if (!slug) {
348
+ console.log(chalk.yellow("\n No recent hub entry in this session yet."));
349
+ return;
350
+ }
351
+ return hubCommandAction("inspect", slug);
352
+ }
353
+ if (normalized === "installed") {
354
+ await setSessionPanel("hub", "installed");
355
+ await printInstalled(await getInstalledHubEntries());
356
+ return { action: "installed", changed: false };
357
+ }
358
+ if (normalized === "registry") {
359
+ await setSessionPanel("hub", `registry:${subAction ?? "list"}`);
360
+ const mode = (subAction ?? "list").toLowerCase();
361
+ if (mode === "list") {
362
+ printRegistryList();
363
+ return { action: "registry", changed: false };
364
+ }
365
+ if (mode === "add") {
366
+ const answer = await prompts([
367
+ { type: "text", name: "name", message: "Registry name" },
368
+ { type: "text", name: "url", message: "Registry URL" },
369
+ ]);
370
+ if (!answer.name || !answer.url) {
371
+ console.log(chalk.yellow("\n Registry add cancelled."));
372
+ return;
373
+ }
374
+ configManager.upsertHubRegistry({
375
+ id: randomUUID(),
376
+ name: answer.name,
377
+ url: answer.url,
378
+ updatedAt: new Date().toISOString(),
379
+ });
380
+ await recordSessionEvent({
381
+ kind: "hub",
382
+ title: `Added hub registry ${answer.name}`,
383
+ detail: `The CLI can now see bundles from ${answer.url}.`,
384
+ panel: "hub",
385
+ focus: `registry:${answer.name}`,
386
+ status: "success",
387
+ });
388
+ console.log(chalk.green(`\n Added registry ${answer.name}.`));
389
+ return { action: "registry", changed: true };
390
+ }
391
+ if (mode === "remove") {
392
+ const registries = configManager.hubRegistries;
393
+ const answer = await prompts({
394
+ type: "select",
395
+ name: "value",
396
+ message: "Choose a registry to remove",
397
+ choices: registries.map((entry) => ({
398
+ title: entry.name,
399
+ description: entry.url,
400
+ value: entry.id,
401
+ })),
402
+ });
403
+ if (!answer.value) {
404
+ console.log(chalk.yellow("\n No registry selected."));
405
+ return;
406
+ }
407
+ const selected = registries.find((entry) => entry.id === answer.value);
408
+ configManager.removeHubRegistry(answer.value);
409
+ await recordSessionEvent({
410
+ kind: "hub",
411
+ title: `Removed hub registry ${selected?.name ?? "registry"}`,
412
+ detail: "The registry was removed from this device configuration.",
413
+ panel: "hub",
414
+ focus: `registry:${selected?.name ?? "removed"}`,
415
+ status: "warning",
416
+ });
417
+ console.log(chalk.green(`\n Removed registry ${selected?.name ?? "registry"}.`));
418
+ return { action: "registry", changed: true };
419
+ }
420
+ console.log(chalk.red("Use: hub registry list | add | remove"));
421
+ return;
422
+ }
423
+ console.log(chalk.red("Use: hub list | hub install [slug] | hub inspect <slug> | hub recent | hub installed | hub registry list|add|remove"));
424
+ }
425
+ export function hubCommand(cli) {
426
+ cli.command("hub <action> [subAction]", "Browse and install skills, bundles, and registries").action(async (action, subAction) => {
427
+ await hubCommandAction(action, subAction);
428
+ });
429
+ }
@@ -1 +1,3 @@
1
+ import { renderCommandList } from "./catalog.js";
1
2
  export declare function interactiveCommand(_args: string[]): Promise<void>;
3
+ export { renderCommandList };