@harbinger-ai/harbinger 0.1.0 → 0.1.1

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 (51) 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/components/sidebar-user-nav.js +1 -1
  7. package/lib/chat/components/sidebar-user-nav.jsx +1 -1
  8. package/lib/chat/components/upgrade-dialog.js +2 -2
  9. package/lib/chat/components/upgrade-dialog.jsx +2 -2
  10. package/lib/cron.js +11 -7
  11. package/lib/db/index.js +6 -1
  12. package/lib/mcp/actions.js +1 -1
  13. package/lib/mcp/handler.js +2 -2
  14. package/lib/mcp/server.js +1 -1
  15. package/lib/paths.js +1 -1
  16. package/package.json +1 -1
  17. package/templates/.env.example +4 -4
  18. package/templates/.github/workflows/rebuild-event-handler.yml +20 -20
  19. package/templates/.github/workflows/run-job.yml +6 -6
  20. package/templates/.github/workflows/upgrade-event-handler.yml +12 -12
  21. package/templates/CLAUDE.md +3 -3
  22. package/templates/CLAUDE.md.template +9 -9
  23. package/templates/app/api/[...thepopebot]/route.js +1 -1
  24. package/templates/app/api/auth/[...nextauth]/route.js +1 -1
  25. package/templates/app/chat/[chatId]/page.js +2 -2
  26. package/templates/app/chats/page.js +2 -2
  27. package/templates/app/components/setup-form.jsx +1 -1
  28. package/templates/app/findings/page.js +2 -2
  29. package/templates/app/globals.css +1 -1
  30. package/templates/app/layout.js +1 -1
  31. package/templates/app/login/page.js +1 -1
  32. package/templates/app/notifications/page.js +2 -2
  33. package/templates/app/page.js +2 -2
  34. package/templates/app/settings/crons/page.js +1 -1
  35. package/templates/app/settings/layout.js +2 -2
  36. package/templates/app/settings/mcp/page.js +1 -1
  37. package/templates/app/settings/secrets/page.js +1 -1
  38. package/templates/app/settings/triggers/page.js +1 -1
  39. package/templates/app/stream/chat/route.js +1 -1
  40. package/templates/app/swarm/page.js +2 -2
  41. package/templates/app/targets/page.js +2 -2
  42. package/templates/app/toolbox/page.js +2 -2
  43. package/templates/config/AGENT.md +2 -2
  44. package/templates/config/EVENT_HANDLER.md +3 -3
  45. package/templates/config/SKILL_BUILDING_GUIDE.md +1 -1
  46. package/templates/config/SOUL.md +1 -1
  47. package/templates/docker/event-handler/Dockerfile +1 -1
  48. package/templates/docker-compose.yml +2 -2
  49. package/templates/instrumentation.js +1 -1
  50. package/templates/middleware.js +1 -1
  51. 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
  }
@@ -39,7 +39,7 @@ function SidebarUserNav({ user, collapsed }) {
39
39
  theme === "dark" ? /* @__PURE__ */ jsx(SunIcon, { size: 14 }) : /* @__PURE__ */ jsx(MoonIcon, { size: 14 }),
40
40
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: theme === "dark" ? "Light Mode" : "Dark Mode" })
41
41
  ] }),
42
- /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: () => window.open("https://github.com/stephengpope/thepopebot/issues", "_blank"), children: [
42
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: () => window.open("https://github.com/Haribinger/bot/issues", "_blank"), children: [
43
43
  /* @__PURE__ */ jsx(BugIcon, { size: 14 }),
44
44
  /* @__PURE__ */ jsx("span", { className: "ml-2", children: "Report Issues" })
45
45
  ] }),
@@ -53,7 +53,7 @@ export function SidebarUserNav({ user, collapsed }) {
53
53
  <span className="ml-2">{theme === 'dark' ? 'Light Mode' : 'Dark Mode'}</span>
54
54
  </DropdownMenuItem>
55
55
  )}
56
- <DropdownMenuItem onClick={() => window.open('https://github.com/stephengpope/thepopebot/issues', '_blank')}>
56
+ <DropdownMenuItem onClick={() => window.open('https://github.com/Haribinger/bot/issues', '_blank')}>
57
57
  <BugIcon size={14} />
58
58
  <span className="ml-2">Report Issues</span>
59
59
  </DropdownMenuItem>
