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.
- package/dist/.claude/commands/ai.md +69 -0
- package/dist/.claude/commands/architect.md +121 -0
- package/dist/.claude/commands/assemble.md +201 -0
- package/dist/.claude/commands/assess.md +75 -0
- package/dist/.claude/commands/blueprint.md +135 -0
- package/dist/.claude/commands/build.md +116 -0
- package/dist/.claude/commands/campaign.md +201 -0
- package/dist/.claude/commands/cultivation.md +166 -0
- package/dist/.claude/commands/current.md +128 -0
- package/dist/.claude/commands/dangerroom.md +74 -0
- package/dist/.claude/commands/debrief.md +178 -0
- package/dist/.claude/commands/deploy.md +99 -0
- package/dist/.claude/commands/devops.md +143 -0
- package/dist/.claude/commands/gauntlet.md +140 -0
- package/dist/.claude/commands/git.md +104 -0
- package/dist/.claude/commands/grow.md +146 -0
- package/dist/.claude/commands/imagine.md +126 -0
- package/dist/.claude/commands/portfolio.md +50 -0
- package/dist/.claude/commands/prd.md +113 -0
- package/dist/.claude/commands/qa.md +107 -0
- package/dist/.claude/commands/review.md +151 -0
- package/dist/.claude/commands/security.md +100 -0
- package/dist/.claude/commands/test.md +96 -0
- package/dist/.claude/commands/thumper.md +116 -0
- package/dist/.claude/commands/treasury.md +100 -0
- package/dist/.claude/commands/ux.md +118 -0
- package/dist/.claude/commands/vault.md +189 -0
- package/dist/.claude/commands/void.md +108 -0
- package/dist/CHANGELOG.md +1918 -0
- package/dist/CLAUDE.md +250 -0
- package/dist/HOLOCRON.md +856 -0
- package/dist/VERSION.md +123 -0
- package/dist/docs/NAMING_REGISTRY.md +478 -0
- package/dist/docs/methods/AI_INTELLIGENCE.md +276 -0
- package/dist/docs/methods/ASSEMBLER.md +142 -0
- package/dist/docs/methods/BACKEND_ENGINEER.md +165 -0
- package/dist/docs/methods/BUILD_JOURNAL.md +185 -0
- package/dist/docs/methods/BUILD_PROTOCOL.md +426 -0
- package/dist/docs/methods/CAMPAIGN.md +568 -0
- package/dist/docs/methods/CONTEXT_MANAGEMENT.md +189 -0
- package/dist/docs/methods/DEEP_CURRENT.md +184 -0
- package/dist/docs/methods/DEVOPS_ENGINEER.md +295 -0
- package/dist/docs/methods/FIELD_MEDIC.md +261 -0
- package/dist/docs/methods/FORGE_ARTIST.md +108 -0
- package/dist/docs/methods/FORGE_KEEPER.md +268 -0
- package/dist/docs/methods/GAUNTLET.md +344 -0
- package/dist/docs/methods/GROWTH_STRATEGIST.md +466 -0
- package/dist/docs/methods/HEARTBEAT.md +168 -0
- package/dist/docs/methods/MCP_INTEGRATION.md +139 -0
- package/dist/docs/methods/MUSTER.md +148 -0
- package/dist/docs/methods/PRD_GENERATOR.md +186 -0
- package/dist/docs/methods/PRODUCT_DESIGN_FRONTEND.md +250 -0
- package/dist/docs/methods/QA_ENGINEER.md +337 -0
- package/dist/docs/methods/RELEASE_MANAGER.md +145 -0
- package/dist/docs/methods/SECURITY_AUDITOR.md +320 -0
- package/dist/docs/methods/SUB_AGENTS.md +335 -0
- package/dist/docs/methods/SYSTEMS_ARCHITECT.md +171 -0
- package/dist/docs/methods/TESTING.md +359 -0
- package/dist/docs/methods/THUMPER.md +175 -0
- package/dist/docs/methods/TIME_VAULT.md +120 -0
- package/dist/docs/methods/TREASURY.md +184 -0
- package/dist/docs/methods/TROUBLESHOOTING.md +265 -0
- package/dist/docs/patterns/README.md +52 -0
- package/dist/docs/patterns/ad-billing-adapter.ts +537 -0
- package/dist/docs/patterns/ad-platform-adapter.ts +421 -0
- package/dist/docs/patterns/ai-classifier.ts +195 -0
- package/dist/docs/patterns/ai-eval.ts +272 -0
- package/dist/docs/patterns/ai-orchestrator.ts +341 -0
- package/dist/docs/patterns/ai-router.ts +194 -0
- package/dist/docs/patterns/ai-tool-schema.ts +237 -0
- package/dist/docs/patterns/api-route.ts +241 -0
- package/dist/docs/patterns/backtest-engine.ts +499 -0
- package/dist/docs/patterns/browser-review.ts +292 -0
- package/dist/docs/patterns/combobox.tsx +300 -0
- package/dist/docs/patterns/component.tsx +262 -0
- package/dist/docs/patterns/daemon-process.ts +338 -0
- package/dist/docs/patterns/data-pipeline.ts +297 -0
- package/dist/docs/patterns/database-migration.ts +466 -0
- package/dist/docs/patterns/e2e-test.ts +629 -0
- package/dist/docs/patterns/error-handling.ts +312 -0
- package/dist/docs/patterns/execution-safety.ts +601 -0
- package/dist/docs/patterns/financial-transaction.ts +342 -0
- package/dist/docs/patterns/funding-plan.ts +462 -0
- package/dist/docs/patterns/game-entity.ts +137 -0
- package/dist/docs/patterns/game-loop.ts +113 -0
- package/dist/docs/patterns/game-state.ts +143 -0
- package/dist/docs/patterns/job-queue.ts +225 -0
- package/dist/docs/patterns/kongo-integration.ts +164 -0
- package/dist/docs/patterns/middleware.ts +363 -0
- package/dist/docs/patterns/mobile-screen.tsx +139 -0
- package/dist/docs/patterns/mobile-service.ts +167 -0
- package/dist/docs/patterns/multi-tenant.ts +382 -0
- package/dist/docs/patterns/oauth-token-lifecycle.ts +223 -0
- package/dist/docs/patterns/outbound-rate-limiter.ts +260 -0
- package/dist/docs/patterns/prompt-template.ts +195 -0
- package/dist/docs/patterns/revenue-source-adapter.ts +311 -0
- package/dist/docs/patterns/service.ts +224 -0
- package/dist/docs/patterns/sse-endpoint.ts +118 -0
- package/dist/docs/patterns/stablecoin-adapter.ts +511 -0
- package/dist/docs/patterns/third-party-script.ts +68 -0
- package/dist/scripts/thumper/gom-jabbar.sh +241 -0
- package/dist/scripts/thumper/relay.sh +610 -0
- package/dist/scripts/thumper/scan.sh +359 -0
- package/dist/scripts/thumper/thumper.sh +190 -0
- package/dist/scripts/thumper/water-rings.sh +76 -0
- package/package.json +1 -1
- 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
|