instar 0.28.46 → 0.28.48
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/README.md +3 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +75 -144
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.js +1 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/Config.d.ts.map +1 -1
- package/dist/core/Config.js +3 -0
- package/dist/core/Config.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +18 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +134 -46
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +14 -2
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +6 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/memory/TopicMemory.d.ts +18 -3
- package/dist/memory/TopicMemory.d.ts.map +1 -1
- package/dist/memory/TopicMemory.js +71 -11
- package/dist/memory/TopicMemory.js.map +1 -1
- package/dist/messaging/imessage/IMessageAdapter.d.ts +35 -0
- package/dist/messaging/imessage/IMessageAdapter.d.ts.map +1 -1
- package/dist/messaging/imessage/IMessageAdapter.js +121 -1
- package/dist/messaging/imessage/IMessageAdapter.js.map +1 -1
- package/dist/messaging/imessage/types.d.ts +33 -0
- package/dist/messaging/imessage/types.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +8 -0
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +16 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/middleware.d.ts +11 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +25 -1
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +9 -3
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/attachments-sync/go.mod +8 -0
- package/scripts/attachments-sync/go.sum +4 -0
- package/scripts/attachments-sync/main.go +253 -0
- package/scripts/setup-imessage-hardlink.sh +73 -0
- package/src/data/builtin-manifest.json +93 -93
- package/src/templates/scripts/slack-reply.sh +10 -0
- package/src/templates/scripts/telegram-reply.sh +12 -0
- package/src/templates/scripts/whatsapp-reply.sh +10 -0
- package/upgrades/0.28.47.md +51 -0
- package/upgrades/0.28.48.md +52 -0
- package/upgrades/side-effects/0.28.47.md +88 -0
- package/upgrades/side-effects/0.28.48.md +90 -0
- package/upgrades/side-effects/outbound-timeout-408-ambiguous.md +146 -0
- /package/upgrades/side-effects/{echo-prevention-self-session-exclusion.md → 0.28.46.md} +0 -0
package/README.md
CHANGED
|
@@ -213,6 +213,9 @@ iMessage support lets your agent send and receive iMessages on macOS. Messages a
|
|
|
213
213
|
```
|
|
214
214
|
4. **Automation permission** for Messages.app — macOS will prompt on first send
|
|
215
215
|
|
|
216
|
+
> **Photo attachments:** If you want your agent to process images and files sent via iMessage, the `instar-attachments-sync` binary must also be running with Full Disk Access granted to it. It mirrors attachments from the Messages sandbox to a readable location. See [docs/LAUNCHDAEMON-SETUP.md#3-imessage-photo-attachments-optional](docs/LAUNCHDAEMON-SETUP.md#3-imessage-photo-attachments-optional) for setup.
|
|
217
|
+
|
|
218
|
+
For running as a LaunchDaemon (always-on, survives reboots), see [docs/LAUNCHDAEMON-SETUP.md](docs/LAUNCHDAEMON-SETUP.md).
|
|
216
219
|
### Configuration
|
|
217
220
|
|
|
218
221
|
Add to your `.instar/config.json`:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AA+CH,UAAU,WAAW;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yFAAyF;IACzF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBrE;AAkzCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA2iC1E;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AA+CH,UAAU,WAAW;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yFAAyF;IACzF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBrE;AAkzCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA2iC1E;AAulBD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAuBlF"}
|
package/dist/commands/init.js
CHANGED
|
@@ -2505,7 +2505,33 @@ function getDefaultJobs(port) {
|
|
|
2505
2505
|
enabled: true,
|
|
2506
2506
|
execute: {
|
|
2507
2507
|
type: 'prompt',
|
|
2508
|
-
value:
|
|
2508
|
+
value: `AUTH=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('authToken',''))" 2>/dev/null)
|
|
2509
|
+
|
|
2510
|
+
# Read recent activity logs to understand what happened in last 4 hours
|
|
2511
|
+
RECENT_LOGS=$(ls -t .instar/logs/activity-*.jsonl 2>/dev/null | head -1)
|
|
2512
|
+
if [ -z "$RECENT_LOGS" ]; then
|
|
2513
|
+
RECENT_LOGS=".instar/logs/activity-$(date +%Y-%m-%d).jsonl"
|
|
2514
|
+
fi
|
|
2515
|
+
|
|
2516
|
+
# Extract recent session activity and key events (filter out noise, keep significant events)
|
|
2517
|
+
echo "=== RECENT ACTIVITY (Last 4 Hours) ==="
|
|
2518
|
+
tail -500 "$RECENT_LOGS" 2>/dev/null | jq -r 'select(.type != "job-start" and .type != "job-queued") | "\(.timestamp) [\(.type)] \(.message // .title // .session_name // .slug // "")"' 2>/dev/null | tail -100
|
|
2519
|
+
|
|
2520
|
+
echo ""
|
|
2521
|
+
echo "=== YOUR TASK ==="
|
|
2522
|
+
echo "Analyze the activity above. Identify any learnings, patterns, or insights worth preserving in MEMORY.md:"
|
|
2523
|
+
echo "- Session patterns or repeated issues"
|
|
2524
|
+
echo "- Completed commitments or action items"
|
|
2525
|
+
echo "- Gaps between intended behavior and actual behavior"
|
|
2526
|
+
echo "- Unexpected interactions or failure modes"
|
|
2527
|
+
echo "- Process improvements or capability gaps"
|
|
2528
|
+
echo ""
|
|
2529
|
+
echo "If you find genuine learnings:"
|
|
2530
|
+
echo "1. Update .instar/MEMORY.md with the insight (append to the file)"
|
|
2531
|
+
echo "2. Be specific: include what was learned, why it matters, and how it should guide future work"
|
|
2532
|
+
echo "3. Signal completion: curl -s -X POST http://localhost:${port}/reflection/record -H 'Content-Type: application/json' -d '{\"type\":\"quick\"}'"
|
|
2533
|
+
echo ""
|
|
2534
|
+
echo "If nothing significant, do nothing. Silence means continuity is working as expected."`,
|
|
2509
2535
|
},
|
|
2510
2536
|
tags: ['cat:learning'],
|
|
2511
2537
|
},
|
|
@@ -2549,7 +2575,7 @@ function getDefaultJobs(port) {
|
|
|
2549
2575
|
expectedDurationMinutes: 3,
|
|
2550
2576
|
model: 'opus',
|
|
2551
2577
|
enabled: true,
|
|
2552
|
-
gate: `curl -sf http://localhost:${port}/evolution/learnings?applied=false 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('learnings',[])) > 0 else 1)"`,
|
|
2578
|
+
gate: `curl -sf -H "Authorization: Bearer $INSTAR_AUTH_TOKEN" http://localhost:${port}/evolution/learnings?applied=false 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('learnings',[])) > 0 else 1)"`,
|
|
2553
2579
|
execute: {
|
|
2554
2580
|
type: 'prompt',
|
|
2555
2581
|
value: `Harvest and synthesize learnings: curl -s http://localhost:${port}/evolution/learnings?applied=false
|
|
@@ -2580,7 +2606,7 @@ If no actionable patterns found, exit silently.`,
|
|
|
2580
2606
|
expectedDurationMinutes: 2,
|
|
2581
2607
|
model: 'haiku',
|
|
2582
2608
|
enabled: true,
|
|
2583
|
-
gate: `curl -sf http://localhost:${port}/evolution/actions/overdue 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('overdue',[])) > 0 else 1)"`,
|
|
2609
|
+
gate: `curl -sf -H "Authorization: Bearer $INSTAR_AUTH_TOKEN" http://localhost:${port}/evolution/actions/overdue 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('overdue',[])) > 0 else 1)"`,
|
|
2584
2610
|
execute: {
|
|
2585
2611
|
type: 'prompt',
|
|
2586
2612
|
value: `Check for overdue commitments: curl -s http://localhost:${port}/evolution/actions/overdue
|
|
@@ -2738,7 +2764,7 @@ If no overdue or stale items, exit silently.`,
|
|
|
2738
2764
|
expectedDurationMinutes: 5,
|
|
2739
2765
|
model: 'haiku',
|
|
2740
2766
|
enabled: true,
|
|
2741
|
-
gate: 'bash
|
|
2767
|
+
gate: 'bash ${CLAUDE_PROJECT_DIR}/.claude/scripts/git-sync-gate.sh',
|
|
2742
2768
|
execute: {
|
|
2743
2769
|
type: 'skill',
|
|
2744
2770
|
value: 'git-sync',
|
|
@@ -2821,7 +2847,7 @@ If everything is coherent and no reflection is needed, exit silently. Only repor
|
|
|
2821
2847
|
expectedDurationMinutes: 3,
|
|
2822
2848
|
model: 'sonnet',
|
|
2823
2849
|
enabled: true,
|
|
2824
|
-
gate: `curl -sf http://localhost:${port}/evolution/proposals?status=proposed 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('proposals',[])) > 0 else 1)"`,
|
|
2850
|
+
gate: `curl -sf -H "Authorization: Bearer $INSTAR_AUTH_TOKEN" http://localhost:${port}/evolution/proposals?status=proposed 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('proposals',[])) > 0 else 1)"`,
|
|
2825
2851
|
execute: {
|
|
2826
2852
|
type: 'prompt',
|
|
2827
2853
|
value: `Review pending evolution proposals: curl -s http://localhost:${port}/evolution/proposals?status=proposed\n\nFor each proposal:\n1. Read the title, description, type, and source\n2. Evaluate: Is this a genuine improvement? Is the effort worth the impact? Does it align with our goals?\n3. If approved, update status: curl -s -X PATCH http://localhost:${port}/evolution/proposals/EVO-XXX -H 'Content-Type: application/json' -d '{"status":"approved"}'\n4. If rejected or deferred, update with reason.\n\nDo NOT implement approved proposals — that's handled by the paired evolution-proposal-implement job.\n\nAlso check the dashboard: curl -s http://localhost:${port}/evolution — report any highlights to the user if they seem important.\n\nIf no proposals need attention, exit silently.`,
|
|
@@ -2837,7 +2863,7 @@ If everything is coherent and no reflection is needed, exit silently. Only repor
|
|
|
2837
2863
|
expectedDurationMinutes: 10,
|
|
2838
2864
|
model: 'opus',
|
|
2839
2865
|
enabled: true,
|
|
2840
|
-
gate: `curl -sf http://localhost:${port}/evolution/proposals?status=approved 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('proposals',[])) > 0 else 1)"`,
|
|
2866
|
+
gate: `curl -sf -H "Authorization: Bearer $INSTAR_AUTH_TOKEN" http://localhost:${port}/evolution/proposals?status=approved 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('proposals',[])) > 0 else 1)"`,
|
|
2841
2867
|
execute: {
|
|
2842
2868
|
type: 'prompt',
|
|
2843
2869
|
value: `Implement approved evolution proposals: curl -s http://localhost:${port}/evolution/proposals?status=approved\n\nFor each approved proposal:\n1. Read the full description and understand what needs to be built\n2. Implement it: create the skill/hook/job/config change described\n3. After implementation, mark complete: curl -s -X PATCH http://localhost:${port}/evolution/proposals/EVO-XXX -H 'Content-Type: application/json' -d '{"status":"implemented","resolution":"What was done"}'\n\nIf no approved proposals exist, exit silently.`,
|
|
@@ -3129,6 +3155,33 @@ function refreshScripts(projectDir, stateDir) {
|
|
|
3129
3155
|
// Always install serendipity-capture.sh
|
|
3130
3156
|
installSerendipityCapture(projectDir);
|
|
3131
3157
|
}
|
|
3158
|
+
/**
|
|
3159
|
+
* Load a messaging-relay script template from src/templates/scripts/,
|
|
3160
|
+
* substituting the agent's configured port into the INSTAR_PORT fallback.
|
|
3161
|
+
*
|
|
3162
|
+
* The canonical templates live at src/templates/scripts/*-reply.sh. Keeping
|
|
3163
|
+
* a single source of truth (vs. duplicated inlined bash) eliminates a class
|
|
3164
|
+
* of bugs where scaffold-time and migration-time versions drift out of sync.
|
|
3165
|
+
* PostUpdateMigrator.getTelegramReplyScript() uses the same loading pattern.
|
|
3166
|
+
*/
|
|
3167
|
+
function loadRelayTemplate(filename, port) {
|
|
3168
|
+
const modDir = path.dirname(new URL(import.meta.url).pathname);
|
|
3169
|
+
const candidates = [
|
|
3170
|
+
// dev: src/commands → ../templates/scripts
|
|
3171
|
+
path.resolve(modDir, '..', 'templates', 'scripts', filename),
|
|
3172
|
+
// dist: dist/commands → ../../src/templates/scripts
|
|
3173
|
+
path.resolve(modDir, '..', '..', 'src', 'templates', 'scripts', filename),
|
|
3174
|
+
];
|
|
3175
|
+
for (const candidate of candidates) {
|
|
3176
|
+
if (fs.existsSync(candidate)) {
|
|
3177
|
+
const content = fs.readFileSync(candidate, 'utf-8');
|
|
3178
|
+
// Template defaults to port 4040; bake the agent's actual port in so
|
|
3179
|
+
// the script works even without INSTAR_PORT set in the environment.
|
|
3180
|
+
return content.replace('${INSTAR_PORT:-4040}', `\${INSTAR_PORT:-${port}}`);
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
throw new Error(`Relay template not found: ${filename}`);
|
|
3184
|
+
}
|
|
3132
3185
|
/**
|
|
3133
3186
|
* Install the Telegram relay script that Claude uses to send responses
|
|
3134
3187
|
* back to Telegram topics via the instar server API.
|
|
@@ -3136,62 +3189,8 @@ function refreshScripts(projectDir, stateDir) {
|
|
|
3136
3189
|
function installTelegramRelay(projectDir, port) {
|
|
3137
3190
|
const scriptsDir = path.join(projectDir, '.claude', 'scripts');
|
|
3138
3191
|
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
3139
|
-
const scriptContent = `#!/bin/bash
|
|
3140
|
-
# telegram-reply.sh — Send a message back to a Telegram topic via instar server.
|
|
3141
|
-
#
|
|
3142
|
-
# Usage:
|
|
3143
|
-
# .claude/scripts/telegram-reply.sh TOPIC_ID "message text"
|
|
3144
|
-
# echo "message text" | .claude/scripts/telegram-reply.sh TOPIC_ID
|
|
3145
|
-
# cat <<'EOF' | .claude/scripts/telegram-reply.sh TOPIC_ID
|
|
3146
|
-
# Multi-line message here
|
|
3147
|
-
# EOF
|
|
3148
|
-
|
|
3149
|
-
TOPIC_ID="$1"
|
|
3150
|
-
shift
|
|
3151
|
-
|
|
3152
|
-
if [ -z "$TOPIC_ID" ]; then
|
|
3153
|
-
echo "Usage: telegram-reply.sh TOPIC_ID [message]" >&2
|
|
3154
|
-
exit 1
|
|
3155
|
-
fi
|
|
3156
|
-
|
|
3157
|
-
# Read message from args or stdin
|
|
3158
|
-
if [ $# -gt 0 ]; then
|
|
3159
|
-
MSG="$*"
|
|
3160
|
-
else
|
|
3161
|
-
MSG="$(cat)"
|
|
3162
|
-
fi
|
|
3163
|
-
|
|
3164
|
-
if [ -z "$MSG" ]; then
|
|
3165
|
-
echo "No message provided" >&2
|
|
3166
|
-
exit 1
|
|
3167
|
-
fi
|
|
3168
|
-
|
|
3169
|
-
PORT="\${INSTAR_PORT:-${port}}"
|
|
3170
|
-
|
|
3171
|
-
# Escape for JSON
|
|
3172
|
-
JSON_MSG=$(printf '%s' "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' 2>/dev/null)
|
|
3173
|
-
if [ -z "$JSON_MSG" ]; then
|
|
3174
|
-
# Fallback if python3 not available: basic escape
|
|
3175
|
-
JSON_MSG="$(printf '%s' "$MSG" | sed 's/\\\\\\\\/\\\\\\\\\\\\\\\\/g; s/"/\\\\\\\\"/g' | sed ':a;N;$!ba;s/\\\\n/\\\\\\\\n/g')"
|
|
3176
|
-
JSON_MSG="\\"$JSON_MSG\\""
|
|
3177
|
-
fi
|
|
3178
|
-
|
|
3179
|
-
RESPONSE=$(curl -s -w "\\n%{http_code}" -X POST "http://localhost:\${PORT}/telegram/reply/\${TOPIC_ID}" \\
|
|
3180
|
-
-H 'Content-Type: application/json' \\
|
|
3181
|
-
-d "{\\"text\\":\${JSON_MSG}}")
|
|
3182
|
-
|
|
3183
|
-
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
|
|
3184
|
-
BODY=$(echo "$RESPONSE" | sed '$d')
|
|
3185
|
-
|
|
3186
|
-
if [ "$HTTP_CODE" = "200" ]; then
|
|
3187
|
-
echo "Sent $(echo "$MSG" | wc -c | tr -d ' ') chars to topic $TOPIC_ID"
|
|
3188
|
-
else
|
|
3189
|
-
echo "Failed (HTTP $HTTP_CODE): $BODY" >&2
|
|
3190
|
-
exit 1
|
|
3191
|
-
fi
|
|
3192
|
-
`;
|
|
3193
3192
|
const scriptPath = path.join(scriptsDir, 'telegram-reply.sh');
|
|
3194
|
-
fs.writeFileSync(scriptPath,
|
|
3193
|
+
fs.writeFileSync(scriptPath, loadRelayTemplate('telegram-reply.sh', port), { mode: 0o755 });
|
|
3195
3194
|
}
|
|
3196
3195
|
/**
|
|
3197
3196
|
* Install the WhatsApp relay script that Claude uses to send responses
|
|
@@ -3200,76 +3199,8 @@ fi
|
|
|
3200
3199
|
function installWhatsAppRelay(projectDir, port) {
|
|
3201
3200
|
const scriptsDir = path.join(projectDir, '.instar', 'scripts');
|
|
3202
3201
|
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
3203
|
-
const scriptContent = `#!/bin/bash
|
|
3204
|
-
# whatsapp-reply.sh — Send a message back to a WhatsApp JID via instar server.
|
|
3205
|
-
#
|
|
3206
|
-
# Usage:
|
|
3207
|
-
# .instar/scripts/whatsapp-reply.sh JID "message text"
|
|
3208
|
-
# echo "message text" | .instar/scripts/whatsapp-reply.sh JID
|
|
3209
|
-
# cat <<'EOF' | .instar/scripts/whatsapp-reply.sh JID
|
|
3210
|
-
# Multi-line message here
|
|
3211
|
-
# EOF
|
|
3212
|
-
#
|
|
3213
|
-
# JID format: phone@s.whatsapp.net (e.g., 12345678901@s.whatsapp.net)
|
|
3214
|
-
|
|
3215
|
-
JID="$1"
|
|
3216
|
-
shift
|
|
3217
|
-
|
|
3218
|
-
if [ -z "$JID" ]; then
|
|
3219
|
-
echo "Usage: whatsapp-reply.sh JID [message]" >&2
|
|
3220
|
-
exit 1
|
|
3221
|
-
fi
|
|
3222
|
-
|
|
3223
|
-
# Read message from args or stdin
|
|
3224
|
-
if [ $# -gt 0 ]; then
|
|
3225
|
-
MSG="$*"
|
|
3226
|
-
else
|
|
3227
|
-
MSG="$(cat)"
|
|
3228
|
-
fi
|
|
3229
|
-
|
|
3230
|
-
if [ -z "$MSG" ]; then
|
|
3231
|
-
echo "No message provided" >&2
|
|
3232
|
-
exit 1
|
|
3233
|
-
fi
|
|
3234
|
-
|
|
3235
|
-
PORT="\${INSTAR_PORT:-${port}}"
|
|
3236
|
-
|
|
3237
|
-
# Read auth token from config (if present)
|
|
3238
|
-
AUTH_TOKEN=""
|
|
3239
|
-
if [ -f ".instar/config.json" ]; then
|
|
3240
|
-
AUTH_TOKEN=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('authToken',''))" 2>/dev/null)
|
|
3241
|
-
fi
|
|
3242
|
-
|
|
3243
|
-
# Escape for JSON
|
|
3244
|
-
JSON_MSG=$(printf '%s' "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' 2>/dev/null)
|
|
3245
|
-
if [ -z "$JSON_MSG" ]; then
|
|
3246
|
-
JSON_MSG="$(printf '%s' "$MSG" | sed 's/\\\\\\\\/\\\\\\\\\\\\\\\\/g; s/"/\\\\\\\\"/g' | sed ':a;N;$!ba;s/\\\\n/\\\\\\\\n/g')"
|
|
3247
|
-
JSON_MSG="\\"$JSON_MSG\\""
|
|
3248
|
-
fi
|
|
3249
|
-
|
|
3250
|
-
if [ -n "$AUTH_TOKEN" ]; then
|
|
3251
|
-
RESPONSE=$(curl -s -w "\\n%{http_code}" -X POST "http://localhost:\${PORT}/whatsapp/send/\${JID}" \\
|
|
3252
|
-
-H 'Content-Type: application/json' \\
|
|
3253
|
-
-H "Authorization: Bearer \${AUTH_TOKEN}" \\
|
|
3254
|
-
-d "{\\"text\\":\${JSON_MSG}}")
|
|
3255
|
-
else
|
|
3256
|
-
RESPONSE=$(curl -s -w "\\n%{http_code}" -X POST "http://localhost:\${PORT}/whatsapp/send/\${JID}" \\
|
|
3257
|
-
-H 'Content-Type: application/json' \\
|
|
3258
|
-
-d "{\\"text\\":\${JSON_MSG}}")
|
|
3259
|
-
fi
|
|
3260
|
-
|
|
3261
|
-
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
|
|
3262
|
-
BODY=$(echo "$RESPONSE" | sed '$d')
|
|
3263
|
-
|
|
3264
|
-
if [ "$HTTP_CODE" = "200" ]; then
|
|
3265
|
-
echo "Sent $(echo "$MSG" | wc -c | tr -d ' ') chars to $JID"
|
|
3266
|
-
else
|
|
3267
|
-
echo "Failed (HTTP $HTTP_CODE): $BODY" >&2
|
|
3268
|
-
exit 1
|
|
3269
|
-
fi
|
|
3270
|
-
`;
|
|
3271
3202
|
const scriptPath = path.join(scriptsDir, 'whatsapp-reply.sh');
|
|
3272
|
-
fs.writeFileSync(scriptPath,
|
|
3203
|
+
fs.writeFileSync(scriptPath, loadRelayTemplate('whatsapp-reply.sh', port), { mode: 0o755 });
|
|
3273
3204
|
}
|
|
3274
3205
|
/**
|
|
3275
3206
|
* Append missing sections to CLAUDE.md without overwriting user customizations.
|
|
@@ -3983,27 +3914,27 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
3983
3914
|
const instarBashHooks = [
|
|
3984
3915
|
{
|
|
3985
3916
|
type: 'command',
|
|
3986
|
-
command: 'bash
|
|
3917
|
+
command: 'bash ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/dangerous-command-guard.sh "$TOOL_INPUT"',
|
|
3987
3918
|
blocking: true,
|
|
3988
3919
|
},
|
|
3989
3920
|
{
|
|
3990
3921
|
type: 'command',
|
|
3991
|
-
command: 'bash
|
|
3922
|
+
command: 'bash ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/grounding-before-messaging.sh "$TOOL_INPUT"',
|
|
3992
3923
|
blocking: false,
|
|
3993
3924
|
},
|
|
3994
3925
|
{
|
|
3995
3926
|
type: 'command',
|
|
3996
|
-
command: 'node
|
|
3927
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/deferral-detector.js',
|
|
3997
3928
|
timeout: 5000,
|
|
3998
3929
|
},
|
|
3999
3930
|
{
|
|
4000
3931
|
type: 'command',
|
|
4001
|
-
command: 'node
|
|
3932
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/external-communication-guard.js',
|
|
4002
3933
|
timeout: 5000,
|
|
4003
3934
|
},
|
|
4004
3935
|
{
|
|
4005
3936
|
type: 'command',
|
|
4006
|
-
command: 'node
|
|
3937
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/post-action-reflection.js',
|
|
4007
3938
|
timeout: 5000,
|
|
4008
3939
|
},
|
|
4009
3940
|
];
|
|
@@ -4011,7 +3942,7 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
4011
3942
|
const instarMcpHooks = [
|
|
4012
3943
|
{
|
|
4013
3944
|
type: 'command',
|
|
4014
|
-
command: 'node
|
|
3945
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/external-operation-gate.js',
|
|
4015
3946
|
blocking: true,
|
|
4016
3947
|
timeout: 5000,
|
|
4017
3948
|
},
|
|
@@ -4060,7 +3991,7 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
4060
3991
|
// The session-start.sh hook handles event routing internally via CLAUDE_HOOK_MATCHER
|
|
4061
3992
|
const sessionStartHook = {
|
|
4062
3993
|
type: 'command',
|
|
4063
|
-
command: 'bash
|
|
3994
|
+
command: 'bash ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/session-start.sh',
|
|
4064
3995
|
timeout: 5,
|
|
4065
3996
|
};
|
|
4066
3997
|
if (!hooks.SessionStart) {
|
|
@@ -4115,7 +4046,7 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
4115
4046
|
// PostToolUse: scope coherence collector tracks implementation depth
|
|
4116
4047
|
const scopeCollectorHook = {
|
|
4117
4048
|
type: 'command',
|
|
4118
|
-
command: 'node
|
|
4049
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/scope-coherence-collector.js',
|
|
4119
4050
|
timeout: 5000,
|
|
4120
4051
|
};
|
|
4121
4052
|
if (!hooks.PostToolUse) {
|
|
@@ -4126,7 +4057,7 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
4126
4057
|
// (scope collector also added to Read and Skill)
|
|
4127
4058
|
const claimInterceptHook = {
|
|
4128
4059
|
type: 'command',
|
|
4129
|
-
command: 'node
|
|
4060
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/claim-intercept.js',
|
|
4130
4061
|
timeout: 5000,
|
|
4131
4062
|
};
|
|
4132
4063
|
for (const matcher of ['Edit', 'Write', 'Bash', 'Read', 'Skill']) {
|
|
@@ -4151,19 +4082,19 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
4151
4082
|
// Stop: response review pipeline — Coherence Gate LLM-powered review
|
|
4152
4083
|
const responseReviewHook = {
|
|
4153
4084
|
type: 'command',
|
|
4154
|
-
command: 'node
|
|
4085
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/response-review.js',
|
|
4155
4086
|
timeout: 10000,
|
|
4156
4087
|
};
|
|
4157
4088
|
// Stop: scope coherence checkpoint fires the zoom-out prompt
|
|
4158
4089
|
const scopeCheckpointHook = {
|
|
4159
4090
|
type: 'command',
|
|
4160
|
-
command: 'node
|
|
4091
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/scope-coherence-checkpoint.js',
|
|
4161
4092
|
timeout: 10000,
|
|
4162
4093
|
};
|
|
4163
4094
|
// Stop: claim intercept response checks direct text for false claims
|
|
4164
4095
|
const claimInterceptResponseHook = {
|
|
4165
4096
|
type: 'command',
|
|
4166
|
-
command: 'node
|
|
4097
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/claim-intercept-response.js',
|
|
4167
4098
|
timeout: 10000,
|
|
4168
4099
|
};
|
|
4169
4100
|
if (!hooks.Stop) {
|
|
@@ -4191,7 +4122,7 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
4191
4122
|
if (!hasAutonomousHook) {
|
|
4192
4123
|
hooks.Stop.unshift({ matcher: '', hooks: [{
|
|
4193
4124
|
type: 'command',
|
|
4194
|
-
command: 'bash
|
|
4125
|
+
command: 'bash ${CLAUDE_PROJECT_DIR}/.claude/skills/autonomous/hooks/autonomous-stop-hook.sh',
|
|
4195
4126
|
timeout: 10000,
|
|
4196
4127
|
}] });
|
|
4197
4128
|
}
|
|
@@ -4207,7 +4138,7 @@ function installClaudeSettings(projectDir, serverPort) {
|
|
|
4207
4138
|
matcher: '',
|
|
4208
4139
|
hooks: [{
|
|
4209
4140
|
type: 'command',
|
|
4210
|
-
command: 'node
|
|
4141
|
+
command: 'node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/auto-approve-permissions.js',
|
|
4211
4142
|
timeout: 5000,
|
|
4212
4143
|
}],
|
|
4213
4144
|
});
|