thevoidforge 21.0.11 → 21.0.12

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 (107) hide show
  1. package/dist/.claude/commands/ai.md +69 -0
  2. package/dist/.claude/commands/architect.md +121 -0
  3. package/dist/.claude/commands/assemble.md +201 -0
  4. package/dist/.claude/commands/assess.md +75 -0
  5. package/dist/.claude/commands/blueprint.md +135 -0
  6. package/dist/.claude/commands/build.md +116 -0
  7. package/dist/.claude/commands/campaign.md +201 -0
  8. package/dist/.claude/commands/cultivation.md +166 -0
  9. package/dist/.claude/commands/current.md +128 -0
  10. package/dist/.claude/commands/dangerroom.md +74 -0
  11. package/dist/.claude/commands/debrief.md +178 -0
  12. package/dist/.claude/commands/deploy.md +99 -0
  13. package/dist/.claude/commands/devops.md +143 -0
  14. package/dist/.claude/commands/gauntlet.md +140 -0
  15. package/dist/.claude/commands/git.md +104 -0
  16. package/dist/.claude/commands/grow.md +146 -0
  17. package/dist/.claude/commands/imagine.md +126 -0
  18. package/dist/.claude/commands/portfolio.md +50 -0
  19. package/dist/.claude/commands/prd.md +113 -0
  20. package/dist/.claude/commands/qa.md +107 -0
  21. package/dist/.claude/commands/review.md +151 -0
  22. package/dist/.claude/commands/security.md +100 -0
  23. package/dist/.claude/commands/test.md +96 -0
  24. package/dist/.claude/commands/thumper.md +116 -0
  25. package/dist/.claude/commands/treasury.md +100 -0
  26. package/dist/.claude/commands/ux.md +118 -0
  27. package/dist/.claude/commands/vault.md +189 -0
  28. package/dist/.claude/commands/void.md +108 -0
  29. package/dist/CHANGELOG.md +1918 -0
  30. package/dist/CLAUDE.md +250 -0
  31. package/dist/HOLOCRON.md +856 -0
  32. package/dist/VERSION.md +123 -0
  33. package/dist/docs/NAMING_REGISTRY.md +478 -0
  34. package/dist/docs/methods/AI_INTELLIGENCE.md +276 -0
  35. package/dist/docs/methods/ASSEMBLER.md +142 -0
  36. package/dist/docs/methods/BACKEND_ENGINEER.md +165 -0
  37. package/dist/docs/methods/BUILD_JOURNAL.md +185 -0
  38. package/dist/docs/methods/BUILD_PROTOCOL.md +426 -0
  39. package/dist/docs/methods/CAMPAIGN.md +568 -0
  40. package/dist/docs/methods/CONTEXT_MANAGEMENT.md +189 -0
  41. package/dist/docs/methods/DEEP_CURRENT.md +184 -0
  42. package/dist/docs/methods/DEVOPS_ENGINEER.md +295 -0
  43. package/dist/docs/methods/FIELD_MEDIC.md +261 -0
  44. package/dist/docs/methods/FORGE_ARTIST.md +108 -0
  45. package/dist/docs/methods/FORGE_KEEPER.md +268 -0
  46. package/dist/docs/methods/GAUNTLET.md +344 -0
  47. package/dist/docs/methods/GROWTH_STRATEGIST.md +466 -0
  48. package/dist/docs/methods/HEARTBEAT.md +168 -0
  49. package/dist/docs/methods/MCP_INTEGRATION.md +139 -0
  50. package/dist/docs/methods/MUSTER.md +148 -0
  51. package/dist/docs/methods/PRD_GENERATOR.md +186 -0
  52. package/dist/docs/methods/PRODUCT_DESIGN_FRONTEND.md +250 -0
  53. package/dist/docs/methods/QA_ENGINEER.md +337 -0
  54. package/dist/docs/methods/RELEASE_MANAGER.md +145 -0
  55. package/dist/docs/methods/SECURITY_AUDITOR.md +320 -0
  56. package/dist/docs/methods/SUB_AGENTS.md +335 -0
  57. package/dist/docs/methods/SYSTEMS_ARCHITECT.md +171 -0
  58. package/dist/docs/methods/TESTING.md +359 -0
  59. package/dist/docs/methods/THUMPER.md +175 -0
  60. package/dist/docs/methods/TIME_VAULT.md +120 -0
  61. package/dist/docs/methods/TREASURY.md +184 -0
  62. package/dist/docs/methods/TROUBLESHOOTING.md +265 -0
  63. package/dist/docs/patterns/README.md +52 -0
  64. package/dist/docs/patterns/ad-billing-adapter.ts +537 -0
  65. package/dist/docs/patterns/ad-platform-adapter.ts +421 -0
  66. package/dist/docs/patterns/ai-classifier.ts +195 -0
  67. package/dist/docs/patterns/ai-eval.ts +272 -0
  68. package/dist/docs/patterns/ai-orchestrator.ts +341 -0
  69. package/dist/docs/patterns/ai-router.ts +194 -0
  70. package/dist/docs/patterns/ai-tool-schema.ts +237 -0
  71. package/dist/docs/patterns/api-route.ts +241 -0
  72. package/dist/docs/patterns/backtest-engine.ts +499 -0
  73. package/dist/docs/patterns/browser-review.ts +292 -0
  74. package/dist/docs/patterns/combobox.tsx +300 -0
  75. package/dist/docs/patterns/component.tsx +262 -0
  76. package/dist/docs/patterns/daemon-process.ts +338 -0
  77. package/dist/docs/patterns/data-pipeline.ts +297 -0
  78. package/dist/docs/patterns/database-migration.ts +466 -0
  79. package/dist/docs/patterns/e2e-test.ts +629 -0
  80. package/dist/docs/patterns/error-handling.ts +312 -0
  81. package/dist/docs/patterns/execution-safety.ts +601 -0
  82. package/dist/docs/patterns/financial-transaction.ts +342 -0
  83. package/dist/docs/patterns/funding-plan.ts +462 -0
  84. package/dist/docs/patterns/game-entity.ts +137 -0
  85. package/dist/docs/patterns/game-loop.ts +113 -0
  86. package/dist/docs/patterns/game-state.ts +143 -0
  87. package/dist/docs/patterns/job-queue.ts +225 -0
  88. package/dist/docs/patterns/kongo-integration.ts +164 -0
  89. package/dist/docs/patterns/middleware.ts +363 -0
  90. package/dist/docs/patterns/mobile-screen.tsx +139 -0
  91. package/dist/docs/patterns/mobile-service.ts +167 -0
  92. package/dist/docs/patterns/multi-tenant.ts +382 -0
  93. package/dist/docs/patterns/oauth-token-lifecycle.ts +223 -0
  94. package/dist/docs/patterns/outbound-rate-limiter.ts +260 -0
  95. package/dist/docs/patterns/prompt-template.ts +195 -0
  96. package/dist/docs/patterns/revenue-source-adapter.ts +311 -0
  97. package/dist/docs/patterns/service.ts +224 -0
  98. package/dist/docs/patterns/sse-endpoint.ts +118 -0
  99. package/dist/docs/patterns/stablecoin-adapter.ts +511 -0
  100. package/dist/docs/patterns/third-party-script.ts +68 -0
  101. package/dist/scripts/thumper/gom-jabbar.sh +241 -0
  102. package/dist/scripts/thumper/relay.sh +610 -0
  103. package/dist/scripts/thumper/scan.sh +359 -0
  104. package/dist/scripts/thumper/thumper.sh +190 -0
  105. package/dist/scripts/thumper/water-rings.sh +76 -0
  106. package/package.json +1 -1
  107. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,359 @@