@@ -71,7 +71,7 @@ function UpgradeDialog({ open, onClose, version, updateAvailable, changelog }) {
71
71
  /* @__PURE__ */ jsx(
72
72
  "a",
73
73
  {
74
- href: "https://github.com/stephengpope/thepopebot?tab=readme-ov-file#understanding-init",
74
+ href: "https://github.com/Haribinger/bot?tab=readme-ov-file#understanding-init",
75
75
  target: "_blank",
76
76
  rel: "noopener noreferrer",
77
77
  className: "text-emerald-500 hover:underline",
@@ -87,7 +87,7 @@ function UpgradeDialog({ open, onClose, version, updateAvailable, changelog }) {
87
87
  /* @__PURE__ */ jsx(
88
88
  "a",
89
89
  {
90
- href: "https://github.com/stephengpope/thepopebot?tab=readme-ov-file#manual-updating",
90
+ href: "https://github.com/Haribinger/bot?tab=readme-ov-file#manual-updating",
91
91
  target: "_blank",
92
92
  rel: "noopener noreferrer",
93
93
  className: "text-emerald-500 hover:underline",
@@ -78,7 +78,7 @@ export function UpgradeDialog({ open, onClose, version, updateAvailable, changel
78
78
  <p>
79
79
  Some files (prompts, crons, triggers) won't be auto-updated to avoid breaking your bot.{' '}
80
80
  <a
81
- href="https://github.com/stephengpope/thepopebot?tab=readme-ov-file#understanding-init"
81
+ href="https://github.com/Haribinger/bot?tab=readme-ov-file#understanding-init"
82
82
  target="_blank"
83
83
  rel="noopener noreferrer"
84
84
  className="text-emerald-500 hover:underline"
@@ -92,7 +92,7 @@ export function UpgradeDialog({ open, onClose, version, updateAvailable, changel
92
92
  <p className="text-xs text-muted-foreground">
93
93
  If you hit unrecoverable errors, see the{' '}
94
94
  <a
95
- href="https://github.com/stephengpope/thepopebot?tab=readme-ov-file#manual-updating"
95
+ href="https://github.com/Haribinger/bot?tab=readme-ov-file#manual-updating"
96
96
  target="_blank"
97
97
  rel="noopener noreferrer"
98
98
  className="text-emerald-500 hover:underline"
package/lib/cron.js CHANGED
@@ -5,8 +5,12 @@ import { cronsFile, cronDir } from './paths.js';
5
5
  import { executeAction } from './actions.js';
6
6
 
7
7
  function getInstalledVersion() {
8
- const pkgPath = path.join(process.cwd(), 'node_modules', 'thepopebot', 'package.json');
9
- return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
8
+ const names = ['@harbinger-ai/harbinger', 'thepopebot'];
9
+ for (const name of names) {
10
+ const pkgPath = path.join(process.cwd(), 'node_modules', ...name.split('/'), 'package.json');
11
+ try { return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version; } catch {}
12
+ }
13
+ return '0.0.0';
10
14
  }
11
15
 
12
16
  // In-memory flag for available update (read by sidebar, written by cron)
@@ -98,7 +102,7 @@ function compareVersions(a, b) {
98
102
  async function fetchAndStoreReleaseNotes(target) {
99
103
  try {
100
104
  const ghRes = await fetch(
101
- `https://api.github.com/repos/stephengpope/thepopebot/releases/tags/v${target}`
105
+ `https://api.github.com/repos/Haribinger/bot/releases/tags/v${target}`
102
106
  );
103
107
  if (!ghRes.ok) return;
104
108
  const release = await ghRes.json();
@@ -110,7 +114,7 @@ async function fetchAndStoreReleaseNotes(target) {
110
114
  }
111
115
 
112
116
  /**
113
- * Check npm registry for a newer version of thepopebot.
117
+ * Check npm registry for a newer version of harbinger.
114
118
  */
115
119
  async function runVersionCheck() {
116
120
  try {
@@ -119,8 +123,8 @@ async function runVersionCheck() {
119
123
  if (isPrerelease(installed)) {
120
124
  // Beta path: check both stable and beta dist-tags
121
125
  const results = await Promise.allSettled([
122
- fetch('https://registry.npmjs.org/thepopebot/latest'),
123
- fetch('https://registry.npmjs.org/thepopebot/beta'),
126
+ fetch('https://registry.npmjs.org/@harbinger-ai%2fharbinger/latest'),
127
+ fetch('https://registry.npmjs.org/@harbinger-ai%2fharbinger/beta'),
124
128
  ]);
125
129
 
126
130
  const candidates = [];
@@ -151,7 +155,7 @@ async function runVersionCheck() {
151
155
  }
152
156
  } else {
153
157
  // Stable path: existing logic, untouched
154
- const res = await fetch('https://registry.npmjs.org/thepopebot/latest');
158
+ const res = await fetch('https://registry.npmjs.org/@harbinger-ai%2fharbinger/latest');
155
159
  if (!res.ok) {
156
160
  console.warn(`[version check] npm registry returned ${res.status}`);
157
161
  return;
package/lib/db/index.js CHANGED
@@ -41,7 +41,12 @@ export function initDatabase() {
41
41
 
42
42
  // Resolve migrations folder from the installed package.
43
43
  // import.meta.url doesn't survive webpack bundling, so resolve from PROJECT_ROOT.
44
- const migrationsFolder = path.join(PROJECT_ROOT, 'node_modules', 'thepopebot', 'drizzle');
44
+ // Try the new scoped package name first, fall back to the legacy name.
45
+ const packageNames = ['@harbinger-ai/harbinger', 'thepopebot'];
46
+ const migrationsFolder = packageNames
47
+ .map(name => path.join(PROJECT_ROOT, 'node_modules', ...name.split('/'), 'drizzle'))
48
+ .find(p => { try { return fs.existsSync(p); } catch { return false; } })
49
+ || path.join(PROJECT_ROOT, 'node_modules', '@harbinger-ai', 'harbinger', 'drizzle');
45
50
 
46
51
  migrate(db, { migrationsFolder });
47
52
 
@@ -44,7 +44,7 @@ export async function getMcpStatus() {
44
44
  }
45
45
 
46
46
  /**
47
- * Get metadata about thepopebot's own MCP server capabilities.
47
+ * Get metadata about Harbinger's own MCP server capabilities.
48
48
  */
49
49
  export async function getOwnMcpServerInfo() {
50
50
  await requireAuth();
@@ -40,9 +40,9 @@ export async function handleMcpRequest(request) {
40
40
 
41
41
  if (method === 'GET') {
42
42
  return Response.json({
43
- name: 'thepopebot',
43
+ name: 'harbinger',
44
44
  version,
45
- description: 'thepopebot MCP server — autonomous AI agent platform',
45
+ description: 'Harbinger MCP server — autonomous AI agent platform',
46
46
  tools: ['create_job', 'get_job_status', 'chat', 'list_agents', 'get_agent_profile'],
47
47
  resources: ['agent://agents', 'agent://{agentId}/soul', 'config://soul', 'config://crons', 'config://triggers'],
48
48
  prompts: ['agent-prompt'],
package/lib/mcp/server.js CHANGED
@@ -12,7 +12,7 @@ const { version } = require('../../package.json');
12
12
  */
13
13
  export function createMcpServer() {
14
14
  const server = new McpServer({
15
- name: 'thepopebot',
15
+ name: 'harbinger',
16
16
  version,
17
17
  });
18
18
 
package/lib/paths.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from 'path';
2
2
 
3
3
  /**
4
- * Central path resolver for thepopebot.
4
+ * Central path resolver for Harbinger.
5
5
  * All paths resolve from process.cwd() (the user's project root).
6
6
  */
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harbinger-ai/harbinger",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Create autonomous AI agents with a two-layer architecture: Next.js Event Handler + Docker Agent.",
6
6
  "bin": {
@@ -1,4 +1,4 @@
1
- # thepopebot Configuration
1
+ # Harbinger Configuration
2
2
  # Copy this file to .env and fill in your values
3
3
  # NEVER commit the actual .env file with real secrets!
4
4
 
@@ -55,9 +55,9 @@ TELEGRAM_CHAT_ID=
55
55
  # If not set, Traefik uses a self-signed certificate
56
56
  # LETSENCRYPT_EMAIL=
57
57
 
58
- # thepopebot version — auto-set by postinstall, used by docker-compose for image tags
59
- # THEPOPEBOT_VERSION=
58
+ # Harbinger version — auto-set by postinstall, used by docker-compose for image tags
59
+ # HARBINGER_VERSION=
60
60
 
61
- # Custom Docker images (optional, overrides the default stephengpope/thepopebot images)
61
+ # Custom Docker images (optional, overrides the default harbinger-ai/harbinger images)
62
62
  # EVENT_HANDLER_IMAGE_URL=
63
63
  # JOB_IMAGE_URL=
@@ -16,7 +16,7 @@ jobs:
16
16
  - name: Pull latest and detect version change
17
17
  id: pull
18
18
  run: |
19
- docker exec thepopebot-event-handler bash -c '
19
+ docker exec harbinger-event-handler bash -c '
20
20
  export GH_TOKEN=$(grep "^GH_TOKEN=" /app/.env | cut -d= -f2-)
21
21
  echo "${GH_TOKEN}" | gh auth login --with-token
22
22
  gh auth setup-git
@@ -29,35 +29,35 @@ jobs:
29
29
  exit 0
30
30
  fi
31
31
 
32
- # Detect thepopebot version change from package-lock.json in git
32
+ # Detect harbinger version change from package-lock.json in git
33
33
  git show HEAD:package-lock.json > /tmp/old-lock.json 2>/dev/null || echo "{}" > /tmp/old-lock.json
34
34
  git show origin/main:package-lock.json > /tmp/new-lock.json
35
- OLD_TPB=$(node -p "try{require(\"/tmp/old-lock.json\").packages[\"node_modules/thepopebot\"]?.version||\"\"}catch(e){\"\"}")
36
- NEW_TPB=$(node -p "try{require(\"/tmp/new-lock.json\").packages[\"node_modules/thepopebot\"]?.version||\"\"}catch(e){\"\"}")
35
+ OLD_TPB=$(node -p "try{require(\"/tmp/old-lock.json\").packages[\"node_modules/@harbinger-ai/harbinger\"]?.version||\"\"}catch(e){\"\"}")
36
+ NEW_TPB=$(node -p "try{require(\"/tmp/new-lock.json\").packages[\"node_modules/@harbinger-ai/harbinger\"]?.version||\"\"}catch(e){\"\"}")
37
37
  rm -f /tmp/old-lock.json /tmp/new-lock.json
38
38
 
39
39
  git reset --hard origin/main
40
40
 
41
41
  if [ -n "$OLD_TPB" ] && [ "$OLD_TPB" != "$NEW_TPB" ]; then
42
- # Version changed — run thepopebot init to scaffold new templates
42
+ # Version changed — run harbinger init to scaffold new templates
43
43
  if echo "$NEW_TPB" | grep -q "-"; then
44
- npx --yes "thepopebot@${NEW_TPB}" init
44
+ npx --yes "@harbinger-ai/harbinger@${NEW_TPB}" init
45
45
  else
46
- npx --yes thepopebot@latest init
46
+ npx --yes @harbinger-ai/harbinger@latest init
47
47
  fi
48
48
 
49
49
  # Commit any template changes from init
50
50
  git add -A
51
51
  if ! git diff --cached --quiet; then
52
- git commit -m "chore: apply thepopebot init after upgrade"
52
+ git commit -m "chore: apply harbinger init after upgrade"
53
53
  git push origin main
54
54
  fi
55
55
 
56
- # Update THEPOPEBOT_VERSION in .env so docker compose pulls the right image
57
- if grep -q "^THEPOPEBOT_VERSION=" /app/.env; then
58
- sed -i "s/^THEPOPEBOT_VERSION=.*/THEPOPEBOT_VERSION=$NEW_TPB/" /app/.env
56
+ # Update HARBINGER_VERSION in .env so docker compose pulls the right image
57
+ if grep -q "^HARBINGER_VERSION=" /app/.env; then
58
+ sed -i "s/^HARBINGER_VERSION=.*/HARBINGER_VERSION=$NEW_TPB/" /app/.env
59
59
  else
60
- echo "THEPOPEBOT_VERSION=$NEW_TPB" >> /app/.env
60
+ echo "HARBINGER_VERSION=$NEW_TPB" >> /app/.env
61
61
  fi
62
62
  echo "VERSION_CHANGED" > /app/.rebuild-status
63
63
  else
@@ -66,14 +66,14 @@ jobs:
66
66
  fi
67
67
  '
68
68
 
69
- STATUS=$(docker exec thepopebot-event-handler cat /app/.rebuild-status)
70
- docker exec thepopebot-event-handler rm -f /app/.rebuild-status
69
+ STATUS=$(docker exec harbinger-event-handler cat /app/.rebuild-status)
70
+ docker exec harbinger-event-handler rm -f /app/.rebuild-status
71
71
  echo "status=$STATUS" >> $GITHUB_OUTPUT
72
72
 
73
73
  - name: Rebuild (no version change)
74
74
  if: steps.pull.outputs.status == 'REBUILD'
75
75
  run: |
76
- docker exec thepopebot-event-handler bash -c '
76
+ docker exec harbinger-event-handler bash -c '
77
77
  rm -rf .next-new .next-old
78
78
  NEXT_BUILD_DIR=.next-new npm run build
79
79
 
@@ -89,10 +89,10 @@ jobs:
89
89
  - name: Pull new image and restart container
90
90
  if: steps.pull.outputs.status == 'VERSION_CHANGED'
91
91
  run: |
92
- HOST_DIR=$(docker inspect thepopebot-event-handler --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}')
92
+ HOST_DIR=$(docker inspect harbinger-event-handler --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}')
93
93
  docker compose -f /project/docker-compose.yml --project-directory "$HOST_DIR" --env-file /project/.env pull event-handler
94
- docker stop thepopebot-event-handler || true
95
- docker rm thepopebot-event-handler || true
94
+ docker stop harbinger-event-handler || true
95
+ docker rm harbinger-event-handler || true
96
96
  docker compose -f /project/docker-compose.yml --project-directory "$HOST_DIR" --env-file /project/.env up -d event-handler
97
97
 
98
98
  - name: Rebuild in new container
@@ -100,13 +100,13 @@ jobs:
100
100
  run: |
101
101
  echo "Waiting for new container..."
102
102
  for i in $(seq 1 30); do
103
- if docker exec thepopebot-event-handler echo "ready" 2>/dev/null; then
103
+ if docker exec harbinger-event-handler echo "ready" 2>/dev/null; then
104
104
  break
105
105
  fi
106
106
  sleep 2
107
107
  done
108
108
 
109
- docker exec thepopebot-event-handler bash -c '
109
+ docker exec harbinger-event-handler bash -c '
110
110
  npm install --omit=dev
111
111
 
112
112
  rm -rf .next-new .next-old
@@ -21,10 +21,10 @@ jobs:
21
21
  logs/*/job.config.json
22
22
  sparse-checkout-cone-mode: false
23
23
 
24
- - name: Get thepopebot version
24
+ - name: Get harbinger version
25
25
  id: version
26
26
  run: |
27
- VERSION=$(jq -r '.packages["node_modules/thepopebot"].version // "latest"' package-lock.json)
27
+ VERSION=$(jq -r '.packages["node_modules/@harbinger-ai/harbinger"].version // "latest"' package-lock.json)
28
28
  echo "tag=$VERSION" >> $GITHUB_OUTPUT
29
29
 
30
30
  - name: Read job config overrides
@@ -47,11 +47,11 @@ jobs:
47
47
  username: ${{ github.actor }}
48
48
  password: ${{ secrets.GITHUB_TOKEN }}
49
49
 
50
- - name: Run thepopebot Agent
50
+ - name: Run Harbinger Agent
51
51
  env:
52
52
  ALL_SECRETS: ${{ toJson(secrets) }}
53
53
  JOB_IMAGE_URL: ${{ vars.JOB_IMAGE_URL }}
54
- THEPOPEBOT_VERSION: ${{ steps.version.outputs.tag }}
54
+ HARBINGER_VERSION: ${{ steps.version.outputs.tag }}
55
55
  LLM_MODEL: ${{ steps.job-config.outputs.llm_model || vars.LLM_MODEL }}
56
56
  LLM_PROVIDER: ${{ steps.job-config.outputs.llm_provider || vars.LLM_PROVIDER }}
57
57
  OPENAI_BASE_URL: ${{ vars.OPENAI_BASE_URL }}
@@ -61,9 +61,9 @@ jobs:
61
61
  if [ -n "$JOB_IMAGE_URL" ]; then
62
62
  IMAGE="${JOB_IMAGE_URL}:latest"
63
63
  elif [ "$AGENT_BACKEND" = "claude-code" ]; then
64
- IMAGE="stephengpope/thepopebot:job-claude-code-${THEPOPEBOT_VERSION}"
64
+ IMAGE="harbinger-ai/harbinger:job-claude-code-${HARBINGER_VERSION}"
65
65
  else
66
- IMAGE="stephengpope/thepopebot:job-${THEPOPEBOT_VERSION}"
66
+ IMAGE="harbinger-ai/harbinger:job-${HARBINGER_VERSION}"
67
67
  fi
68
68
  echo "Using image: $IMAGE"
69
69
 
@@ -17,9 +17,9 @@ jobs:
17
17
  runs-on: self-hosted
18
18
  timeout-minutes: 20
19
19
  steps:
20
- - name: Upgrade thepopebot
20
+ - name: Upgrade Harbinger
21
21
  run: |
22
- docker exec thepopebot-event-handler bash -c '
22
+ docker exec harbinger-event-handler bash -c '
23
23
  export GH_TOKEN=$(grep "^GH_TOKEN=" /app/.env | cut -d= -f2-)
24
24
  echo "${GH_TOKEN}" | gh auth login --with-token
25
25
  gh auth setup-git
@@ -29,33 +29,33 @@ jobs:
29
29
  git clone --depth 1 "$REPO_URL" "$WORK_DIR"
30
30
  cd "$WORK_DIR"
31
31
 
32
- git config user.name "thepopebot"
33
- git config user.email "thepopebot@users.noreply.github.com"
32
+ git config user.name "harbinger"
33
+ git config user.email "harbinger@users.noreply.github.com"
34
34
 
35
35
  npm install --omit=dev
36
- OLD_VERSION=$(node -p "require(\"./node_modules/thepopebot/package.json\").version")
36
+ OLD_VERSION=$(node -p "require(\"./node_modules/@harbinger-ai/harbinger/package.json\").version")
37
37
 
38
38
  TARGET="${{ github.event.inputs.target_version }}"
39
39
  if [ -n "$TARGET" ] && echo "$TARGET" | grep -q "-"; then
40
40
  # Beta: npm update cannot upgrade an exact-pinned dependency, must use install
41
- npm install "thepopebot@${TARGET}" --prefer-online
41
+ npm install "@harbinger-ai/harbinger@${TARGET}" --prefer-online
42
42
  else
43
43
  # Stable: npm update resolves to latest within semver range
44
- npm update thepopebot --prefer-online
44
+ npm update @harbinger-ai/harbinger --prefer-online
45
45
  fi
46
46
 
47
- NEW_VERSION=$(node -p "require(\"./node_modules/thepopebot/package.json\").version")
47
+ NEW_VERSION=$(node -p "require(\"./node_modules/@harbinger-ai/harbinger/package.json\").version")
48
48
 
49
49
  if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
50
- BRANCH="upgrade/thepopebot-${NEW_VERSION}-$(date +%s)"
50
+ BRANCH="upgrade/harbinger-${NEW_VERSION}-$(date +%s)"
51
51
  git add -A
52
52
  git checkout -b "$BRANCH"
53
- git commit -m "chore: upgrade thepopebot to $NEW_VERSION"
53
+ git commit -m "chore: upgrade harbinger to $NEW_VERSION"
54
54
  git push origin "$BRANCH"
55
- gh pr create --title "chore: upgrade thepopebot" --body "Automated upgrade via upgrade-event-handler workflow." --base main --head "$BRANCH"
55
+ gh pr create --title "chore: upgrade harbinger" --body "Automated upgrade via upgrade-event-handler workflow." --base main --head "$BRANCH"
56
56
  gh pr merge "$BRANCH" --squash --auto --delete-branch
57
57
  else
58
- echo "No changes — thepopebot is already up to date."
58
+ echo "No changes — harbinger is already up to date."
59
59
  fi
60
60
 
61
61
  rm -rf "$WORK_DIR"
@@ -1,6 +1,6 @@
1
1
  # Templates Directory — Scaffolding Only
2
2
 
3
- This directory contains files that get copied into user projects when they run `npx thepopebot init`. It is **not** where event handler logic, API routes, or core features live.
3
+ This directory contains files that get copied into user projects when they run `npx harbinger init`. It is **not** where event handler logic, API routes, or core features live.
4
4
 
5
5
  ## Rules
6
6
 
@@ -10,7 +10,7 @@ This directory contains files that get copied into user projects when they run `
10
10
 
11
11
  ## What belongs here
12
12
 
13
- - **Next.js wiring**: `next.config.mjs`, `instrumentation.js`, catch-all route, middleware — thin re-exports from `thepopebot/*`
13
+ - **Next.js wiring**: `next.config.mjs`, `instrumentation.js`, catch-all route, middleware — thin re-exports from `@harbinger-ai/harbinger/*`
14
14
  - **User-editable config**: `config/SOUL.md`, `config/EVENT_HANDLER.md`, `config/CRONS.json`, `config/TRIGGERS.json`, etc.
15
15
  - **GitHub Actions workflows**: `.github/workflows/`
16
16
  - **Docker files**: `docker/`, `docker-compose.yml`
@@ -24,6 +24,6 @@ This directory contains files that get copied into user projects when they run `
24
24
  - Database operations
25
25
  - LLM/AI integrations
26
26
  - Tool implementations
27
- - Anything that should be shared across all users via `npm update thepopebot`
27
+ - Anything that should be shared across all users via `npm update @harbinger-ai/harbinger`
28
28
 
29
29
  If you're adding a feature to the event handler, put it in the package. Templates just wire into it.
@@ -2,26 +2,26 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- This is an autonomous AI agent powered by [thepopebot](https://github.com/stephengpope/thepopebot). It uses a **two-layer architecture**:
5
+ This is an autonomous AI agent powered by [Harbinger](https://github.com/harbinger-ai/harbinger). It uses a **two-layer architecture**:
6
6
 
7
7
  1. **Event Handler** — A Next.js server that orchestrates everything: web UI, Telegram chat, cron scheduling, webhook triggers, and job creation.
8
8
  2. **Docker Agent** — A container that runs the Pi coding agent for autonomous task execution. Each job gets its own branch, container, and PR.
9
9
 
10
- All core logic lives in the `thepopebot` npm package. This project is a scaffolded shell — thin Next.js wiring, user-editable configuration, GitHub Actions workflows, and Docker files.
10
+ All core logic lives in the `@harbinger-ai/harbinger` npm package. This project is a scaffolded shell — thin Next.js wiring, user-editable configuration, GitHub Actions workflows, and Docker files.
11
11
 
12
12
  ## Directory Structure
13
13
 
14
14
  ```
15
15
  project-root/
16
16
  ├── CLAUDE.md # This file (project documentation)
17
- ├── next.config.mjs # Next.js config (wraps withThepopebot())
17
+ ├── next.config.mjs # Next.js config (wraps withHarbinger())
18
18
  ├── instrumentation.js # Server startup hook (re-exports from package)
19
19
  ├── middleware.js # Auth middleware (re-exports from package)
20
20
  ├── .env # API keys and tokens (gitignored)
21
21
  ├── package.json
22
22
 
23
23
  ├── app/ # Next.js app directory (page shells + client components)
24
- │ ├── api/[...thepopebot]/route.js # Catch-all API route (re-exports from package)
24
+ │ ├── api/[...thepopebot]/route.js # Catch-all API route (re-exports from @harbinger-ai/harbinger/api)
25
25
  │ ├── stream/chat/route.js # Chat streaming endpoint (session auth)
26
26
  │ └── components/ # Client-side components
27
27
 
@@ -44,7 +44,7 @@ project-root/
44
44
  ├── cron/ # Scripts for command-type cron actions
45
45
  ├── triggers/ # Scripts for command-type trigger actions
46
46
  ├── logs/ # Per-job output (logs/<JOB_ID>/job.md + session .jsonl)
47
- └── data/ # SQLite database (data/thepopebot.sqlite)
47
+ └── data/ # SQLite database (data/harbinger.sqlite)
48
48
  ```
49
49
 
50
50
  ## Two-Layer Architecture
@@ -228,7 +228,7 @@ NextAuth v5 with Credentials provider (email/password), JWT in httpOnly cookies.
228
228
 
229
229
  ## Database
230
230
 
231
- SQLite via Drizzle ORM at `data/thepopebot.sqlite`. Auto-initialized and auto-migrated on server startup. Tables: `users`, `chats`, `messages`, `notifications`, `subscriptions`, `settings` (key-value store, also stores API keys). Column naming: camelCase in JS → snake_case in SQL.
231
+ SQLite via Drizzle ORM at `data/harbinger.sqlite`. Auto-initialized and auto-migrated on server startup. Tables: `users`, `chats`, `messages`, `notifications`, `subscriptions`, `settings` (key-value store, also stores API keys). Column naming: camelCase in JS → snake_case in SQL.
232
232
 
233
233
  ## GitHub Actions Workflows
234
234
 
@@ -236,7 +236,7 @@ SQLite via Drizzle ORM at `data/thepopebot.sqlite`. Auto-initialized and auto-mi
236
236
  |----------|---------|---------|
237
237
  | `run-job.yml` | `job/*` branch created | Runs the Docker agent container |
238
238
  | `rebuild-event-handler.yml` | Push to `main` | Rebuilds server (fast path or Docker restart) |
239
- | `upgrade-event-handler.yml` | Manual `workflow_dispatch` | Creates PR to upgrade thepopebot package |
239
+ | `upgrade-event-handler.yml` | Manual `workflow_dispatch` | Creates PR to upgrade Harbinger package |
240
240
  | `build-image.yml` | `docker/job-pi-coding-agent/**` changes | Builds Pi coding agent Docker image to GHCR |
241
241
  | `auto-merge.yml` | Job PR opened | Squash-merges if changes are within `ALLOWED_PATHS` |
242
242
  | `notify-pr-complete.yml` | After `auto-merge.yml` | Sends job completion notification |
@@ -261,8 +261,8 @@ SQLite via Drizzle ORM at `data/thepopebot.sqlite`. Auto-initialized and auto-mi
261
261
  | `APP_URL` | Public URL for the event handler | Required |
262
262
  | `AUTO_MERGE` | Set to `"false"` to disable auto-merge | Enabled |
263
263
  | `ALLOWED_PATHS` | Comma-separated path prefixes for auto-merge | `/logs` |
264
- | `JOB_IMAGE_URL` | Docker image for job agent (GHCR URLs trigger auto-builds) | Default thepopebot image |
265
- | `EVENT_HANDLER_IMAGE_URL` | Docker image for event handler | Default thepopebot image |
264
+ | `JOB_IMAGE_URL` | Docker image for job agent (GHCR URLs trigger auto-builds) | Default Harbinger image |
265
+ | `EVENT_HANDLER_IMAGE_URL` | Docker image for event handler | Default Harbinger image |
266
266
  | `RUNS_ON` | GitHub Actions runner label | `ubuntu-latest` |
267
267
  | `LLM_PROVIDER` | LLM provider for Docker agent | `anthropic` |
268
268
  | `LLM_MODEL` | LLM model name for Docker agent | Provider default |
@@ -1 +1 @@
1
- export { GET, POST } from 'thepopebot/api';
1
+ export { GET, POST } from '@harbinger-ai/harbinger/api';
@@ -1 +1 @@
1
- export { GET, POST } from 'thepopebot/auth';
1
+ export { GET, POST } from '@harbinger-ai/harbinger/auth';
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { ChatPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { ChatPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function ChatRoute({ params }) {
5
5
  const { chatId } = await params;
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { ChatsPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { ChatsPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function ChatsRoute() {
5
5
  const session = await auth();
@@ -6,7 +6,7 @@ import { Button } from './ui/button';
6
6
  import { Input } from './ui/input';
7
7
  import { Label } from './ui/label';
8
8
  import { Card, CardHeader, CardTitle, CardDescription, CardContent } from './ui/card';
9
- import { setupAdmin } from 'thepopebot/auth/actions';
9
+ import { setupAdmin } from '@harbinger-ai/harbinger/auth/actions';
10
10
 
11
11
  export function SetupForm() {
12
12
  const router = useRouter();
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { FindingsPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { FindingsPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function FindingsRoute() {
5
5
  const session = await auth();
@@ -1,6 +1,6 @@
1
1
  @import "tailwindcss";
2
2
  @import "streamdown/styles.css";
3
- @source "../node_modules/thepopebot/lib/**/*.{js,jsx}";
3
+ @source "../node_modules/@harbinger-ai/harbinger/lib/**/*.{js,jsx}";
4
4
  @source "../node_modules/streamdown/dist/**/*.js";
5
5
 
6
6
  :root {
@@ -2,7 +2,7 @@ import './globals.css';
2
2
  import { ThemeProvider } from './components/theme-provider';
3
3
 
4
4
  export const metadata = {
5
- title: 'thepopebot',
5
+ title: 'Harbinger',
6
6
  description: 'AI Agent',
7
7
  };
8
8
 
@@ -1,4 +1,4 @@
1
- import { getPageAuthState } from 'thepopebot/auth';
1
+ import { getPageAuthState } from '@harbinger-ai/harbinger/auth';
2
2
  import { AsciiLogo } from '../components/ascii-logo';
3
3
  import { SetupForm } from '../components/setup-form';
4
4
  import { LoginForm } from '../components/login-form';
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { NotificationsPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { NotificationsPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function NotificationsRoute() {
5
5
  const session = await auth();
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { ChatPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { ChatPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function Home() {
5
5
  const session = await auth();
@@ -1,4 +1,4 @@
1
- import { CronsPage } from 'thepopebot/chat';
1
+ import { CronsPage } from '@harbinger-ai/harbinger/chat';
2
2
 
3
3
  export default function SettingsCronsRoute() {
4
4
  return <CronsPage />;
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { SettingsLayout } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { SettingsLayout } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function Layout({ children }) {
5
5
  const session = await auth();
@@ -1,4 +1,4 @@
1
- import { McpPage } from 'thepopebot/chat';
1
+ import { McpPage } from '@harbinger-ai/harbinger/chat';
2
2
 
3
3
  export default function SettingsMcpRoute() {
4
4
  return <McpPage />;
@@ -1,4 +1,4 @@
1
- import { SettingsSecretsPage } from 'thepopebot/chat';
1
+ import { SettingsSecretsPage } from '@harbinger-ai/harbinger/chat';
2
2
 
3
3
  export default function SecretsRoute() {
4
4
  return <SettingsSecretsPage />;
@@ -1,4 +1,4 @@
1
- import { TriggersPage } from 'thepopebot/chat';
1
+ import { TriggersPage } from '@harbinger-ai/harbinger/chat';
2
2
 
3
3
  export default function SettingsTriggersRoute() {
4
4
  return <TriggersPage />;
@@ -1 +1 @@
1
- export { POST } from 'thepopebot/chat/api';
1
+ export { POST } from '@harbinger-ai/harbinger/chat/api';
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { SwarmPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { SwarmPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function SwarmRoute() {
5
5
  const session = await auth();
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { TargetsPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { TargetsPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function TargetsRoute() {
5
5
  const session = await auth();
@@ -1,5 +1,5 @@
1
- import { auth } from 'thepopebot/auth';
2
- import { RegistryPage } from 'thepopebot/chat';
1
+ import { auth } from '@harbinger-ai/harbinger/auth';
2
+ import { RegistryPage } from '@harbinger-ai/harbinger/chat';
3
3
 
4
4
  export default async function ToolboxRoute() {
5
5
  const session = await auth();
@@ -1,4 +1,4 @@
1
- # thepopebot Agent Environment
1
+ # Harbinger Agent Environment
2
2
 
3
3
  **This document describes what you are and your operating environment**
4
4
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## 1. What You Are
8
8
 
9
- You are **thepopebot**, an autonomous AI agent running inside a Docker container.
9
+ You are **Harbinger**, an autonomous AI agent running inside a Docker container.
10
10
  - You have full access to the machine and anything it can do to get the job done.
11
11
 
12
12
  ---
@@ -104,7 +104,7 @@ If a skill needs an API key:
104
104
 
105
105
  1. **Tell the user** what credential is needed and where to get it
106
106
  2. **Suggest setting it up now** so the skill can be tested in the same job:
107
- - Run: `npx thepopebot set-agent-llm-secret <KEY_NAME> <value>`
107
+ - Run: `npx harbinger set-agent-llm-secret <KEY_NAME> <value>`
108
108
  - The value is stored exactly as provided, no transformation needed
109
109
  - This creates a GitHub secret with the `AGENT_LLM_` prefix — the Docker container exposes it as an environment variable (e.g., `AGENT_LLM_BRAVE_API_KEY` → `BRAVE_API_KEY`)
110
110
  - They can rotate the key later with the same command
@@ -172,7 +172,7 @@ This applies to every job — including simple or obvious tasks. Even if the use
172
172
  >
173
173
  > If you set it up now, I can build AND test the skill in one job:
174
174
  > ```
175
- > npx thepopebot set-agent-llm-secret SLACK_WEBHOOK_URL <your-url>
175
+ > npx harbinger set-agent-llm-secret SLACK_WEBHOOK_URL <your-url>
176
176
  > ```
177
177
  > (You can rotate this later with the same command.)
178
178
  >
@@ -205,7 +205,7 @@ New skill creation:
205
205
  > 1. Create `SKILL.md` with frontmatter (name: slack-post, description: "Post messages to Slack channels via incoming webhook.") and usage docs referencing `skills/slack-post/post.sh <message>`
206
206
  > 2. Create `post.sh` — bash script that takes a message argument, sends it to the Slack webhook URL via curl using $SLACK_WEBHOOK_URL. Make it executable.
207
207
  > 3. Activate: `ln -s ../slack-post skills/active/slack-post`
208
- > 4. Test: run `skills/slack-post/post.sh "test message from thepopebot"` and verify successful delivery. Fix any issues before committing.
208
+ > 4. Test: run `skills/slack-post/post.sh "test message from harbinger"` and verify successful delivery. Fix any issues before committing.
209
209
 
210
210
  ---
211
211
 
@@ -84,7 +84,7 @@ Tell the agent to test the skill with real input after creating it and fix any i
84
84
  ## Credential setup
85
85
 
86
86
  If a skill needs an API key, the user should set it up BEFORE the job runs:
87
- - `npx thepopebot set-agent-llm-secret <KEY_NAME> <value>` — creates a GitHub secret with `AGENT_LLM_` prefix, exposed as an env var in the Docker container
87
+ - `npx harbinger set-agent-llm-secret <KEY_NAME> <value>` — creates a GitHub secret with `AGENT_LLM_` prefix, exposed as an env var in the Docker container
88
88
  - The value is stored exactly as provided, no transformation needed
89
89
  - Also add to `.env` for local development
90
90
  - Keys can be rotated later with the same command
@@ -1,4 +1,4 @@
1
- # thepopebot Soul
1
+ # Harbinger Soul
2
2
 
3
3
  ## Identity
4
4
 
@@ -12,7 +12,7 @@ RUN npm install -g pm2
12
12
  WORKDIR /app
13
13
  COPY package.json package-lock.json* ./
14
14
  RUN npm install --omit=dev && \
15
- npm install --no-save thepopebot@$(node -p "require('./package.json').version")
15
+ npm install --no-save @harbinger-ai/harbinger@$(node -p "require('./package.json').version")
16
16
 
17
17
  COPY /templates/docker/event-handler/ecosystem.config.cjs /opt/ecosystem.config.cjs
18
18
 
@@ -22,8 +22,8 @@ services:
22
22
  restart: unless-stopped
23
23
 
24
24
  event-handler:
25
- container_name: thepopebot-event-handler
26
- image: ${EVENT_HANDLER_IMAGE_URL:-stephengpope/thepopebot:event-handler-${THEPOPEBOT_VERSION:-latest}}
25
+ container_name: harbinger-event-handler
26
+ image: ${EVENT_HANDLER_IMAGE_URL:-harbinger-ai/harbinger:event-handler-${HARBINGER_VERSION:-latest}}
27
27
  volumes:
28
28
  - .:/app
29
29
  - /app/node_modules
@@ -1,6 +1,6 @@
1
1
  export async function register() {
2
2
  if (process.env.NEXT_RUNTIME === 'nodejs') {
3
- const { register } = await import('thepopebot/instrumentation');
3
+ const { register } = await import('@harbinger-ai/harbinger/instrumentation');
4
4
  await register();
5
5
  }
6
6
  }
@@ -1 +1 @@
1
- export { middleware, config } from 'thepopebot/middleware';
1
+ export { middleware, config } from '@harbinger-ai/harbinger/middleware';
@@ -1,3 +1,3 @@
1
- import { withThepopebot } from 'thepopebot/config';
1
+ import { withHarbinger } from '@harbinger-ai/harbinger/config';
2
2
 
3
- export default withThepopebot({});
3
+ export default withHarbinger({});