@harbinger-ai/harbinger 0.1.0 → 0.1.2

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 (65) hide show
  1. package/bin/cli.js +40 -38
  2. package/bin/local.sh +2 -2
  3. package/bin/postinstall.js +8 -8
  4. package/config/index.js +6 -3
  5. package/config/instrumentation.js +11 -11
  6. package/lib/chat/actions.js +19 -0
  7. package/lib/chat/components/app-sidebar.js +17 -1
  8. package/lib/chat/components/app-sidebar.jsx +19 -1
  9. package/lib/chat/components/findings-page.js +164 -103
  10. package/lib/chat/components/findings-page.jsx +156 -101
  11. package/lib/chat/components/icons.js +22 -0
  12. package/lib/chat/components/icons.jsx +20 -0
  13. package/lib/chat/components/index.js +1 -0
  14. package/lib/chat/components/mission-control.js +490 -0
  15. package/lib/chat/components/mission-control.jsx +618 -0
  16. package/lib/chat/components/registry-page.js +267 -133
  17. package/lib/chat/components/registry-page.jsx +299 -138
  18. package/lib/chat/components/sidebar-user-nav.js +1 -1
  19. package/lib/chat/components/sidebar-user-nav.jsx +1 -1
  20. package/lib/chat/components/targets-page.js +269 -200
  21. package/lib/chat/components/targets-page.jsx +181 -111
  22. package/lib/chat/components/upgrade-dialog.js +2 -2
  23. package/lib/chat/components/upgrade-dialog.jsx +2 -2
  24. package/lib/cron.js +11 -7
  25. package/lib/db/index.js +6 -1
  26. package/lib/mcp/actions.js +1 -1
  27. package/lib/mcp/handler.js +2 -2
  28. package/lib/mcp/server.js +1 -1
  29. package/lib/paths.js +1 -1
  30. package/package.json +1 -1
  31. package/templates/.env.example +4 -4
  32. package/templates/.github/workflows/rebuild-event-handler.yml +20 -20
  33. package/templates/.github/workflows/run-job.yml +6 -6
  34. package/templates/.github/workflows/upgrade-event-handler.yml +12 -12
  35. package/templates/CLAUDE.md +3 -3
  36. package/templates/CLAUDE.md.template +9 -9
  37. package/templates/app/api/[...thepopebot]/route.js +1 -1
  38. package/templates/app/api/auth/[...nextauth]/route.js +1 -1
  39. package/templates/app/chat/[chatId]/page.js +2 -2
  40. package/templates/app/chats/page.js +2 -2
  41. package/templates/app/components/setup-form.jsx +1 -1
  42. package/templates/app/findings/page.js +2 -2
  43. package/templates/app/globals.css +1 -1
  44. package/templates/app/layout.js +1 -1
  45. package/templates/app/login/page.js +1 -1
  46. package/templates/app/notifications/page.js +2 -2
  47. package/templates/app/page.js +2 -2
  48. package/templates/app/settings/crons/page.js +1 -1
  49. package/templates/app/settings/layout.js +2 -2
  50. package/templates/app/settings/mcp/page.js +1 -1
  51. package/templates/app/settings/secrets/page.js +1 -1
  52. package/templates/app/settings/triggers/page.js +1 -1
  53. package/templates/app/stream/chat/route.js +1 -1
  54. package/templates/app/swarm/page.js +2 -2
  55. package/templates/app/targets/page.js +2 -2
  56. package/templates/app/toolbox/page.js +2 -2
  57. package/templates/config/AGENT.md +2 -2
  58. package/templates/config/EVENT_HANDLER.md +3 -3
  59. package/templates/config/SKILL_BUILDING_GUIDE.md +1 -1
  60. package/templates/config/SOUL.md +1 -1
  61. package/templates/docker/event-handler/Dockerfile +1 -1
  62. package/templates/docker-compose.yml +2 -2
  63. package/templates/instrumentation.js +1 -1
  64. package/templates/middleware.js +1 -1
  65. package/templates/next.config.mjs +2 -2