1
+ #!/bin/bash
2
+ # scan.sh — Reading the sand — Chani's environment scanner
3
+ # Detects runtime environment, collects Telegram credentials, writes sietch vault
4
+
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
9
+ CONFIG_DIR="$PROJECT_ROOT/.voidforge/thumper"
10
+ CONFIG_FILE="$CONFIG_DIR/sietch.env"
11
+
12
+ # ─── Non-Interactive Mode (--token and --chat-id args) ───────
13
+ # Allows /thumper setup to run from Claude Code's Bash tool,
14
+ # which doesn't support interactive stdin (read -r -p).
15
+ # Usage: scan.sh --token 123:ABC --chat-id 456
16
+ ARG_TOKEN=""
17
+ ARG_CHAT_ID=""
18
+ while [[ $# -gt 0 ]]; do
19
+ case "$1" in
20
+ --token) ARG_TOKEN="$2"; shift 2 ;;
21
+ --chat-id) ARG_CHAT_ID="$2"; shift 2 ;;
22
+ *) shift ;;
23
+ esac
24
+ done
25
+
26
+ if ! command -v curl >/dev/null 2>&1; then
27
+ echo "āŒ curl is required but not found. Install curl and re-run."
28
+ exit 1
29
+ fi
30
+
31
+ # ─── JSON Parsing Helper ──────────────────────────────────────
32
+
33
+ json_extract() {
34
+ local json="$1" dotpath="$2"
35
+ if command -v python3 >/dev/null 2>&1; then
36
+ echo "$json" | python3 -c "
37
+ import sys, json
38
+ try:
39
+ d = json.load(sys.stdin)
40
+ for key in sys.argv[1].split('.'):
41
+ d = d[key] if isinstance(d, dict) else d[int(key)]
42
+ print(d)
43
+ except Exception:
44
+ pass
45
+ " "$dotpath" 2>/dev/null
46
+ elif command -v jq >/dev/null 2>&1; then
47
+ echo "$json" | jq -r ".$dotpath" 2>/dev/null
48
+ else
49
+ echo ""
50
+ fi
51
+ }
52
+
53
+ # ─── Setup ─────────────────────────────────────────────────────
54
+
55
+ echo ""
56
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
57
+ echo "šŸœļø Reading the Sand — Chani's Scanner"
58
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
59
+ echo ""
60
+
61
+ # ─── Non-Interactive Fast Path ────────────────────────────────
62
+ # Skip all prompts when --token and --chat-id are both provided
63
+ if [[ -n "$ARG_TOKEN" ]] && [[ -n "$ARG_CHAT_ID" ]]; then
64
+ BOT_TOKEN="$ARG_TOKEN"
65
+ CHAT_ID="$ARG_CHAT_ID"
66
+ HAS_BOT="yes"
67
+
68
+ echo "šŸœļø Non-interactive mode — validating credentials..."
69
+ API_BASE="https://api.telegram.org/bot${BOT_TOKEN}"
70
+ VALIDATE=$(curl -s --connect-timeout 5 --max-time 10 "${API_BASE}/getMe" 2>/dev/null || echo "")
71
+ if echo "$VALIDATE" | grep -q '"ok":true'; then
72
+ BOT_NAME=$(json_extract "$VALIDATE" "result.username" 2>/dev/null || echo "unknown")
73
+ echo "āœ… Voice validated: @${BOT_NAME}"
74
+ echo "āœ… Chat ID: $CHAT_ID"
75
+ else
76
+ echo "āŒ Invalid bot token or Telegram unreachable."
77
+ exit 1
78
+ fi
79
+ else
80
+ # ─── Interactive Mode ──────────────────────────────────────
81
+
82
+ if [[ -f "$CONFIG_FILE" ]]; then
83
+ echo "āš ļø Existing sietch vault found at $CONFIG_FILE"
84
+ read -r -p "Overwrite? (yes/no): " OVERWRITE
85
+ if [[ "$OVERWRITE" != "yes" ]]; then
86
+ echo "Scan aborted. Sietch vault preserved."
87
+ exit 0
88
+ fi
89
+ fi
90
+
91
+ # ─── Step 1: Telegram Bot ─────────────────────────────────────
92
+
93
+ read -r -p "Do you have an existing Telegram bot? (yes/no): " HAS_BOT
94
+
95
+ if [[ "$HAS_BOT" != "yes" ]]; then
96
+ echo ""
97
+ echo "ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”"
98
+ echo "│ Summon Your Voice (Create a Bot) │"
99
+ echo "ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜"
100
+ echo ""
101
+ echo " 1. Open Telegram"
102
+ echo " 2. Search for @BotFather and start a chat"
103
+ echo " 3. Send the message: /newbot"
104
+ echo " 4. Choose a name for your bot (display name)"
105
+ echo " 5. Choose a username ending in 'bot' (e.g. mydev_bot)"
106
+ echo " 6. BotFather will give you a token like: 123456:ABC-DEF..."
107
+ echo " 7. Copy that token, then send ANY message to your new bot"
108
+ echo " (type \"hello\" — required before the bridge can find you)"
109
+ echo ""
110
+ fi
111
+
112
+ read -r -p "Paste your bot token here: " BOT_TOKEN
113
+
114
+ if [[ -z "$BOT_TOKEN" ]]; then
115
+ echo "āŒ No token provided. Scan aborted."
116
+ exit 1
117
+ fi
118
+
119
+ echo ""
120
+ echo "šŸ” Validating your Voice..."
121
+ API_BASE="https://api.telegram.org/bot${BOT_TOKEN}"
122
+ VALIDATE=$(curl -s --connect-timeout 5 --max-time 10 "${API_BASE}/getMe" 2>/dev/null || echo "")
123
+
124
+ if echo "$VALIDATE" | grep -q '"ok":true'; then
125
+ BOT_NAME=$(json_extract "$VALIDATE" "result.username" 2>/dev/null || echo "unknown")
126
+ echo "āœ… Voice validated: @${BOT_NAME}"
127
+ else
128
+ echo "āŒ Invalid bot token or Telegram unreachable."
129
+ exit 1
130
+ fi
131
+
132
+ CHAT_ID=""
133
+ if [[ "$HAS_BOT" == "yes" ]]; then
134
+ read -r -p "Do you know your chat ID? (yes/no): " KNOWS_CHAT_ID
135
+ if [[ "$KNOWS_CHAT_ID" == "yes" ]]; then
136
+ read -r -p "Enter your chat ID: " CHAT_ID
137
+ fi
138
+ fi
139
+
140
+ if [[ -z "$CHAT_ID" ]]; then
141
+ echo ""
142
+ echo "šŸ” Scanning for your presence in the desert..."
143
+
144
+ auto_detect_chat_id() {
145
+ local updates
146
+ updates=$(curl -s --connect-timeout 5 --max-time 15 "${API_BASE}/getUpdates?limit=10" 2>/dev/null || echo "")
147
+ if command -v python3 >/dev/null 2>&1; then
148
+ echo "$updates" | python3 -c "
149
+ import sys, json
150
+ try:
151
+ data = json.load(sys.stdin)
152
+ if data.get('ok') and data.get('result'):
153
+ for update in data['result']:
154
+ msg = update.get('message', {})
155
+ chat = msg.get('chat', {})
156
+ if chat.get('type') == 'private':
157
+ print(chat['id'])
158
+ break
159
+ except Exception: pass
160
+ " 2>/dev/null
161
+ elif command -v jq >/dev/null 2>&1; then
162
+ echo "$updates" | jq -r '[.result[]? | select(.message.chat.type == "private")] | first | .message.chat.id // empty' 2>/dev/null
163
+ else
164
+ echo "$updates" | grep -o '"chat":{"id":[0-9]*' | head -1 | sed 's/.*://'
165
+ fi
166
+ }
167
+
168
+ CHAT_ID=$(auto_detect_chat_id)
169
+
170
+ if [[ -z "$CHAT_ID" ]]; then
171
+ echo " No presence detected. Send any message to your bot now."
172
+ read -r -p " Press Enter when done..." _
173
+ sleep 2
174
+ CHAT_ID=$(auto_detect_chat_id)
175
+ fi
176
+
177
+ if [[ -z "$CHAT_ID" ]]; then
178
+ echo " āš ļø Auto-detection failed."
179
+ read -r -p " Enter your chat ID manually: " CHAT_ID
180
+ fi
181
+
182
+ if [[ -z "$CHAT_ID" ]]; then
183
+ echo "āŒ No chat ID provided. Scan aborted."
184
+ exit 1
185
+ fi
186
+ fi
187
+
188
+ echo "āœ… Presence confirmed. Chat ID: $CHAT_ID"
189
+
190
+ fi # end interactive mode (else branch of non-interactive check)
191
+
192
+ # ─── Step 2: Environment Scan ─────────────────────────────────
193
+
194
+ echo ""
195
+ echo "šŸœļø Reading the sand..."
196
+
197
+ INJECT_METHOD=""
198
+ TMUX_SESSION_NAME=""
199
+ ENV_LABEL=""
200
+
201
+ detect_environment() {
202
+ # Priority 1: tmux (most reliable, cross-platform)
203
+ if command -v tmux >/dev/null 2>&1 && [[ -n "${TMUX:-}" ]]; then
204
+ INJECT_METHOD="TMUX_SENDKEYS"
205
+ TMUX_SESSION_NAME=$(tmux display-message -p '#S' 2>/dev/null || echo "0")
206
+ ENV_LABEL="TMUX session ($TMUX_SESSION_NAME)"
207
+ return
208
+ fi
209
+
210
+ # Priority 2: Headless Linux SSH
211
+ if [[ -z "${DISPLAY:-}" ]] && [[ -z "${WAYLAND_DISPLAY:-}" ]] && \
212
+ [[ "$(tty 2>/dev/null || echo "")" == /dev/pts/* ]] && \
213
+ [[ "${OSTYPE:-}" == linux* ]] && [[ -d "/proc" ]]; then
214
+ INJECT_METHOD="PTY_INJECT"
215
+ ENV_LABEL="Headless desert (SSH)"
216
+ return
217
+ fi
218
+
219
+ # Priority 3: macOS with supported terminal (Terminal.app or iTerm2 only)
220
+ if [[ "${OSTYPE:-}" == darwin* ]] && [[ -z "${SSH_CLIENT:-}" ]] && [[ -z "${SSH_TTY:-}" ]]; then
221
+ local tp="${TERM_PROGRAM:-}"
222
+ if [[ "$tp" == "Apple_Terminal" ]] || [[ "$tp" == "iTerm.app" ]] || [[ "$tp" == "iTerm2" ]]; then
223
+ INJECT_METHOD="OSASCRIPT"
224
+ ENV_LABEL="Local oasis ($tp)"
225
+ return
226
+ fi
227
+ # macOS with unsupported terminal (VS Code, Warp, Alacritty, Kitty, etc.)
228
+ if [[ -n "$tp" ]]; then
229
+ INJECT_METHOD=""
230
+ ENV_LABEL="macOS — $tp (OSASCRIPT not supported for this terminal)"
231
+ echo ""
232
+ echo "āš ļø Your terminal ($tp) is not supported by OSASCRIPT injection."
233
+ echo " OSASCRIPT only works with Terminal.app and iTerm2."
234
+ echo ""
235
+ echo " Recommended: install tmux and run Claude Code inside a tmux session."
236
+ echo " Then re-run /thumper setup — tmux will be auto-detected."
237
+ echo ""
238
+ # Don't return — fall through to manual selection
239
+ fi
240
+ fi
241
+
242
+ # Priority 4: Linux with /proc
243
+ if [[ "${OSTYPE:-}" == linux* ]] && [[ -d "/proc" ]]; then
244
+ INJECT_METHOD="PTY_INJECT"
245
+ ENV_LABEL="Linux desert"
246
+ return
247
+ fi
248
+
249
+ # Priority 5: Windows (Git Bash, MSYS2, Cygwin) — not supported
250
+ if [[ "${OSTYPE:-}" == msys* ]] || [[ "${OSTYPE:-}" == cygwin* ]]; then
251
+ echo ""
252
+ echo "āš ļø Windows native terminals (Git Bash, MSYS2) are not supported."
253
+ echo " The Voice requires a Unix PTY for message injection."
254
+ echo ""
255
+ echo " Recommended: use WSL (Windows Subsystem for Linux) with tmux."
256
+ echo " Claude Code in WSL + tmux will be auto-detected."
257
+ echo ""
258
+ fi
259
+
260
+ INJECT_METHOD=""
261
+ ENV_LABEL="Unknown terrain"
262
+ }
263
+
264
+ detect_environment
265
+
266
+ if [[ -z "$INJECT_METHOD" ]]; then
267
+ echo "āš ļø Cannot determine a viable worm path."
268
+ echo ""
269
+ echo " Supported terrain:"
270
+ echo " 1. Any platform with tmux (TMUX_SENDKEYS)"
271
+ echo " 2. Headless Linux over SSH (PTY_INJECT)"
272
+ echo " 3. macOS local terminal (OSASCRIPT)"
273
+ echo " 4. Linux with /proc filesystem (PTY_INJECT)"
274
+ echo ""
275
+ read -r -p " Select manually (1-4) or 'q' to abort: " MANUAL_CHOICE
276
+ case "$MANUAL_CHOICE" in
277
+ 1) INJECT_METHOD="TMUX_SENDKEYS"; read -r -p " tmux session name: " TMUX_SESSION_NAME ;;
278
+ 2|4) INJECT_METHOD="PTY_INJECT" ;;
279
+ 3) INJECT_METHOD="OSASCRIPT" ;;
280
+ *) echo "Scan aborted."; exit 1 ;;
281
+ esac
282
+ fi
283
+
284
+ echo "šŸ” Terrain: $ENV_LABEL"
285
+ echo "⚔ Worm path: $INJECT_METHOD"
286
+
287
+ # Skip confirmation in non-interactive mode
288
+ if [[ -n "$ARG_TOKEN" ]] && [[ -n "$ARG_CHAT_ID" ]]; then
289
+ CONFIRM_ENV="yes"
290
+ else
291
+ read -r -p "Does this look right? (yes/no): " CONFIRM_ENV
292
+ fi
293
+
294
+ if [[ "$CONFIRM_ENV" != "yes" ]]; then
295
+ echo ""
296
+ echo "Select worm path:"
297
+ echo " 1. TMUX_SENDKEYS — tmux send-keys (requires tmux)"
298
+ echo " 2. PTY_INJECT — Direct PTY write (Linux with /proc)"
299
+ echo " 3. OSASCRIPT — macOS keystroke injection"
300
+ read -r -p "Choice (1-3): " MANUAL_CHOICE
301
+ case "$MANUAL_CHOICE" in
302
+ 1) INJECT_METHOD="TMUX_SENDKEYS"; read -r -p "tmux session name: " TMUX_SESSION_NAME ;;
303
+ 2) INJECT_METHOD="PTY_INJECT" ;;
304
+ 3) INJECT_METHOD="OSASCRIPT" ;;
305
+ *) echo "Invalid choice."; exit 1 ;;
306
+ esac
307
+ fi
308
+
309
+ # ─── Step 3: Write Sietch Vault ────────────────────────────────
310
+
311
+ mkdir -p "$CONFIG_DIR"
312
+ chmod 700 "$CONFIG_DIR"
313
+
314
+ if [[ ! "$BOT_TOKEN" =~ ^[0-9]+:[A-Za-z0-9_-]+$ ]]; then
315
+ echo "āš ļø Warning: Bot token format is unusual. Verify it's correct."
316
+ fi
317
+
318
+ (
319
+ umask 077
320
+ {
321
+ echo "# Sietch vault — generated by Chani's scanner"
322
+ echo "# WARNING: Contains credentials. Never commit this file."
323
+ printf 'BOT_TOKEN=%q\n' "$BOT_TOKEN"
324
+ printf 'CHAT_ID=%q\n' "$CHAT_ID"
325
+ printf 'INJECT_METHOD=%q\n' "$INJECT_METHOD"
326
+ printf 'TMUX_SESSION=%q\n' "${TMUX_SESSION_NAME:-}"
327
+ printf 'TERM_PROGRAM=%q\n' "${TERM_PROGRAM:-}"
328
+ printf 'SETUP_COMPLETE=%q\n' "true"
329
+ printf 'SETUP_DATE=%q\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
330
+ printf 'CONFIG_VERSION=%q\n' "1"
331
+ } > "$CONFIG_FILE"
332
+ )
333
+ chmod 600 "$CONFIG_FILE"
334
+
335
+ # ─── Step 4: Summary ──────────────────────────────────────────
336
+
337
+ echo ""
338
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
339
+ echo "šŸœļø Sietch Vault Sealed"
340
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
341
+ echo " Bot token: ****...${BOT_TOKEN: -4}"
342
+ echo " Chat ID: $CHAT_ID"
343
+ echo " Worm path: $INJECT_METHOD"
344
+ echo " Vault: $CONFIG_FILE"
345
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
346
+ echo ""
347
+
348
+ # Skip prompt in non-interactive mode — don't auto-start, let user /thumper on
349
+ if [[ -n "$ARG_TOKEN" ]] && [[ -n "$ARG_CHAT_ID" ]]; then
350
+ START_NOW="no"
351
+ else
352
+ read -r -p "Speak The Voice now? (yes/no): " START_NOW
353
+ fi
354
+
355
+ if [[ "$START_NOW" == "yes" ]]; then
356
+ bash "$SCRIPT_DIR/thumper.sh" on
357
+ else
358
+ echo "Run /thumper on when you're ready. The desert waits."
359
+ fi
@@ -0,0 +1,190 @@
1
+ #!/bin/bash
2
+ # thumper.sh — Main entrypoint for Chani's worm rider
3
+ # The Bene Gesserit Voice — command through speech across the desert
4
+ # Routes commands to scan.sh (setup), relay.sh (sandworm daemon), and status
5
+
6
+ set -euo pipefail
7
+
8
+ # Security: refuse to run as root
9
+ if [[ "$(id -u)" -eq 0 ]]; then
10
+ echo "āŒ Cannot run as root. Use a normal user account." >&2
11
+ exit 1
12
+ fi
13
+
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
16
+ CONFIG_DIR="$PROJECT_ROOT/.voidforge/thumper"
17
+ CONFIG_FILE="$CONFIG_DIR/sietch.env"
18
+ CHANNEL_FLAG="$CONFIG_DIR/.thumper.active"
19
+ PID_FILE="$CONFIG_DIR/.worm.pid"
20
+ GOM_JABBAR_FILE="$CONFIG_DIR/.gom-jabbar"
21
+
22
+ usage() {
23
+ cat <<'EOF'
24
+ šŸœļø /thumper — Chani's Worm Rider
25
+
26
+ Usage:
27
+ /thumper setup — First-time scan or re-configure
28
+ /thumper on — The Voice carries (open channel)
29
+ /thumper off — Silence in the desert (close channel)
30
+ /thumper status — Report channel state and worm path
31
+ EOF
32
+ }
33
+
34
+ ensure_config() {
35
+ if [[ ! -f "$CONFIG_FILE" ]]; then
36
+ echo "šŸœļø No config found. Running first-time setup..."
37
+ bash "$SCRIPT_DIR/scan.sh"
38
+ fi
39
+ }
40
+
41
+ is_worm_running() {
42
+ if [[ -f "$PID_FILE" ]]; then
43
+ local pid
44
+ pid=$(cat "$PID_FILE" 2>/dev/null || echo "")
45
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
46
+ return 0
47
+ fi
48
+ fi
49
+ return 1
50
+ }
51
+
52
+ cmd_setup() {
53
+ bash "$SCRIPT_DIR/scan.sh"
54
+ }
55
+
56
+ cmd_on() {
57
+ ensure_config
58
+
59
+ if [[ -f "$CHANNEL_FLAG" ]] && is_worm_running; then
60
+ echo "🪱 The Voice already carries. Sandworm active."
61
+ return 0
62
+ fi
63
+
64
+ # Clean up stale state
65
+ rm -f "$CHANNEL_FLAG" "$PID_FILE" "$GOM_JABBAR_FILE"
66
+
67
+ mkdir -p "$CONFIG_DIR"
68
+ touch "$CHANNEL_FLAG"
69
+
70
+ # Source config for display before starting daemon
71
+ source "$CONFIG_FILE"
72
+
73
+ # Start sandworm daemon (relay.sh writes its own PID file)
74
+ nohup bash "$SCRIPT_DIR/relay.sh" >> "$CONFIG_DIR/worm.log" 2>&1 &
75
+
76
+ # Brief pause for relay.sh to write its PID file
77
+ sleep 1
78
+
79
+ echo "🪱 The Voice carries. Sandworm active."
80
+ echo "⚔ Transport: $INJECT_METHOD"
81
+ echo "šŸ”® Open your Telegram bot chat — you'll be asked to set a passphrase."
82
+ }
83
+
84
+ cmd_off() {
85
+ rm -f "$CHANNEL_FLAG"
86
+
87
+ if [[ -f "$PID_FILE" ]]; then
88
+ local pid
89
+ pid=$(cat "$PID_FILE" 2>/dev/null || echo "")
90
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
91
+ kill "$pid" 2>/dev/null || true
92
+ local i
93
+ for i in 1 2 3; do
94
+ kill -0 "$pid" 2>/dev/null || break
95
+ sleep 1
96
+ done
97
+ kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null || true
98
+ fi
99
+ rm -f "$PID_FILE"
100
+ fi
101
+
102
+ # Clean up Gom Jabbar state (passphrase hash is session-scoped)
103
+ rm -f "$GOM_JABBAR_FILE"
104
+
105
+ echo "šŸ”‡ Silence in the desert. Sandworm dormant."
106
+ }
107
+
108
+ cmd_status() {
109
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
110
+ echo "šŸœļø Thumper Status Report"
111
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
112
+
113
+ if [[ ! -f "$CONFIG_FILE" ]]; then
114
+ echo "Sietch: āŒ No sietch vault found"
115
+ echo " Run /thumper setup to configure"
116
+ return 0
117
+ fi
118
+
119
+ source "$CONFIG_FILE"
120
+ echo "Sietch: āœ… $CONFIG_FILE"
121
+ echo "Worm path: $INJECT_METHOD"
122
+ [[ -n "${TMUX_SESSION:-}" ]] && echo "Session: $TMUX_SESSION"
123
+ echo "Setup: ${SETUP_DATE:-unknown}"
124
+
125
+ if [[ -f "$CHANNEL_FLAG" ]]; then
126
+ echo "Channel: 🟢 The Voice carries"
127
+ else
128
+ echo "Channel: šŸ”‡ Silence in the desert"
129
+ fi
130
+
131
+ if [[ -f "$PID_FILE" ]]; then
132
+ local pid
133
+ pid=$(cat "$PID_FILE" 2>/dev/null || echo "")
134
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
135
+ echo "Sandworm: 🪱 Running (PID $pid)"
136
+ else
137
+ echo "Sandworm: āš ļø Stale PID ($pid) — worm not running"
138
+ fi
139
+ else
140
+ echo "Sandworm: ⬚ No PID file"
141
+ fi
142
+
143
+ # Gom Jabbar state
144
+ if [[ -f "$GOM_JABBAR_FILE" ]]; then
145
+ source "$GOM_JABBAR_FILE"
146
+ case "${GJ_STATE:-}" in
147
+ AUTHENTICATED)
148
+ local now elapsed
149
+ now=$(date +%s)
150
+ elapsed=$(( now - ${GJ_LAST_ACTIVITY:-0} ))
151
+ local remaining=$(( 3600 - elapsed ))
152
+ if [[ $remaining -gt 0 ]]; then
153
+ echo "Gom Jabbar: āœ… Authenticated (${remaining}s until re-test)"
154
+ else
155
+ echo "Gom Jabbar: ā³ Idle — re-test required"
156
+ fi
157
+ ;;
158
+ PENDING) echo "Gom Jabbar: šŸ”® Awaiting word of passage" ;;
159
+ CHALLENGE) echo "Gom Jabbar: šŸ”® Re-test required" ;;
160
+ LOCKED) echo "Gom Jabbar: šŸ’€ Locked (failed attempts)" ;;
161
+ *) echo "Gom Jabbar: ā“ Unknown state" ;;
162
+ esac
163
+ else
164
+ echo "Gom Jabbar: ⬚ Not initialized"
165
+ fi
166
+
167
+ if [[ -f "$CONFIG_DIR/.last_thumper_id" ]]; then
168
+ echo "Last beat: $(cat "$CONFIG_DIR/.last_thumper_id" 2>/dev/null || echo "unknown")"
169
+ fi
170
+
171
+ if [[ -f "$CONFIG_DIR/worm.log" ]]; then
172
+ local log_size
173
+ log_size=$(stat -f%z "$CONFIG_DIR/worm.log" 2>/dev/null || stat -c%s "$CONFIG_DIR/worm.log" 2>/dev/null || echo "?")
174
+ if [[ "$log_size" =~ ^[0-9]+$ ]] && [[ "$log_size" -gt 1048576 ]]; then
175
+ echo "Log: ${log_size} bytes (āš ļø >1MB — rotates on next worm start)"
176
+ elif [[ "$log_size" =~ ^[0-9]+$ ]]; then
177
+ echo "Log: ${log_size} bytes"
178
+ fi
179
+ fi
180
+
181
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
182
+ }
183
+
184
+ case "${1:-}" in
185
+ setup) cmd_setup ;;
186
+ on) cmd_on ;;
187
+ off) cmd_off ;;
188
+ status) cmd_status ;;
189
+ *) usage ;;
190
+ esac
@@ -0,0 +1,76 @@
1
+ #!/bin/bash
2
+ # water-rings.sh — The Water Rings — Chani's stop hook
3
+ # Sends Claude Code's response to Telegram when a turn completes.
4
+ # "Among the Fremen, water rings record the dead — and the debts of the living."
5
+ #
6
+ # The Stop hook receives JSON on stdin with `last_assistant_message` — the
7
+ # actual response text. No need to read transcript files.
8
+ #
9
+ # Note: -e (errexit) intentionally omitted — this hook must never fail.
10
+ set -uo pipefail
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
14
+ CONFIG_DIR="$PROJECT_ROOT/.voidforge/thumper"
15
+ CONFIG_FILE="$CONFIG_DIR/sietch.env"
16
+ CHANNEL_FLAG="$CONFIG_DIR/.thumper.active"
17
+
18
+ # Silent exit if not configured or channel closed
19
+ [ -f "$CONFIG_FILE" ] || exit 0
20
+ [ -f "$CHANNEL_FLAG" ] || exit 0
21
+
22
+ source "$CONFIG_FILE"
23
+ [ "${SETUP_COMPLETE:-}" = "true" ] || exit 0
24
+
25
+ # Read stop hook metadata from stdin
26
+ HOOK_INPUT=""
27
+ if ! [ -t 0 ]; then
28
+ HOOK_INPUT=$(perl -e 'alarm 5; local $/; print <STDIN>' 2>/dev/null || head -c 65536 2>/dev/null || echo "")
29
+ fi
30
+
31
+ # Extract last_assistant_message directly from hook metadata
32
+ MESSAGE=""
33
+ if [ -n "$HOOK_INPUT" ]; then
34
+ if command -v python3 >/dev/null 2>&1; then
35
+ MESSAGE=$(echo "$HOOK_INPUT" | python3 -c "
36
+ import sys, json
37
+ try:
38
+ data = json.load(sys.stdin)
39
+ text = data.get('last_assistant_message', '')
40
+ if text:
41
+ if len(text) > 3600:
42
+ text = text[:3600] + '\n\n[...truncated]'
43
+ print(text)
44
+ except Exception:
45
+ pass
46
+ " 2>/dev/null)
47
+ elif command -v jq >/dev/null 2>&1; then
48
+ MESSAGE=$(echo "$HOOK_INPUT" | jq -r '.last_assistant_message // empty' 2>/dev/null | head -c 3600)
49
+ fi
50
+ fi
51
+
52
+ if [ -n "$MESSAGE" ]; then
53
+ NOTIFICATION="$(printf 'āœ… Task complete\n\n%s\n\n─────────────────\nšŸ“” Reply to continue' "$MESSAGE")"
54
+ else
55
+ NOTIFICATION="$(printf 'āœ… Claude Code finished — no summary available.\n\n─────────────────\nšŸ“” Reply to continue')"
56
+ fi
57
+
58
+ _send_notification() {
59
+ local api_base="https://api.telegram.org/bot${BOT_TOKEN}"
60
+ # Try with Markdown first, fall back to plain text
61
+ curl -s --connect-timeout 5 --max-time 10 \
62
+ -X POST \
63
+ -d chat_id="$CHAT_ID" \
64
+ --data-urlencode text="$NOTIFICATION" \
65
+ -d parse_mode="Markdown" \
66
+ "${api_base}/sendMessage" >/dev/null 2>&1 || \
67
+ curl -s --connect-timeout 5 --max-time 10 \
68
+ -X POST \
69
+ -d chat_id="$CHAT_ID" \
70
+ --data-urlencode text="$NOTIFICATION" \
71
+ "${api_base}/sendMessage" >/dev/null 2>&1 || true
72
+ }
73
+
74
+ _send_notification &
75
+
76
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thevoidforge",
3
- "version": "21.0.11",
3
+ "version": "21.0.12",
4
4
  "description": "From nothing, everything. A methodology framework for building with Claude Code.",
5
5
  "type": "module",
6
6
  "engines": {