@harbinger-ai/harbinger 0.1.0

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 (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/agents/README.md +76 -0
  4. package/agents/_template/CONFIG.yaml +7 -0
  5. package/agents/_template/HEARTBEAT.md +59 -0
  6. package/agents/_template/IDENTITY.md +4 -0
  7. package/agents/_template/SKILLS.md +1 -0
  8. package/agents/_template/SOUL.md +25 -0
  9. package/agents/_template/TOOLS.md +3 -0
  10. package/agents/binary-reverser/CONFIG.yaml +21 -0
  11. package/agents/binary-reverser/HEARTBEAT.md +65 -0
  12. package/agents/binary-reverser/IDENTITY.md +1 -0
  13. package/agents/binary-reverser/SKILLS.md +1 -0
  14. package/agents/binary-reverser/SOUL.md +23 -0
  15. package/agents/binary-reverser/TOOLS.md +99 -0
  16. package/agents/browser-agent/CONFIG.yaml +20 -0
  17. package/agents/browser-agent/HEARTBEAT.md +79 -0
  18. package/agents/browser-agent/IDENTITY.md +5 -0
  19. package/agents/browser-agent/SKILLS.md +86 -0
  20. package/agents/browser-agent/SOUL.md +23 -0
  21. package/agents/browser-agent/TOOLS.md +186 -0
  22. package/agents/cloud-infiltrator/CONFIG.yaml +22 -0
  23. package/agents/cloud-infiltrator/HEARTBEAT.md +78 -0
  24. package/agents/cloud-infiltrator/IDENTITY.md +1 -0
  25. package/agents/cloud-infiltrator/SKILLS.md +1 -0
  26. package/agents/cloud-infiltrator/SOUL.md +23 -0
  27. package/agents/cloud-infiltrator/TOOLS.md +68 -0
  28. package/agents/coding-assistant/CONFIG.yaml +22 -0
  29. package/agents/coding-assistant/HEARTBEAT.md +57 -0
  30. package/agents/coding-assistant/IDENTITY.md +5 -0
  31. package/agents/coding-assistant/SKILLS.md +69 -0
  32. package/agents/coding-assistant/SOUL.md +60 -0
  33. package/agents/coding-assistant/TOOLS.md +168 -0
  34. package/agents/learning-agent/CONFIG.yaml +21 -0
  35. package/agents/learning-agent/HEARTBEAT.md +63 -0
  36. package/agents/learning-agent/IDENTITY.md +5 -0
  37. package/agents/learning-agent/SKILLS.md +86 -0
  38. package/agents/learning-agent/SOUL.md +77 -0
  39. package/agents/learning-agent/TOOLS.md +145 -0
  40. package/agents/maintainer/CONFIG.yaml +31 -0
  41. package/agents/maintainer/HEARTBEAT.md +28 -0
  42. package/agents/maintainer/IDENTITY.md +33 -0
  43. package/agents/maintainer/SKILLS.md +24 -0
  44. package/agents/maintainer/SOUL.md +61 -0
  45. package/agents/maintainer/TOOLS.md +29 -0
  46. package/agents/maintainer/lib/engine.js +279 -0
  47. package/agents/maintainer/lib/safe-fixer.js +183 -0
  48. package/agents/morning-brief/CONFIG.yaml +22 -0
  49. package/agents/morning-brief/HEARTBEAT.md +60 -0
  50. package/agents/morning-brief/IDENTITY.md +5 -0
  51. package/agents/morning-brief/SKILLS.md +56 -0
  52. package/agents/morning-brief/SOUL.md +64 -0
  53. package/agents/morning-brief/TOOLS.md +112 -0
  54. package/agents/osint-detective/CONFIG.yaml +24 -0
  55. package/agents/osint-detective/HEARTBEAT.md +66 -0
  56. package/agents/osint-detective/IDENTITY.md +1 -0
  57. package/agents/osint-detective/SKILLS.md +1 -0
  58. package/agents/osint-detective/SOUL.md +23 -0
  59. package/agents/osint-detective/TOOLS.md +81 -0
  60. package/agents/recon-scout/CONFIG.yaml +22 -0
  61. package/agents/recon-scout/HEARTBEAT.md +79 -0
  62. package/agents/recon-scout/IDENTITY.md +1 -0
  63. package/agents/recon-scout/SKILLS.md +1 -0
  64. package/agents/recon-scout/SOUL.md +23 -0
  65. package/agents/recon-scout/TOOLS.md +93 -0
  66. package/agents/report-writer/CONFIG.yaml +21 -0
  67. package/agents/report-writer/HEARTBEAT.md +63 -0
  68. package/agents/report-writer/IDENTITY.md +1 -0
  69. package/agents/report-writer/SKILLS.md +1 -0
  70. package/agents/report-writer/SOUL.md +23 -0
  71. package/agents/report-writer/TOOLS.md +69 -0
  72. package/agents/shared/README.md +13 -0
  73. package/agents/web-hacker/CONFIG.yaml +24 -0
  74. package/agents/web-hacker/HEARTBEAT.md +78 -0
  75. package/agents/web-hacker/IDENTITY.md +1 -0
  76. package/agents/web-hacker/SKILLS.md +1 -0
  77. package/agents/web-hacker/SOUL.md +23 -0
  78. package/agents/web-hacker/TOOLS.md +86 -0
  79. package/api/CLAUDE.md +19 -0
  80. package/api/index.js +274 -0
  81. package/bin/cli.js +620 -0
  82. package/bin/local.sh +31 -0
  83. package/bin/postinstall.js +63 -0
  84. package/config/index.js +24 -0
  85. package/config/instrumentation.js +93 -0
  86. package/drizzle/0000_initial.sql +52 -0
  87. package/drizzle/0001_bounty_and_registry.sql +82 -0
  88. package/drizzle/0002_sync_columns.sql +7 -0
  89. package/drizzle/0003_graceful_bloodscream.sql +86 -0
  90. package/drizzle/meta/0000_snapshot.json +321 -0
  91. package/drizzle/meta/0003_snapshot.json +878 -0
  92. package/drizzle/meta/_journal.json +34 -0
  93. package/drizzle/relations.ts +3 -0
  94. package/drizzle/schema.ts +145 -0
  95. package/lib/actions.js +47 -0
  96. package/lib/agents.js +166 -0
  97. package/lib/ai/agent.js +96 -0
  98. package/lib/ai/autonomous-engine.js +261 -0
  99. package/lib/ai/index.js +359 -0
  100. package/lib/ai/model-router.js +254 -0
  101. package/lib/ai/model.js +73 -0
  102. package/lib/ai/tools.js +84 -0
  103. package/lib/auth/actions.js +28 -0
  104. package/lib/auth/config.js +27 -0
  105. package/lib/auth/edge-config.js +27 -0
  106. package/lib/auth/index.js +27 -0
  107. package/lib/auth/middleware.js +53 -0
  108. package/lib/bounty/actions.js +119 -0
  109. package/lib/bounty/findings.js +64 -0
  110. package/lib/bounty/programs.js +34 -0
  111. package/lib/bounty/sync-targets.js +267 -0
  112. package/lib/bounty/targets.js +33 -0
  113. package/lib/channels/base.js +56 -0
  114. package/lib/channels/index.js +15 -0
  115. package/lib/channels/telegram.js +148 -0
  116. package/lib/chat/actions.js +288 -0
  117. package/lib/chat/api.js +135 -0
  118. package/lib/chat/components/app-sidebar.js +237 -0
  119. package/lib/chat/components/app-sidebar.jsx +289 -0
  120. package/lib/chat/components/chat-header.js +27 -0
  121. package/lib/chat/components/chat-header.jsx +37 -0
  122. package/lib/chat/components/chat-input.js +230 -0
  123. package/lib/chat/components/chat-input.jsx +228 -0
  124. package/lib/chat/components/chat-nav-context.js +11 -0
  125. package/lib/chat/components/chat-nav-context.jsx +11 -0
  126. package/lib/chat/components/chat-page.js +81 -0
  127. package/lib/chat/components/chat-page.jsx +100 -0
  128. package/lib/chat/components/chat.js +150 -0
  129. package/lib/chat/components/chat.jsx +182 -0
  130. package/lib/chat/components/chats-page.js +302 -0
  131. package/lib/chat/components/chats-page.jsx +330 -0
  132. package/lib/chat/components/crons-page.js +172 -0
  133. package/lib/chat/components/crons-page.jsx +244 -0
  134. package/lib/chat/components/enhanced-tool-call.js +103 -0
  135. package/lib/chat/components/enhanced-tool-call.jsx +139 -0
  136. package/lib/chat/components/findings-page.js +175 -0
  137. package/lib/chat/components/findings-page.jsx +214 -0
  138. package/lib/chat/components/greeting.js +22 -0
  139. package/lib/chat/components/greeting.jsx +26 -0
  140. package/lib/chat/components/icons.js +777 -0
  141. package/lib/chat/components/icons.jsx +741 -0
  142. package/lib/chat/components/index.js +26 -0
  143. package/lib/chat/components/mcp-page.js +260 -0
  144. package/lib/chat/components/mcp-page.jsx +355 -0
  145. package/lib/chat/components/message.js +289 -0
  146. package/lib/chat/components/message.jsx +315 -0
  147. package/lib/chat/components/messages.js +66 -0
  148. package/lib/chat/components/messages.jsx +77 -0
  149. package/lib/chat/components/notifications-page.js +56 -0
  150. package/lib/chat/components/notifications-page.jsx +87 -0
  151. package/lib/chat/components/page-layout.js +21 -0
  152. package/lib/chat/components/page-layout.jsx +28 -0
  153. package/lib/chat/components/registry-page.js +222 -0
  154. package/lib/chat/components/registry-page.jsx +255 -0
  155. package/lib/chat/components/settings-layout.js +40 -0
  156. package/lib/chat/components/settings-layout.jsx +54 -0
  157. package/lib/chat/components/settings-secrets-page.js +216 -0
  158. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  159. package/lib/chat/components/sidebar-history-item.js +132 -0
  160. package/lib/chat/components/sidebar-history-item.jsx +113 -0
  161. package/lib/chat/components/sidebar-history.js +115 -0
  162. package/lib/chat/components/sidebar-history.jsx +157 -0
  163. package/lib/chat/components/sidebar-user-nav.js +63 -0
  164. package/lib/chat/components/sidebar-user-nav.jsx +73 -0
  165. package/lib/chat/components/status-bar.js +39 -0
  166. package/lib/chat/components/status-bar.jsx +51 -0
  167. package/lib/chat/components/swarm-page.js +157 -0
  168. package/lib/chat/components/swarm-page.jsx +210 -0
  169. package/lib/chat/components/targets-page.js +376 -0
  170. package/lib/chat/components/targets-page.jsx +389 -0
  171. package/lib/chat/components/tool-call.js +86 -0
  172. package/lib/chat/components/tool-call.jsx +104 -0
  173. package/lib/chat/components/tool-panel.js +107 -0
  174. package/lib/chat/components/tool-panel.jsx +145 -0
  175. package/lib/chat/components/triggers-page.js +153 -0
  176. package/lib/chat/components/triggers-page.jsx +221 -0
  177. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  178. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  179. package/lib/chat/components/ui/dropdown-menu.js +98 -0
  180. package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
  181. package/lib/chat/components/ui/rename-dialog.js +74 -0
  182. package/lib/chat/components/ui/rename-dialog.jsx +72 -0
  183. package/lib/chat/components/ui/scroll-area.js +13 -0
  184. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  185. package/lib/chat/components/ui/separator.js +21 -0
  186. package/lib/chat/components/ui/separator.jsx +18 -0
  187. package/lib/chat/components/ui/sheet.js +75 -0
  188. package/lib/chat/components/ui/sheet.jsx +95 -0
  189. package/lib/chat/components/ui/sidebar.js +227 -0
  190. package/lib/chat/components/ui/sidebar.jsx +245 -0
  191. package/lib/chat/components/ui/tooltip.js +56 -0
  192. package/lib/chat/components/ui/tooltip.jsx +66 -0
  193. package/lib/chat/components/upgrade-dialog.js +151 -0
  194. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  195. package/lib/chat/utils.js +11 -0
  196. package/lib/cron.js +246 -0
  197. package/lib/db/api-keys.js +163 -0
  198. package/lib/db/chats.js +145 -0
  199. package/lib/db/index.js +52 -0
  200. package/lib/db/notifications.js +99 -0
  201. package/lib/db/schema.js +145 -0
  202. package/lib/db/update-check.js +96 -0
  203. package/lib/db/users.js +89 -0
  204. package/lib/mcp/actions.js +104 -0
  205. package/lib/mcp/client.js +79 -0
  206. package/lib/mcp/handler.js +57 -0
  207. package/lib/mcp/server.js +165 -0
  208. package/lib/paths.js +46 -0
  209. package/lib/registry/actions.js +164 -0
  210. package/lib/registry/catalog.js +137 -0
  211. package/lib/registry/tools.js +71 -0
  212. package/lib/tools/create-job.js +99 -0
  213. package/lib/tools/github.js +217 -0
  214. package/lib/tools/openai.js +35 -0
  215. package/lib/tools/telegram.js +292 -0
  216. package/lib/triggers.js +118 -0
  217. package/lib/utils/render-md.js +102 -0
  218. package/package.json +103 -0
  219. package/setup/lib/auth.mjs +81 -0
  220. package/setup/lib/env.mjs +21 -0
  221. package/setup/lib/fs-utils.mjs +20 -0
  222. package/setup/lib/github.mjs +149 -0
  223. package/setup/lib/prerequisites.mjs +155 -0
  224. package/setup/lib/prompts.mjs +267 -0
  225. package/setup/lib/providers.mjs +48 -0
  226. package/setup/lib/sync.mjs +125 -0
  227. package/setup/lib/targets.mjs +45 -0
  228. package/setup/lib/telegram-verify.mjs +63 -0
  229. package/setup/lib/telegram.mjs +76 -0
  230. package/setup/setup-telegram.mjs +264 -0
  231. package/setup/setup.mjs +842 -0
  232. package/templates/.dockerignore +5 -0
  233. package/templates/.env.example +63 -0
  234. package/templates/.github/workflows/auto-merge.yml +117 -0
  235. package/templates/.github/workflows/build-image.yml +36 -0
  236. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  237. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  238. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  239. package/templates/.github/workflows/run-job.yml +89 -0
  240. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  241. package/templates/.gitignore.template +45 -0
  242. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  243. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  244. package/templates/CLAUDE.md +29 -0
  245. package/templates/CLAUDE.md.template +307 -0
  246. package/templates/app/api/[...thepopebot]/route.js +1 -0
  247. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  248. package/templates/app/chat/[chatId]/page.js +8 -0
  249. package/templates/app/chats/page.js +7 -0
  250. package/templates/app/components/ascii-logo.jsx +10 -0
  251. package/templates/app/components/login-form.jsx +92 -0
  252. package/templates/app/components/setup-form.jsx +82 -0
  253. package/templates/app/components/theme-provider.jsx +11 -0
  254. package/templates/app/components/theme-toggle.jsx +38 -0
  255. package/templates/app/components/ui/button.jsx +21 -0
  256. package/templates/app/components/ui/card.jsx +23 -0
  257. package/templates/app/components/ui/input.jsx +10 -0
  258. package/templates/app/components/ui/label.jsx +10 -0
  259. package/templates/app/crons/page.js +5 -0
  260. package/templates/app/findings/page.js +7 -0
  261. package/templates/app/globals.css +90 -0
  262. package/templates/app/layout.js +19 -0
  263. package/templates/app/login/page.js +15 -0
  264. package/templates/app/notifications/page.js +7 -0
  265. package/templates/app/page.js +7 -0
  266. package/templates/app/settings/crons/page.js +5 -0
  267. package/templates/app/settings/layout.js +7 -0
  268. package/templates/app/settings/mcp/page.js +5 -0
  269. package/templates/app/settings/page.js +5 -0
  270. package/templates/app/settings/secrets/page.js +5 -0
  271. package/templates/app/settings/triggers/page.js +5 -0
  272. package/templates/app/stream/chat/route.js +1 -0
  273. package/templates/app/swarm/page.js +7 -0
  274. package/templates/app/targets/page.js +7 -0
  275. package/templates/app/toolbox/page.js +7 -0
  276. package/templates/app/triggers/page.js +5 -0
  277. package/templates/config/AGENT.md +34 -0
  278. package/templates/config/CRONS.json +56 -0
  279. package/templates/config/EVENT_HANDLER.md +224 -0
  280. package/templates/config/HEARTBEAT.md +3 -0
  281. package/templates/config/JOB_SUMMARY.md +130 -0
  282. package/templates/config/MCP_SERVERS.json +1 -0
  283. package/templates/config/SKILL_BUILDING_GUIDE.md +90 -0
  284. package/templates/config/SOUL.md +17 -0
  285. package/templates/config/TRIGGERS.json +58 -0
  286. package/templates/docker/event-handler/Dockerfile +20 -0
  287. package/templates/docker/event-handler/ecosystem.config.cjs +8 -0
  288. package/templates/docker/job-claude-code/Dockerfile +34 -0
  289. package/templates/docker/job-claude-code/entrypoint.sh +139 -0
  290. package/templates/docker/job-pi-coding-agent/Dockerfile +44 -0
  291. package/templates/docker/job-pi-coding-agent/entrypoint.sh +163 -0
  292. package/templates/docker-compose.yml +63 -0
  293. package/templates/instrumentation.js +6 -0
  294. package/templates/middleware.js +1 -0
  295. package/templates/next.config.mjs +3 -0
  296. package/templates/postcss.config.mjs +5 -0
  297. package/templates/skills/LICENSE +21 -0
  298. package/templates/skills/README.md +119 -0
  299. package/templates/skills/brave-search/SKILL.md +79 -0
  300. package/templates/skills/brave-search/content.js +86 -0
  301. package/templates/skills/brave-search/package-lock.json +621 -0
  302. package/templates/skills/brave-search/package.json +14 -0
  303. package/templates/skills/brave-search/search.js +199 -0
  304. package/templates/skills/browser-tools/SKILL.md +196 -0
  305. package/templates/skills/browser-tools/browser-content.js +103 -0
  306. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  307. package/templates/skills/browser-tools/browser-eval.js +53 -0
  308. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  309. package/templates/skills/browser-tools/browser-nav.js +44 -0
  310. package/templates/skills/browser-tools/browser-pick.js +162 -0
  311. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  312. package/templates/skills/browser-tools/browser-start.js +87 -0
  313. package/templates/skills/browser-tools/package-lock.json +2556 -0
  314. package/templates/skills/browser-tools/package.json +19 -0
  315. package/templates/skills/llm-secrets/SKILL.md +34 -0
  316. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  317. package/templates/skills/modify-self/SKILL.md +12 -0
@@ -0,0 +1,163 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Extract job ID from branch name (job/uuid -> uuid), fallback to random UUID
5
+ if [[ "$BRANCH" == job/* ]]; then
6
+ JOB_ID="${BRANCH#job/}"
7
+ else
8
+ JOB_ID=$(cat /proc/sys/kernel/random/uuid)
9
+ fi
10
+ echo "Job ID: ${JOB_ID}"
11
+
12
+ # Export SECRETS (JSON) as flat env vars (GH_TOKEN, ANTHROPIC_API_KEY, etc.)
13
+ # These are filtered from LLM's bash subprocess by env-sanitizer extension
14
+ if [ -n "$SECRETS" ]; then
15
+ eval $(echo "$SECRETS" | jq -r 'to_entries | .[] | "export \(.key)=\(.value | @sh)"')
16
+ fi
17
+
18
+ # Export LLM_SECRETS (JSON) as flat env vars
19
+ # These are NOT filtered - LLM can access these (browser logins, skill API keys, etc.)
20
+ if [ -n "$LLM_SECRETS" ]; then
21
+ eval $(echo "$LLM_SECRETS" | jq -r 'to_entries | .[] | "export \(.key)=\(.value | @sh)"')
22
+ fi
23
+
24
+ # Git setup - derive identity from GitHub token
25
+ gh auth setup-git
26
+ GH_USER_JSON=$(gh api user -q '{name: .name, login: .login, email: .email, id: .id}')
27
+ GH_USER_NAME=$(echo "$GH_USER_JSON" | jq -r '.name // .login')
28
+ GH_USER_EMAIL=$(echo "$GH_USER_JSON" | jq -r '.email // "\(.id)+\(.login)@users.noreply.github.com"')
29
+ git config --global user.name "$GH_USER_NAME"
30
+ git config --global user.email "$GH_USER_EMAIL"
31
+
32
+ # Clone branch
33
+ if [ -n "$REPO_URL" ]; then
34
+ git clone --single-branch --branch "$BRANCH" --depth 1 "$REPO_URL" /job
35
+ else
36
+ echo "No REPO_URL provided"
37
+ fi
38
+
39
+ cd /job
40
+
41
+ # Create temp directory for agent use (gitignored via tmp/)
42
+ mkdir -p /job/tmp
43
+
44
+ # Install npm deps for active skills (native deps need correct Linux arch)
45
+ for skill_dir in /job/skills/active/*/; do
46
+ if [ -f "${skill_dir}package.json" ]; then
47
+ echo "Installing skill deps: $(basename "$skill_dir")"
48
+ (cd "$skill_dir" && npm install --omit=dev --no-package-lock)
49
+ fi
50
+ done
51
+
52
+ # Start Chrome if available (installed by browser-tools skill via Puppeteer)
53
+ CHROME_PID=""
54
+ CHROME_BIN=$(find /root/.cache/puppeteer -name "chrome" -type f 2>/dev/null | head -1)
55
+ if [ -n "$CHROME_BIN" ]; then
56
+ $CHROME_BIN --headless --no-sandbox --disable-gpu --remote-debugging-port=9222 2>/dev/null &
57
+ CHROME_PID=$!
58
+ sleep 2
59
+ fi
60
+
61
+ # Setup logs
62
+ LOG_DIR="/job/logs/${JOB_ID}"
63
+ mkdir -p "${LOG_DIR}"
64
+
65
+ # 1. Build system prompt from config MD files
66
+ SYSTEM_FILES=("SOUL.md" "AGENT.md")
67
+ > /job/.pi/SYSTEM.md
68
+ for i in "${!SYSTEM_FILES[@]}"; do
69
+ cat "/job/config/${SYSTEM_FILES[$i]}" >> /job/.pi/SYSTEM.md
70
+ if [ "$i" -lt $((${#SYSTEM_FILES[@]} - 1)) ]; then
71
+ echo -e "\n\n" >> /job/.pi/SYSTEM.md
72
+ fi
73
+ done
74
+
75
+ # Resolve {{datetime}} variable in SYSTEM.md
76
+ sed -i "s/{{datetime}}/$(date -u +"%Y-%m-%dT%H:%M:%SZ")/g" /job/.pi/SYSTEM.md
77
+
78
+ # Read job metadata from job.config.json
79
+ JOB_CONFIG="/job/logs/${JOB_ID}/job.config.json"
80
+ TITLE=$(jq -r '.title // empty' "$JOB_CONFIG")
81
+ JOB_DESCRIPTION=$(jq -r '.job // empty' "$JOB_CONFIG")
82
+
83
+ PROMPT="
84
+
85
+ # Your Job
86
+
87
+ ${JOB_DESCRIPTION}"
88
+
89
+ LLM_PROVIDER="${LLM_PROVIDER:-anthropic}"
90
+
91
+ MODEL_FLAGS="--provider $LLM_PROVIDER"
92
+ if [ -n "$LLM_MODEL" ]; then
93
+ MODEL_FLAGS="$MODEL_FLAGS --model $LLM_MODEL"
94
+ fi
95
+
96
+ # Generate models.json for custom provider (OpenAI-compatible endpoints like Ollama)
97
+ if [ "$LLM_PROVIDER" = "custom" ] && [ -n "$OPENAI_BASE_URL" ]; then
98
+ # If no API key was provided, set a dummy so Pi doesn't send empty auth
99
+ if [ -z "$CUSTOM_API_KEY" ]; then
100
+ export CUSTOM_API_KEY="not-needed"
101
+ fi
102
+ cat > /root/.pi/agent/models.json <<MODELS
103
+ {
104
+ "providers": {
105
+ "custom": {
106
+ "baseUrl": "$OPENAI_BASE_URL",
107
+ "api": "openai-completions",
108
+ "apiKey": "CUSTOM_API_KEY",
109
+ "models": [{ "id": "$LLM_MODEL" }]
110
+ }
111
+ }
112
+ }
113
+ MODELS
114
+ fi
115
+
116
+ # Copy custom models.json to PI's global config if present in repo (overrides generated)
117
+ if [ -f "/job/.pi/agent/models.json" ]; then
118
+ mkdir -p /root/.pi/agent
119
+ cp /job/.pi/agent/models.json /root/.pi/agent/models.json
120
+ fi
121
+
122
+ # Run Pi — capture exit code instead of letting set -e kill the script
123
+ set +e
124
+ pi $MODEL_FLAGS -p "$PROMPT" --session-dir "${LOG_DIR}"
125
+ PI_EXIT=$?
126
+
127
+ # 2. Commit based on outcome
128
+ if [ $PI_EXIT -ne 0 ]; then
129
+ # Pi failed — only commit session logs, not partial code changes
130
+ git reset || true
131
+ git add -f "${LOG_DIR}"
132
+ git commit -m "🤖 Agent Job: ${TITLE} (failed)" || true
133
+ else
134
+ # Pi succeeded — commit everything
135
+ git add -A
136
+ git add -f "${LOG_DIR}"
137
+ git commit -m "🤖 Agent Job: ${TITLE}" || true
138
+ fi
139
+
140
+ git push origin
141
+ set -e
142
+
143
+ # 3. Merge (pi has memory of job via session)
144
+ #if [ -n "$REPO_URL" ] && [ -f "/job/MERGE_JOB.md" ]; then
145
+ # echo "MERGED"
146
+ # pi -p "$(cat /job/MERGE_JOB.md)" --session-dir "${LOG_DIR}" --continue
147
+ #fi
148
+
149
+ # 5. Create PR (auto-merge handled by GitHub Actions workflow)
150
+ gh pr create --title "🤖 Agent Job: ${TITLE}" --body "${JOB_DESCRIPTION}" --base main || true
151
+
152
+ # Cleanup
153
+ if [ -n "$CHROME_PID" ]; then
154
+ kill $CHROME_PID 2>/dev/null || true
155
+ fi
156
+
157
+ # Re-raise Pi's failure so the workflow reports it
158
+ if [ $PI_EXIT -ne 0 ]; then
159
+ echo "Pi exited with code ${PI_EXIT}"
160
+ exit $PI_EXIT
161
+ fi
162
+
163
+ echo "Done. Job ID: ${JOB_ID}"
@@ -0,0 +1,63 @@
1
+ services:
2
+ traefik:
3
+ image: traefik:v3
4
+ command:
5
+ - --providers.docker=true
6
+ - --providers.docker.exposedByDefault=false
7
+ - --entrypoints.web.address=:80
8
+ - --entrypoints.websecure.address=:443
9
+ ## Uncomment the following lines to enable TLS via Let's Encrypt
10
+ ## (requires LETSENCRYPT_EMAIL in .env):
11
+ # - --entrypoints.web.http.redirections.entrypoint.to=websecure
12
+ # - --entrypoints.web.http.redirections.entrypoint.scheme=https
13
+ # - --certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}
14
+ # - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
15
+ # - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
16
+ ports:
17
+ - "80:80"
18
+ - "443:443"
19
+ volumes:
20
+ - /var/run/docker.sock:/var/run/docker.sock:ro
21
+ - traefik_certs:/letsencrypt
22
+ restart: unless-stopped
23
+
24
+ event-handler:
25
+ container_name: thepopebot-event-handler
26
+ image: ${EVENT_HANDLER_IMAGE_URL:-stephengpope/thepopebot:event-handler-${THEPOPEBOT_VERSION:-latest}}
27
+ volumes:
28
+ - .:/app
29
+ - /app/node_modules
30
+ labels:
31
+ - traefik.enable=true
32
+ # Set APP_HOSTNAME in .env to the domain from APP_URL (e.g., mybot.example.com)
33
+ - traefik.http.routers.event-handler.rule=Host(`${APP_HOSTNAME}`)
34
+ - traefik.http.routers.event-handler.entrypoints=web
35
+ - traefik.http.services.event-handler.loadbalancer.server.port=80
36
+ ## Uncomment the following lines to enable TLS via Let's Encrypt:
37
+ # - traefik.http.routers.event-handler.entrypoints=websecure
38
+ # - traefik.http.routers.event-handler.tls.certresolver=letsencrypt
39
+ stop_grace_period: 120s
40
+ healthcheck:
41
+ test: ["CMD", "curl", "-f", "http://localhost:80/api/ping"]
42
+ interval: 10s
43
+ timeout: 3s
44
+ retries: 3
45
+ start_period: 30s
46
+ restart: unless-stopped
47
+
48
+ runner:
49
+ image: myoung34/github-runner:latest
50
+ deploy:
51
+ replicas: ${RUNNER_REPLICAS:-2}
52
+ environment:
53
+ REPO_URL: https://github.com/${GH_OWNER}/${GH_REPO}
54
+ ACCESS_TOKEN: ${GH_TOKEN}
55
+ RUNNER_SCOPE: repo
56
+ LABELS: self-hosted
57
+ volumes:
58
+ - /var/run/docker.sock:/var/run/docker.sock
59
+ - .:/project:ro
60
+ restart: unless-stopped
61
+
62
+ volumes:
63
+ traefik_certs:
@@ -0,0 +1,6 @@
1
+ export async function register() {
2
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
3
+ const { register } = await import('thepopebot/instrumentation');
4
+ await register();
5
+ }
6
+ }
@@ -0,0 +1 @@
1
+ export { middleware, config } from 'thepopebot/middleware';
@@ -0,0 +1,3 @@
1
+ import { withThepopebot } from 'thepopebot/config';
2
+
3
+ export default withThepopebot({});
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ };
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mario Zechner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,119 @@
1
+ # pi-skills
2
+
3
+ A collection of skills for [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent), compatible with Claude Code, Codex CLI, Amp, and Droid.
4
+
5
+ ## Installation
6
+
7
+ ### pi-coding-agent
8
+
9
+ ```bash
10
+ # User-level (available in all projects)
11
+ git clone https://github.com/badlogic/pi-skills ~/.pi/agent/skills/pi-skills
12
+
13
+ # Or project-level
14
+ git clone https://github.com/badlogic/pi-skills .pi/skills/pi-skills
15
+ ```
16
+
17
+ ### Codex CLI
18
+
19
+ ```bash
20
+ git clone https://github.com/badlogic/pi-skills ~/.codex/skills/pi-skills
21
+ ```
22
+
23
+ ### Amp
24
+
25
+ Amp finds skills recursively in toolboxes:
26
+
27
+ ```bash
28
+ git clone https://github.com/badlogic/pi-skills ~/.config/amp/tools/pi-skills
29
+ ```
30
+
31
+ ### Droid (Factory)
32
+
33
+ ```bash
34
+ # User-level
35
+ git clone https://github.com/badlogic/pi-skills ~/.factory/skills/pi-skills
36
+
37
+ # Or project-level
38
+ git clone https://github.com/badlogic/pi-skills .factory/skills/pi-skills
39
+ ```
40
+
41
+ ### Claude Code
42
+
43
+ Claude Code only looks one level deep for `SKILL.md` files, so each skill folder must be directly under the skills directory. Clone the repo somewhere, then symlink individual skills:
44
+
45
+ ```bash
46
+ # Clone to a convenient location
47
+ git clone https://github.com/badlogic/pi-skills ~/pi-skills
48
+
49
+ # Symlink individual skills (user-level)
50
+ mkdir -p ~/.claude/skills
51
+ ln -s ~/pi-skills/brave-search ~/.claude/skills/brave-search
52
+ ln -s ~/pi-skills/browser-tools ~/.claude/skills/browser-tools
53
+ ln -s ~/pi-skills/gccli ~/.claude/skills/gccli
54
+ ln -s ~/pi-skills/gdcli ~/.claude/skills/gdcli
55
+ ln -s ~/pi-skills/gmcli ~/.claude/skills/gmcli
56
+ ln -s ~/pi-skills/transcribe ~/.claude/skills/transcribe
57
+ ln -s ~/pi-skills/vscode ~/.claude/skills/vscode
58
+ ln -s ~/pi-skills/youtube-transcript ~/.claude/skills/youtube-transcript
59
+
60
+ # Or project-level
61
+ mkdir -p .claude/skills
62
+ ln -s ~/pi-skills/brave-search .claude/skills/brave-search
63
+ ln -s ~/pi-skills/browser-tools .claude/skills/browser-tools
64
+ ln -s ~/pi-skills/gccli .claude/skills/gccli
65
+ ln -s ~/pi-skills/gdcli .claude/skills/gdcli
66
+ ln -s ~/pi-skills/gmcli .claude/skills/gmcli
67
+ ln -s ~/pi-skills/transcribe .claude/skills/transcribe
68
+ ln -s ~/pi-skills/vscode .claude/skills/vscode
69
+ ln -s ~/pi-skills/youtube-transcript .claude/skills/youtube-transcript
70
+ ```
71
+
72
+ ## Available Skills
73
+
74
+ | Skill | Description |
75
+ |-------|-------------|
76
+ | [brave-search](brave-search/SKILL.md) | Web search and content extraction via Brave Search |
77
+ | [browser-tools](browser-tools/SKILL.md) | Interactive browser automation via Chrome DevTools Protocol |
78
+ | [gccli](gccli/SKILL.md) | Google Calendar CLI for events and availability |
79
+ | [gdcli](gdcli/SKILL.md) | Google Drive CLI for file management and sharing |
80
+ | [gmcli](gmcli/SKILL.md) | Gmail CLI for email, drafts, and labels |
81
+ | [transcribe](transcribe/SKILL.md) | Speech-to-text transcription via Groq Whisper API |
82
+ | [vscode](vscode/SKILL.md) | VS Code integration for diffs and file comparison |
83
+ | [youtube-transcript](youtube-transcript/SKILL.md) | Fetch YouTube video transcripts |
84
+
85
+ ## Skill Format
86
+
87
+ Each skill follows the pi/Claude Code format:
88
+
89
+ ```markdown
90
+ ---
91
+ name: skill-name
92
+ description: Short description shown to agent
93
+ ---
94
+
95
+ # Instructions
96
+
97
+ Detailed instructions here...
98
+ Helper files available at: skills/skill-name/
99
+ ```
100
+
101
+ Skills use project-root-relative paths (e.g., `skills/brave-search/search.js`).
102
+
103
+ ## Requirements
104
+
105
+ Some skills require additional setup. Generally, the agent will walk you through that. But if not, here you go:
106
+
107
+ - **brave-search**: Requires Node.js. Run `npm install` in the skill directory.
108
+ - **browser-tools**: Requires Chrome and Node.js. Run `npm install` in the skill directory.
109
+ - **gccli**: Requires Node.js. Install globally with `npm install -g @mariozechner/gccli`.
110
+ - **gdcli**: Requires Node.js. Install globally with `npm install -g @mariozechner/gdcli`.
111
+ - **gmcli**: Requires Node.js. Install globally with `npm install -g @mariozechner/gmcli`.
112
+ - **subagent**: Requires pi-coding-agent. Install globally with `npm install -g @mariozechner/pi-coding-agent`.
113
+ - **transcribe**: Requires curl and a Groq API key.
114
+ - **vscode**: Requires VS Code with `code` CLI in PATH.
115
+ - **youtube-transcript**: Requires Node.js. Run `npm install` in the skill directory.
116
+
117
+ ## License
118
+
119
+ MIT
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: brave-search
3
+ description: Web search and content extraction via Brave Search API. Use for searching documentation, facts, or any web content. Lightweight, no browser required.
4
+ ---
5
+
6
+ # Brave Search
7
+
8
+ Web search and content extraction using the official Brave Search API. No browser required.
9
+
10
+ ## Setup
11
+
12
+ Requires a Brave Search API account with a free subscription. A credit card is required to create the free subscription (you won't be charged).
13
+
14
+ 1. Create an account at https://api-dashboard.search.brave.com/register
15
+ 2. Create a "Free AI" subscription
16
+ 3. Create an API key for the subscription
17
+ 4. Add to your shell profile (`~/.profile` or `~/.zprofile` for zsh):
18
+ ```bash
19
+ export BRAVE_API_KEY="your-api-key-here"
20
+ ```
21
+ 5. Install dependencies (run once):
22
+ ```bash
23
+ cd skills/brave-search
24
+ npm install
25
+ ```
26
+
27
+ ## Search
28
+
29
+ ```bash
30
+ skills/brave-search/search.js "query" # Basic search (5 results)
31
+ skills/brave-search/search.js "query" -n 10 # More results (max 20)
32
+ skills/brave-search/search.js "query" --content # Include page content as markdown
33
+ skills/brave-search/search.js "query" --freshness pw # Results from last week
34
+ skills/brave-search/search.js "query" --freshness 2024-01-01to2024-06-30 # Date range
35
+ skills/brave-search/search.js "query" --country DE # Results from Germany
36
+ skills/brave-search/search.js "query" -n 3 --content # Combined options
37
+ ```
38
+
39
+ ### Options
40
+
41
+ - `-n <num>` - Number of results (default: 5, max: 20)
42
+ - `--content` - Fetch and include page content as markdown
43
+ - `--country <code>` - Two-letter country code (default: US)
44
+ - `--freshness <period>` - Filter by time:
45
+ - `pd` - Past day (24 hours)
46
+ - `pw` - Past week
47
+ - `pm` - Past month
48
+ - `py` - Past year
49
+ - `YYYY-MM-DDtoYYYY-MM-DD` - Custom date range
50
+
51
+ ## Extract Page Content
52
+
53
+ ```bash
54
+ skills/brave-search/content.js https://example.com/article
55
+ ```
56
+
57
+ Fetches a URL and extracts readable content as markdown.
58
+
59
+ ## Output Format
60
+
61
+ ```
62
+ --- Result 1 ---
63
+ Title: Page Title
64
+ Link: https://example.com/page
65
+ Age: 2 days ago
66
+ Snippet: Description from search results
67
+ Content: (if --content flag used)
68
+ Markdown content extracted from the page...
69
+
70
+ --- Result 2 ---
71
+ ...
72
+ ```
73
+
74
+ ## When to Use
75
+
76
+ - Searching for documentation or API references
77
+ - Looking up facts or current information
78
+ - Fetching content from specific URLs
79
+ - Any task requiring web search without interactive browsing
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Readability } from "@mozilla/readability";
4
+ import { JSDOM } from "jsdom";
5
+ import TurndownService from "turndown";
6
+ import { gfm } from "turndown-plugin-gfm";
7
+
8
+ const url = process.argv[2];
9
+
10
+ if (!url) {
11
+ console.log("Usage: content.js <url>");
12
+ console.log("\nExtracts readable content from a webpage as markdown.");
13
+ console.log("\nExamples:");
14
+ console.log(" content.js https://example.com/article");
15
+ console.log(" content.js https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html");
16
+ process.exit(1);
17
+ }
18
+
19
+ function htmlToMarkdown(html) {
20
+ const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
21
+ turndown.use(gfm);
22
+ turndown.addRule("removeEmptyLinks", {
23
+ filter: (node) => node.nodeName === "A" && !node.textContent?.trim(),
24
+ replacement: () => "",
25
+ });
26
+ return turndown
27
+ .turndown(html)
28
+ .replace(/\[\\?\[\s*\\?\]\]\([^)]*\)/g, "")
29
+ .replace(/ +/g, " ")
30
+ .replace(/\s+,/g, ",")
31
+ .replace(/\s+\./g, ".")
32
+ .replace(/\n{3,}/g, "\n\n")
33
+ .trim();
34
+ }
35
+
36
+ try {
37
+ const response = await fetch(url, {
38
+ headers: {
39
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
40
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
41
+ "Accept-Language": "en-US,en;q=0.9",
42
+ },
43
+ signal: AbortSignal.timeout(15000),
44
+ });
45
+
46
+ if (!response.ok) {
47
+ console.error(`HTTP ${response.status}: ${response.statusText}`);
48
+ process.exit(1);
49
+ }
50
+
51
+ const html = await response.text();
52
+ const dom = new JSDOM(html, { url });
53
+ const reader = new Readability(dom.window.document);
54
+ const article = reader.parse();
55
+
56
+ if (article && article.content) {
57
+ if (article.title) {
58
+ console.log(`# ${article.title}\n`);
59
+ }
60
+ console.log(htmlToMarkdown(article.content));
61
+ process.exit(0);
62
+ }
63
+
64
+ // Fallback: try to extract main content
65
+ const fallbackDoc = new JSDOM(html, { url });
66
+ const body = fallbackDoc.window.document;
67
+ body.querySelectorAll("script, style, noscript, nav, header, footer, aside").forEach(el => el.remove());
68
+
69
+ const title = body.querySelector("title")?.textContent?.trim();
70
+ const main = body.querySelector("main, article, [role='main'], .content, #content") || body.body;
71
+
72
+ if (title) {
73
+ console.log(`# ${title}\n`);
74
+ }
75
+
76
+ const text = main?.innerHTML || "";
77
+ if (text.trim().length > 100) {
78
+ console.log(htmlToMarkdown(text));
79
+ } else {
80
+ console.error("Could not extract readable content from this page.");
81
+ process.exit(1);
82
+ }
83
+ } catch (e) {
84
+ console.error(`Error: ${e.message}`);
85
+ process.exit(1);
86
+ }