package/bin/cli.js CHANGED
@@ -49,10 +49,10 @@ function templatePath(userPath, templatesDir) {
49
49
 
50
50
  function printUsage() {
51
51
  console.log(`
52
- Usage: thepopebot <command>
52
+ Usage: harbinger <command>
53
53
 
54
54
  Commands:
55
- init Scaffold a new thepopebot project
55
+ init Scaffold a new Harbinger project
56
56
  setup Run interactive setup wizard
57
57
  setup-telegram Reconfigure Telegram webhook
58
58
  reset-auth Regenerate AUTH_SECRET (invalidates all sessions)
@@ -90,7 +90,7 @@ async function init() {
90
90
  const templatesDir = path.join(packageDir, 'templates');
91
91
  const noManaged = args.includes('--no-managed');
92
92
 
93
- // Guard: warn if the directory is not empty (unless it's an existing thepopebot project)
93
+ // Guard: warn if the directory is not empty (unless it's an existing Harbinger project)
94
94
  const entries = fs.readdirSync(cwd);
95
95
  if (entries.length > 0) {
96
96
  const pkgPath = path.join(cwd, 'package.json');
@@ -100,7 +100,7 @@ async function init() {
100
100
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
101
101
  const deps = pkg.dependencies || {};
102
102
  const devDeps = pkg.devDependencies || {};
103
- if (deps.thepopebot || devDeps.thepopebot) {
103
+ if (deps['@harbinger-ai/harbinger'] || devDeps['@harbinger-ai/harbinger'] || deps.thepopebot || devDeps.thepopebot) {
104
104
  isExistingProject = true;
105
105
  }
106
106
  } catch {}
@@ -111,7 +111,7 @@ async function init() {
111
111
  const { text, isCancel } = await import('@clack/prompts');
112
112
  const dirName = await text({
113
113
  message: 'Project directory name:',
114
- defaultValue: 'my-popebot',
114
+ defaultValue: 'my-agent',
115
115
  });
116
116
  if (isCancel(dirName)) {
117
117
  console.log('\nCancelled.\n');
@@ -125,7 +125,7 @@ async function init() {
125
125
  }
126
126
  }
127
127
 
128
- console.log('\nScaffolding thepopebot project...\n');
128
+ console.log('\nScaffolding Harbinger project...\n');
129
129
 
130
130
  const templateFiles = getTemplateFiles(templatesDir);
131
131
  const created = [];
@@ -168,7 +168,7 @@ async function init() {
168
168
  if (!fs.existsSync(pkgPath)) {
169
169
  const dirName = path.basename(cwd);
170
170
  const { version } = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'));
171
- const thepopebotDep = version.includes('-') ? version : '^1.0.0';
171
+ const harbingerDep = version.includes('-') ? version : '^1.0.0';
172
172
  const pkg = {
173
173
  name: dirName,
174
174
  private: true,
@@ -176,12 +176,12 @@ async function init() {
176
176
  dev: 'next dev --turbopack',
177
177
  build: 'next build',
178
178
  start: 'next start',
179
- setup: 'thepopebot setup',
180
- 'setup-telegram': 'thepopebot setup-telegram',
181
- 'reset-auth': 'thepopebot reset-auth',
179
+ setup: 'harbinger setup',
180
+ 'setup-telegram': 'harbinger setup-telegram',
181
+ 'reset-auth': 'harbinger reset-auth',
182
182
  },
183
183
  dependencies: {
184
- thepopebot: thepopebotDep,
184
+ '@harbinger-ai/harbinger': harbingerDep,
185
185
  next: '^15.5.12',
186
186
  'next-auth': '5.0.0-beta.30',
187
187
  'next-themes': '^0.4.0',
@@ -247,12 +247,12 @@ async function init() {
247
247
  if (changed.length > 0) {
248
248
  console.log('\n Updated templates available:');
249
249
  console.log(' These files differ from the current package templates.');
250
- console.log(' This may be from your edits, or from a thepopebot update.\n');
250
+ console.log(' This may be from your edits, or from a Harbinger update.\n');
251
251
  for (const file of changed) {
252
252
  console.log(` ${file}`);
253
253
  }
254
- console.log('\n To view differences: npx thepopebot diff <file>');
255
- console.log(' To reset to default: npx thepopebot reset <file>');
254
+ console.log('\n To view differences: npx harbinger diff <file>');
255
+ console.log(' To reset to default: npx harbinger reset <file>');
256
256
  }
257
257
 
258
258
  // Run npm install
@@ -262,32 +262,34 @@ async function init() {
262
262
  // Create or update .env with auto-generated infrastructure values
263
263
  const envPath = path.join(cwd, '.env');
264
264
  const { randomBytes } = await import('crypto');
265
- const thepopebotPkg = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'));
266
- const version = thepopebotPkg.version;
265
+ const harbingerPkg = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'));
266
+ const version = harbingerPkg.version;
267
267
 
268
268
  if (!fs.existsSync(envPath)) {
269
269
  // Seed .env for new projects
270
270
  const authSecret = randomBytes(32).toString('base64');
271
- const seedEnv = `# thepopebot Configuration
271
+ const seedEnv = `# Harbinger Configuration
272
272
  # Run "npm run setup" to complete configuration
273
273
 
274
274
  AUTH_SECRET=${authSecret}
275
275
  AUTH_TRUST_HOST=true
276
- THEPOPEBOT_VERSION=${version}
276
+ HARBINGER_VERSION=${version}
277
277
  `;
278
278
  fs.writeFileSync(envPath, seedEnv);
279
- console.log(` Created .env (AUTH_SECRET, THEPOPEBOT_VERSION=${version})`);
279
+ console.log(` Created .env (AUTH_SECRET, HARBINGER_VERSION=${version})`);
280
280
  } else {
281
- // Update THEPOPEBOT_VERSION in existing .env
281
+ // Update HARBINGER_VERSION in existing .env (also migrate legacy THEPOPEBOT_VERSION key)
282
282
  try {
283
283
  let envContent = fs.readFileSync(envPath, 'utf8');
284
- if (envContent.match(/^THEPOPEBOT_VERSION=.*/m)) {
285
- envContent = envContent.replace(/^THEPOPEBOT_VERSION=.*/m, `THEPOPEBOT_VERSION=${version}`);
284
+ if (envContent.match(/^HARBINGER_VERSION=.*/m)) {
285
+ envContent = envContent.replace(/^HARBINGER_VERSION=.*/m, `HARBINGER_VERSION=${version}`);
286
+ } else if (envContent.match(/^THEPOPEBOT_VERSION=.*/m)) {
287
+ envContent = envContent.replace(/^THEPOPEBOT_VERSION=.*/m, `HARBINGER_VERSION=${version}`);
286
288
  } else {
287
- envContent = envContent.trimEnd() + `\nTHEPOPEBOT_VERSION=${version}\n`;
289
+ envContent = envContent.trimEnd() + `\nHARBINGER_VERSION=${version}\n`;
288
290
  }
289
291
  fs.writeFileSync(envPath, envContent);
290
- console.log(` Updated THEPOPEBOT_VERSION to ${version}`);
292
+ console.log(` Updated HARBINGER_VERSION to ${version}`);
291
293
  } catch {}
292
294
  }
293
295
 
@@ -308,8 +310,8 @@ function reset(filePath) {
308
310
  for (const file of files) {
309
311
  console.log(` ${destPath(file)}`);
310
312
  }
311
- console.log('\nUsage: thepopebot reset <file>');
312
- console.log('Example: thepopebot reset config/SOUL.md\n');
313
+ console.log('\nUsage: harbinger reset <file>');
314
+ console.log('Example: harbinger reset config/SOUL.md\n');
313
315
  return;
314
316
  }
315
317
 
@@ -319,7 +321,7 @@ function reset(filePath) {
319
321
 
320
322
  if (!fs.existsSync(src)) {
321
323
  console.error(`\nTemplate not found: ${filePath}`);
322
- console.log('Run "thepopebot reset" to see available templates.\n');
324
+ console.log('Run "harbinger reset" to see available templates.\n');
323
325
  process.exit(1);
324
326
  }
325
327
 
@@ -365,8 +367,8 @@ function diff(filePath) {
365
367
  if (!anyDiff) {
366
368
  console.log(' All files match package templates.');
367
369
  }
368
- console.log('\nUsage: thepopebot diff <file>');
369
- console.log('Example: thepopebot diff config/SOUL.md\n');
370
+ console.log('\nUsage: harbinger diff <file>');
371
+ console.log('Example: harbinger diff config/SOUL.md\n');
370
372
  return;
371
373
  }
372
374
 
@@ -381,7 +383,7 @@ function diff(filePath) {
381
383
 
382
384
  if (!fs.existsSync(dest)) {
383
385
  console.log(`\n${filePath} does not exist in your project.`);
384
- console.log(`Run "thepopebot reset ${filePath}" to create it.\n`);
386
+ console.log(`Run "harbinger reset ${filePath}" to create it.\n`);
385
387
  return;
386
388
  }
387
389
 
@@ -391,7 +393,7 @@ function diff(filePath) {
391
393
  console.log('\nFiles are identical.\n');
392
394
  } catch (e) {
393
395
  // git diff exits with 1 when files differ (output already printed)
394
- console.log(`\n To reset: thepopebot reset ${filePath}\n`);
396
+ console.log(`\n To reset: harbinger reset ${filePath}\n`);
395
397
  }
396
398
  }
397
399
 
@@ -489,7 +491,7 @@ function readStdin() {
489
491
 
490
492
  /**
491
493
  * Prompt for a secret value interactively if not provided as an argument.
492
- * Supports piped stdin (e.g. echo "val" | thepopebot set-var KEY).
494
+ * Supports piped stdin (e.g. echo "val" | harbinger set-var KEY).
493
495
  */
494
496
  async function promptForValue(key) {
495
497
  const stdin = await readStdin();
@@ -516,8 +518,8 @@ async function promptForValue(key) {
516
518
 
517
519
  async function setAgentSecret(key, value) {
518
520
  if (!key) {
519
- console.error('\n Usage: thepopebot set-agent-secret <KEY> [VALUE]\n');
520
- console.error(' Example: thepopebot set-agent-secret ANTHROPIC_API_KEY\n');
521
+ console.error('\n Usage: harbinger set-agent-secret <KEY> [VALUE]\n');
522
+ console.error(' Example: harbinger set-agent-secret ANTHROPIC_API_KEY\n');
521
523
  process.exit(1);
522
524
  }
523
525
 
@@ -543,8 +545,8 @@ async function setAgentSecret(key, value) {
543
545
 
544
546
  async function setAgentLlmSecret(key, value) {
545
547
  if (!key) {
546
- console.error('\n Usage: thepopebot set-agent-llm-secret <KEY> [VALUE]\n');
547
- console.error(' Example: thepopebot set-agent-llm-secret BRAVE_API_KEY\n');
548
+ console.error('\n Usage: harbinger set-agent-llm-secret <KEY> [VALUE]\n');
549
+ console.error(' Example: harbinger set-agent-llm-secret BRAVE_API_KEY\n');
548
550
  process.exit(1);
549
551
  }
550
552
 
@@ -566,8 +568,8 @@ async function setAgentLlmSecret(key, value) {
566
568
 
567
569
  async function setVar(key, value) {
568
570
  if (!key) {
569
- console.error('\n Usage: thepopebot set-var <KEY> [VALUE]\n');
570
- console.error(' Example: thepopebot set-var LLM_MODEL claude-sonnet-4-5-20250929\n');
571
+ console.error('\n Usage: harbinger set-var <KEY> [VALUE]\n');
572
+ console.error(' Example: harbinger set-var LLM_MODEL claude-sonnet-4-5-20250929\n');
571
573
  process.exit(1);
572
574
  }
573
575
 
package/bin/local.sh CHANGED
@@ -2,7 +2,7 @@
2
2
  set -e
3
3
 
4
4
  PACKAGE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
5
- DEV_DIR="${1:-/tmp/thepopebot.local}"
5
+ DEV_DIR="${1:-/tmp/harbinger.local}"
6
6
  ENV_BACKUP="/tmp/env.$(uuidgen)"
7
7
 
8
8
  HAS_ENV=false
@@ -17,7 +17,7 @@ cd "$DEV_DIR"
17
17
 
18
18
  node "$PACKAGE_DIR/bin/cli.js" init
19
19
 
20
- sed -i '' "s|\"thepopebot\": \".*\"|\"thepopebot\": \"file:$PACKAGE_DIR\"|" package.json
20
+ sed -i '' "s|\"@harbinger-ai/harbinger\": \".*\"|\"@harbinger-ai/harbinger\": \"file:$PACKAGE_DIR\"|" package.json
21
21
 
22
22
  rm -rf node_modules package-lock.json
23
23
  npm install --install-links
@@ -8,20 +8,20 @@ const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = path.dirname(__filename);
9
9
 
10
10
  // postinstall runs from the package dir inside node_modules.
11
- // The user's project root is two levels up: node_modules/thepopebot/ -> project root
12
- const projectRoot = path.resolve(__dirname, '..', '..', '..');
11
+ // The user's project root is two levels up: node_modules/@harbinger-ai/harbinger/ -> project root
12
+ const projectRoot = path.resolve(__dirname, '..', '..', '..', '..');
13
13
  const templatesDir = path.join(__dirname, '..', 'templates');
14
14
 
15
15
  // Skip if templates dir doesn't exist (shouldn't happen, but be safe)
16
16
  if (!fs.existsSync(templatesDir)) process.exit(0);
17
17
 
18
- // Skip if this doesn't look like a user project (no package.json with thepopebot dep)
18
+ // Skip if this doesn't look like a user project (no package.json with harbinger dep)
19
19
  const pkgPath = path.join(projectRoot, 'package.json');
20
20
  if (!fs.existsSync(pkgPath)) process.exit(0);
21
21
  try {
22
22
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
23
23
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
24
- if (!deps || !deps.thepopebot) process.exit(0);
24
+ if (!deps || (!deps['@harbinger-ai/harbinger'] && !deps.thepopebot)) process.exit(0);
25
25
  } catch { process.exit(0); }
26
26
 
27
27
  function walk(dir) {
@@ -52,12 +52,12 @@ for (const relPath of walk(templatesDir)) {
52
52
  }
53
53
 
54
54
  if (changed.length > 0) {
55
- console.log('\n thepopebot: these project files differ from the latest package templates.');
56
- console.log(' This is normal if you\'ve customized them. If thepopebot was just');
55
+ console.log('\n Harbinger: these project files differ from the latest package templates.');
56
+ console.log(' This is normal if you\'ve customized them. If Harbinger was just');
57
57
  console.log(' updated, new defaults may be available.\n');
58
58
  for (const file of changed) {
59
59
  console.log(` ${file}`);
60
60
  }
61
- console.log('\n To compare: npx thepopebot diff <file>');
62
- console.log(' To restore: npx thepopebot reset <file>\n');
61
+ console.log('\n To compare: npx harbinger diff <file>');
62
+ console.log(' To restore: npx harbinger reset <file>\n');
63
63
  }
package/config/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  /**
2
- * Next.js config wrapper for thepopebot.
2
+ * Next.js config wrapper for Harbinger.
3
3
  * Enables instrumentation hook for cron scheduling on server start.
4
4
  *
5
5
  * Usage in user's next.config.mjs:
6
- * import { withThepopebot } from 'thepopebot/config';
7
- * export default withThepopebot({});
6
+ * import { withHarbinger } from '@harbinger-ai/harbinger/config';
7
+ * export default withHarbinger({});
8
+ *
9
+ * Legacy alias also available:
10
+ * import { withThepopebot } from '@harbinger-ai/harbinger/config';
8
11
  *
9
12
  * @param {Object} nextConfig - User's Next.js config
10
13
  * @returns {Object} Enhanced Next.js config
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Next.js instrumentation hook for thepopebot.
2
+ * Next.js instrumentation hook for Harbinger.
3
3
  * This file is loaded by Next.js on server start when instrumentationHook is enabled.
4
4
  *
5
5
  * Users should create an instrumentation.js in their project root that imports this:
6
6
  *
7
- * export { register } from 'thepopebot/instrumentation';
7
+ * export { register } from '@harbinger-ai/harbinger/instrumentation';
8
8
  *
9
9
  * Or they can re-export and add their own logic.
10
10
  */
@@ -48,19 +48,19 @@ export async function register() {
48
48
  const agents = discoverAgents();
49
49
  if (agents.length > 0) {
50
50
  const names = agents.map(a => a.codename || a.id).join(', ');
51
- console.log(`thepopebot: ${agents.length} agent profiles loaded (${names})`);
52
- console.log('thepopebot: use @AGENT_NAME in chat to route to a specific agent');
51
+ console.log(`harbinger: ${agents.length} agent profiles loaded (${names})`);
52
+ console.log('harbinger: use @AGENT_NAME in chat to route to a specific agent');
53
53
  }
54
54
 
55
55
  // Optionally start autonomous thinking engine
56
56
  if (process.env.AUTONOMOUS_THINKING === 'true') {
57
57
  const { startAutonomousEngine } = await import('../lib/ai/autonomous-engine.js');
58
58
  startAutonomousEngine({
59
- agentId: 'thepopebot',
60
- agentName: 'THEPOPEBOT',
59
+ agentId: 'harbinger',
60
+ agentName: 'HARBINGER',
61
61
  interval: Number(process.env.AUTONOMOUS_INTERVAL) || 60000,
62
62
  });
63
- console.log('thepopebot: autonomous thinking engine started');
63
+ console.log('harbinger: autonomous thinking engine started');
64
64
  }
65
65
 
66
66
  // Start cron scheduler
@@ -77,17 +77,17 @@ export async function register() {
77
77
  const stored = getAvailableVersion();
78
78
  if (stored) setUpdateAvailable(stored);
79
79
  } catch (err) {
80
- console.warn('thepopebot: update check warm-up failed:', err.message);
80
+ console.warn('harbinger: update check warm-up failed:', err.message);
81
81
  }
82
82
 
83
83
  // Pre-warm MCP client (load external tools)
84
84
  try {
85
85
  const { loadMcpTools } = await import('../lib/mcp/client.js');
86
86
  const mcpTools = await loadMcpTools();
87
- if (mcpTools.length > 0) console.log(`thepopebot: ${mcpTools.length} MCP tools loaded`);
87
+ if (mcpTools.length > 0) console.log(`harbinger: ${mcpTools.length} MCP tools loaded`);
88
88
  } catch (err) {
89
- console.warn('thepopebot: MCP tools pre-warm failed:', err.message);
89
+ console.warn('harbinger: MCP tools pre-warm failed:', err.message);
90
90
  }
91
91
 
92
- console.log('thepopebot initialized');
92
+ console.log('harbinger initialized');
93
93
  }
@@ -202,6 +202,25 @@ export async function triggerUpgrade() {
202
202
  return { success: true };
203
203
  }
204
204
 
205
+ // ─────────────────────────────────────────────────────────────────────────────
206
+ // Agent profile actions
207
+ // ─────────────────────────────────────────────────────────────────────────────
208
+
209
+ /**
210
+ * Get all discovered agent profiles for Mission Control.
211
+ * @returns {Promise<object[]>}
212
+ */
213
+ export async function getAgentProfiles() {
214
+ await requireAuth();
215
+ try {
216
+ const { discoverAgents } = await import('../agents.js');
217
+ return discoverAgents();
218
+ } catch (err) {
219
+ console.error('Failed to discover agents:', err);
220
+ return [];
221
+ }
222
+ }
223
+
205
224
  // ─────────────────────────────────────────────────────────────────────────────
206
225
  // API Key actions
207
226
  // ─────────────────────────────────────────────────────────────────────────────
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect } from "react";
4
- import { CirclePlusIcon, PanelLeftIcon, MessageIcon, BellIcon, SwarmIcon, ArrowUpCircleIcon, LifeBuoyIcon, CrosshairIcon, ShieldIcon, PackageIcon } from "./icons.js";
4
+ import { CirclePlusIcon, PanelLeftIcon, MessageIcon, BellIcon, SwarmIcon, ArrowUpCircleIcon, LifeBuoyIcon, CrosshairIcon, ShieldIcon, PackageIcon, CommandIcon } from "./icons.js";
5
5
  import { getUnreadNotificationCount, getAppVersion } from "../actions.js";
6
6
  import { SidebarHistory } from "./sidebar-history.js";
7
7
  import { SidebarUserNav } from "./sidebar-user-nav.js";
@@ -112,6 +112,22 @@ function AppSidebar({ user }) {
112
112
  ) }),
113
113
  collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Swarm" })
114
114
  ] }) }),
115
+ /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
116
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
117
+ SidebarMenuButton,
118
+ {
119
+ className: collapsed ? "justify-center" : "",
120
+ onClick: () => {
121
+ window.location.href = "/mission-control";
122
+ },
123
+ children: [
124
+ /* @__PURE__ */ jsx(CommandIcon, { size: 16 }),
125
+ !collapsed && /* @__PURE__ */ jsx("span", { children: "Mission Control" })
126
+ ]
127
+ }
128
+ ) }),
129
+ collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Mission Control" })
130
+ ] }) }),
115
131
  /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
116
132
  /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
117
133
  SidebarMenuButton,
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect } from 'react';
4
- import { CirclePlusIcon, PanelLeftIcon, MessageIcon, BellIcon, SwarmIcon, ArrowUpCircleIcon, LifeBuoyIcon, CrosshairIcon, ShieldIcon, PackageIcon } from './icons.js';
4
+ import { CirclePlusIcon, PanelLeftIcon, MessageIcon, BellIcon, SwarmIcon, ArrowUpCircleIcon, LifeBuoyIcon, CrosshairIcon, ShieldIcon, PackageIcon, CommandIcon } from './icons.js';
5
5
  import { getUnreadNotificationCount, getAppVersion } from '../actions.js';
6
6
  import { SidebarHistory } from './sidebar-history.js';
7
7
  import { SidebarUserNav } from './sidebar-user-nav.js';
@@ -129,6 +129,24 @@ export function AppSidebar({ user }) {
129
129
  </Tooltip>
130
130
  </SidebarMenuItem>
131
131
 
132
+ {/* Mission Control */}
133
+ <SidebarMenuItem>
134
+ <Tooltip>
135
+ <TooltipTrigger asChild>
136
+ <SidebarMenuButton
137
+ className={collapsed ? 'justify-center' : ''}
138
+ onClick={() => { window.location.href = '/mission-control'; }}
139
+ >
140
+ <CommandIcon size={16} />
141
+ {!collapsed && <span>Mission Control</span>}
142
+ </SidebarMenuButton>
143
+ </TooltipTrigger>
144
+ {collapsed && (
145
+ <TooltipContent side="right">Mission Control</TooltipContent>
146
+ )}
147
+ </Tooltip>
148
+ </SidebarMenuItem>
149
+
132
150
  {/* Targets */}
133
151
  <SidebarMenuItem>
134
152
  <Tooltip>