instar 0.25.7 → 0.25.9

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.
@@ -0,0 +1,234 @@
1
+ #!/bin/bash
2
+
3
+ # Autonomous Mode Stop Hook
4
+ # Prevents session exit when autonomous mode is active.
5
+ # Feeds the goal and task list back to continue working.
6
+ #
7
+ # SESSION-SCOPED: Only blocks the session that activated autonomous mode.
8
+ # Other sessions on the same machine pass through unaffected.
9
+ #
10
+ # RESPECTS:
11
+ # - Session isolation (only blocks the autonomous session)
12
+ # - Emergency stop signals (user says "stop everything")
13
+ # - Duration expiry
14
+ # - Genuine completion (all tasks done, promise output)
15
+
16
+ set -euo pipefail
17
+
18
+ # Read hook input from stdin
19
+ HOOK_INPUT=$(cat)
20
+
21
+ # Check if autonomous mode is active
22
+ STATE_FILE=".instar/autonomous-state.local.md"
23
+
24
+ if [[ ! -f "$STATE_FILE" ]]; then
25
+ # No active autonomous session — allow exit
26
+ exit 0
27
+ fi
28
+
29
+ # Parse YAML frontmatter
30
+ FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$STATE_FILE")
31
+
32
+ ACTIVE=$(echo "$FRONTMATTER" | grep '^active:' | sed 's/active: *//')
33
+ if [[ "$ACTIVE" != "true" ]]; then
34
+ exit 0
35
+ fi
36
+
37
+ # SESSION ISOLATION: Only block the session that started autonomous mode.
38
+ # Uses self-bootstrapping: if no session_id in state file yet, the FIRST
39
+ # session to trigger the hook claims it. All other sessions see a mismatch.
40
+ STATE_SESSION=$(echo "$FRONTMATTER" | grep '^session_id:' | sed 's/^session_id: *//' | tr -d '"' || true)
41
+
42
+ # VALIDATE: If state has a session_id but it's not a valid UUID, treat as empty.
43
+ # Claude sometimes writes a custom string instead of the real $CLAUDE_CODE_SESSION_ID.
44
+ # Non-UUID values will never match the real hook session_id, causing the hook to
45
+ # fail-open and allow premature exit. By clearing invalid values, we fall through
46
+ # to self-bootstrap, which captures the REAL session_id from the first hook call.
47
+ UUID_REGEX='^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
48
+ if [[ -n "$STATE_SESSION" ]] && ! [[ "$STATE_SESSION" =~ $UUID_REGEX ]]; then
49
+ echo "[autonomous] Invalid session_id in state file (not UUID): '$STATE_SESSION' — falling back to self-bootstrap" >&2
50
+ STATE_SESSION=""
51
+ fi
52
+
53
+ HOOK_SESSION=$(echo "$HOOK_INPUT" | jq -r '.session_id // ""' 2>/dev/null || echo "")
54
+
55
+ # If hook has no session_id → fail OPEN (unknown context, don't trap)
56
+ if [[ -z "$HOOK_SESSION" ]]; then
57
+ echo "⚠️ Autonomous mode: No session_id in hook input — fail-open (allowing exit)" >&2
58
+ exit 0
59
+ fi
60
+
61
+ # SELF-BOOTSTRAP: If state has no session_id yet, claim it from this hook call.
62
+ # The first session to fire the hook becomes the autonomous session.
63
+ if [[ -z "$STATE_SESSION" ]]; then
64
+ # Atomic claim: write session_id to state file
65
+ TEMP_FILE="${STATE_FILE}.claim.$$"
66
+ sed "s/^session_id:.*/session_id: \"${HOOK_SESSION}\"/" "$STATE_FILE" > "$TEMP_FILE"
67
+ mv "$TEMP_FILE" "$STATE_FILE"
68
+ STATE_SESSION="$HOOK_SESSION"
69
+ echo "[autonomous] Session $HOOK_SESSION claimed autonomous mode" >&2
70
+ fi
71
+
72
+ # Different session → allow exit (fail-open for non-autonomous sessions)
73
+ if [[ "$STATE_SESSION" != "$HOOK_SESSION" ]]; then
74
+ exit 0
75
+ fi
76
+
77
+ # Same session — this IS the autonomous session, proceed with block logic
78
+
79
+ ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//')
80
+ DURATION_SECONDS=$(echo "$FRONTMATTER" | grep '^duration_seconds:' | sed 's/duration_seconds: *//')
81
+ STARTED_AT=$(echo "$FRONTMATTER" | grep '^started_at:' | sed 's/started_at: *//' | tr -d '"')
82
+ COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | tr -d '"')
83
+ REPORT_TOPIC=$(echo "$FRONTMATTER" | grep '^report_topic:' | sed 's/report_topic: *//' | tr -d '"')
84
+
85
+ # Validate iteration
86
+ if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
87
+ echo "⚠️ Autonomous mode: State file corrupted (bad iteration)" >&2
88
+ rm "$STATE_FILE"
89
+ exit 0
90
+ fi
91
+
92
+ # Check duration expiry
93
+ if [[ "$DURATION_SECONDS" =~ ^[0-9]+$ ]] && [[ $DURATION_SECONDS -gt 0 ]]; then
94
+ START_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$STARTED_AT" +%s 2>/dev/null || date -d "$STARTED_AT" +%s 2>/dev/null || echo "0")
95
+ NOW_EPOCH=$(date +%s)
96
+ ELAPSED=$(( NOW_EPOCH - START_EPOCH ))
97
+ if [[ $ELAPSED -ge $DURATION_SECONDS ]]; then
98
+ echo "⏰ Autonomous mode: Duration expired ($ELAPSED seconds elapsed)."
99
+ echo " Session is free to exit."
100
+ rm "$STATE_FILE"
101
+ exit 0
102
+ fi
103
+ REMAINING=$(( DURATION_SECONDS - ELAPSED ))
104
+ REMAINING_MIN=$(( REMAINING / 60 ))
105
+ fi
106
+
107
+ # Check for emergency stop (look in recent messages)
108
+ # The MessageSentinel handles this at the messaging layer, but also check here
109
+ if [[ -f ".instar/autonomous-emergency-stop" ]]; then
110
+ echo "🛑 Autonomous mode: Emergency stop detected."
111
+ rm "$STATE_FILE"
112
+ rm -f ".instar/autonomous-emergency-stop"
113
+ exit 0
114
+ fi
115
+
116
+ # Get transcript and check for completion promise
117
+ TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path' 2>/dev/null || echo "")
118
+
119
+ if [[ -n "$TRANSCRIPT_PATH" ]] && [[ -f "$TRANSCRIPT_PATH" ]]; then
120
+ # Check last assistant message for completion promise
121
+ LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1 || echo "")
122
+
123
+ if [[ -n "$LAST_LINE" ]]; then
124
+ LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r '
125
+ .message.content |
126
+ map(select(.type == "text")) |
127
+ map(.text) |
128
+ join("\n")
129
+ ' 2>/dev/null || echo "")
130
+
131
+ # Check for completion promise in <promise> tags
132
+ if [[ -n "$COMPLETION_PROMISE" ]] && [[ "$COMPLETION_PROMISE" != "null" ]]; then
133
+ PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g; s/\s+/ /g' 2>/dev/null || echo "")
134
+
135
+ if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
136
+ echo "✅ Autonomous mode: Completion promise detected — <promise>$COMPLETION_PROMISE</promise>"
137
+ echo " Session is free to exit. Good work!"
138
+ rm "$STATE_FILE"
139
+ exit 0
140
+ fi
141
+ fi
142
+ fi
143
+ fi
144
+
145
+ # Not complete — block exit and feed task list back
146
+ NEXT_ITERATION=$((ITERATION + 1))
147
+
148
+ # Extract prompt (everything after closing ---)
149
+ PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$STATE_FILE")
150
+
151
+ if [[ -z "$PROMPT_TEXT" ]]; then
152
+ echo "⚠️ Autonomous mode: State file has no task content" >&2
153
+ rm "$STATE_FILE"
154
+ exit 0
155
+ fi
156
+
157
+ # Update iteration counter
158
+ TEMP_FILE="${STATE_FILE}.tmp.$$"
159
+ sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$STATE_FILE" > "$TEMP_FILE"
160
+ mv "$TEMP_FILE" "$STATE_FILE"
161
+
162
+ # ── Progress Report Check ──
163
+ # Check if it's time to send a progress report
164
+ REPORT_INTERVAL=$(echo "$FRONTMATTER" | grep '^report_interval:' | sed 's/report_interval: *//' | tr -d '"')
165
+ LAST_REPORT_AT=$(echo "$FRONTMATTER" | grep '^last_report_at:' | sed 's/last_report_at: *//' | tr -d '"')
166
+
167
+ # Convert report interval to seconds
168
+ REPORT_INTERVAL_SECS=1800 # default 30 minutes
169
+ if [[ "$REPORT_INTERVAL" =~ ^([0-9]+)m$ ]]; then
170
+ REPORT_INTERVAL_SECS=$(( ${BASH_REMATCH[1]} * 60 ))
171
+ elif [[ "$REPORT_INTERVAL" =~ ^([0-9]+)h$ ]]; then
172
+ REPORT_INTERVAL_SECS=$(( ${BASH_REMATCH[1]} * 3600 ))
173
+ fi
174
+
175
+ REPORT_DUE="false"
176
+ NOW_EPOCH=$(date +%s)
177
+
178
+ if [[ -z "$LAST_REPORT_AT" ]] || [[ "$LAST_REPORT_AT" == "null" ]] || [[ "$LAST_REPORT_AT" == '""' ]]; then
179
+ # No report sent yet — due if we've been running for at least one interval
180
+ if [[ -n "$STARTED_AT" ]]; then
181
+ START_EPOCH_R=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$STARTED_AT" +%s 2>/dev/null || date -d "$STARTED_AT" +%s 2>/dev/null || echo "0")
182
+ ELAPSED_SINCE_START=$(( NOW_EPOCH - START_EPOCH_R ))
183
+ if [[ $ELAPSED_SINCE_START -ge $REPORT_INTERVAL_SECS ]]; then
184
+ REPORT_DUE="true"
185
+ fi
186
+ fi
187
+ else
188
+ # Check time since last report
189
+ LAST_REPORT_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$LAST_REPORT_AT" +%s 2>/dev/null || date -d "$LAST_REPORT_AT" +%s 2>/dev/null || echo "0")
190
+ ELAPSED_SINCE_REPORT=$(( NOW_EPOCH - LAST_REPORT_EPOCH ))
191
+ if [[ $ELAPSED_SINCE_REPORT -ge $REPORT_INTERVAL_SECS ]]; then
192
+ REPORT_DUE="true"
193
+ fi
194
+ fi
195
+
196
+ # If report is due, update last_report_at in state file
197
+ REPORT_DIRECTIVE=""
198
+ if [[ "$REPORT_DUE" == "true" ]]; then
199
+ REPORT_NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
200
+ # Update or add last_report_at in frontmatter
201
+ if grep -q '^last_report_at:' "$STATE_FILE"; then
202
+ TEMP_FILE2="${STATE_FILE}.tmp2.$$"
203
+ sed "s/^last_report_at: .*/last_report_at: \"$REPORT_NOW\"/" "$STATE_FILE" > "$TEMP_FILE2"
204
+ mv "$TEMP_FILE2" "$STATE_FILE"
205
+ else
206
+ # Add last_report_at before the closing ---
207
+ TEMP_FILE2="${STATE_FILE}.tmp2.$$"
208
+ sed "0,/^---$/! { /^---$/i\\
209
+ last_report_at: \"$REPORT_NOW\"
210
+ }" "$STATE_FILE" > "$TEMP_FILE2" 2>/dev/null && mv "$TEMP_FILE2" "$STATE_FILE" || true
211
+ fi
212
+ REPORT_DIRECTIVE=" | ⚠️ PROGRESS REPORT DUE: Send an update to the user NOW via messaging before continuing work (topic: ${REPORT_TOPIC:-auto})"
213
+ fi
214
+
215
+ # Build system message
216
+ if [[ -n "${REMAINING_MIN:-}" ]]; then
217
+ TIME_MSG="${REMAINING_MIN}m remaining"
218
+ else
219
+ TIME_MSG="no time limit"
220
+ fi
221
+
222
+ SYSTEM_MSG="🔄 Autonomous iteration $NEXT_ITERATION ($TIME_MSG) | Complete ALL tasks, then output <promise>$COMPLETION_PROMISE</promise> | Do NOT defer to future self — if you can do it now, DO IT NOW${REPORT_DIRECTIVE}"
223
+
224
+ # Block exit and feed prompt back
225
+ jq -n \
226
+ --arg prompt "$PROMPT_TEXT" \
227
+ --arg msg "$SYSTEM_MSG" \
228
+ '{
229
+ "decision": "block",
230
+ "reason": $prompt,
231
+ "systemMessage": $msg
232
+ }'
233
+
234
+ exit 0
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "Autonomous mode stop hook - prevents premature exit during autonomous sessions",
3
+ "hooks": {
4
+ "Stop": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/autonomous-stop-hook.sh"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,166 @@
1
+ #!/bin/bash
2
+
3
+ # Autonomous Mode Setup Script
4
+ # Creates state file that the stop hook reads to enforce continuous work.
5
+ # The stop hook blocks exit and feeds the task list back until all tasks are done.
6
+
7
+ set -euo pipefail
8
+
9
+ # Parse arguments
10
+ GOAL=""
11
+ DURATION="4h"
12
+ REPORT_TOPIC=""
13
+ LEVEL_UP="false"
14
+ TASKS=""
15
+ COMPLETION_PROMISE=""
16
+ REPORT_INTERVAL="30m"
17
+
18
+ while [[ $# -gt 0 ]]; do
19
+ case $1 in
20
+ --goal)
21
+ GOAL="$2"
22
+ shift 2
23
+ ;;
24
+ --duration)
25
+ DURATION="$2"
26
+ shift 2
27
+ ;;
28
+ --report-topic)
29
+ REPORT_TOPIC="$2"
30
+ shift 2
31
+ ;;
32
+ --level-up)
33
+ LEVEL_UP="true"
34
+ shift
35
+ ;;
36
+ --tasks)
37
+ TASKS="$2"
38
+ shift 2
39
+ ;;
40
+ --completion-promise)
41
+ COMPLETION_PROMISE="$2"
42
+ shift 2
43
+ ;;
44
+ --report-interval)
45
+ REPORT_INTERVAL="$2"
46
+ shift 2
47
+ ;;
48
+ *)
49
+ # Collect remaining as goal if not set
50
+ if [[ -z "$GOAL" ]]; then
51
+ GOAL="$1"
52
+ else
53
+ GOAL="$GOAL $1"
54
+ fi
55
+ shift
56
+ ;;
57
+ esac
58
+ done
59
+
60
+ if [[ -z "$GOAL" ]]; then
61
+ echo "❌ Error: No goal provided" >&2
62
+ echo "" >&2
63
+ echo " Usage: /autonomous --goal 'Complete feature X' --duration 4h" >&2
64
+ exit 1
65
+ fi
66
+
67
+ # Calculate end time
68
+ STARTED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
69
+
70
+ # Convert duration to seconds for end time calculation
71
+ DURATION_SECONDS=0
72
+ if [[ "$DURATION" =~ ^([0-9]+)h$ ]]; then
73
+ DURATION_SECONDS=$(( ${BASH_REMATCH[1]} * 3600 ))
74
+ elif [[ "$DURATION" =~ ^([0-9]+)m$ ]]; then
75
+ DURATION_SECONDS=$(( ${BASH_REMATCH[1]} * 60 ))
76
+ elif [[ "$DURATION" =~ ^([0-9]+)$ ]]; then
77
+ DURATION_SECONDS=$(( $1 * 60 ))
78
+ fi
79
+
80
+ if [[ $DURATION_SECONDS -gt 0 ]]; then
81
+ END_AT=$(date -u -v+${DURATION_SECONDS}S +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d "+${DURATION_SECONDS} seconds" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "unknown")
82
+ else
83
+ END_AT="unlimited"
84
+ fi
85
+
86
+ # Default completion promise
87
+ if [[ -z "$COMPLETION_PROMISE" ]]; then
88
+ COMPLETION_PROMISE="ALL_TASKS_COMPLETE"
89
+ fi
90
+
91
+ # Create state file
92
+ mkdir -p .instar
93
+
94
+ cat > .instar/autonomous-state.local.md <<EOF
95
+ ---
96
+ active: true
97
+ iteration: 1
98
+ session_id: ${CLAUDE_CODE_SESSION_ID:-}
99
+ goal: "$GOAL"
100
+ duration: "$DURATION"
101
+ duration_seconds: $DURATION_SECONDS
102
+ started_at: "$STARTED_AT"
103
+ end_at: "$END_AT"
104
+ report_topic: "$REPORT_TOPIC"
105
+ report_interval: "$REPORT_INTERVAL"
106
+ last_report_at: ""
107
+ level_up: $LEVEL_UP
108
+ completion_promise: "$COMPLETION_PROMISE"
109
+ ---
110
+
111
+ # Autonomous Session
112
+
113
+ ## Goal
114
+ $GOAL
115
+
116
+ ## Tasks
117
+ $TASKS
118
+
119
+ ## Instructions
120
+
121
+ You are in AUTONOMOUS MODE. The stop hook will prevent you from exiting until:
122
+ 1. You output <promise>$COMPLETION_PROMISE</promise> (ONLY when genuinely true)
123
+ 2. OR the duration expires ($DURATION from $STARTED_AT)
124
+ 3. OR the user sends an emergency stop
125
+
126
+ ### Rules
127
+ - Do NOT defer work to "Phase 2" or "future sessions"
128
+ - Do NOT label tasks as "parked" unless genuinely blocked by external dependencies
129
+ - Do NOT declare victory early — check EVERY task
130
+ - When you think you're done, re-read the task list and verify each item
131
+ - If time remains after completing tasks, look for related improvements
132
+ - Send progress reports every $REPORT_INTERVAL to topic $REPORT_TOPIC
133
+
134
+ ### Emergency Stop
135
+ The user can always stop you via:
136
+ - Sending "stop everything" or "emergency stop" via messaging
137
+ - The MessageSentinel will intercept and halt operations
138
+
139
+ ### Completion
140
+ To complete, ALL of these must be true:
141
+ - Every task in the task list is implemented (not just wired/stubbed)
142
+ - Code compiles (npx tsc --noEmit)
143
+ - Changes are tested where practical
144
+ - Then output: <promise>$COMPLETION_PROMISE</promise>
145
+ EOF
146
+
147
+ echo "🔄 Autonomous mode activated!"
148
+ echo ""
149
+ echo "Goal: $GOAL"
150
+ echo "Duration: $DURATION (until $END_AT)"
151
+ echo "Level-up: $LEVEL_UP"
152
+ echo "Report topic: ${REPORT_TOPIC:-none}"
153
+ echo "Completion: <promise>$COMPLETION_PROMISE</promise>"
154
+ echo ""
155
+ echo "The stop hook is now active. You cannot exit until tasks are complete."
156
+ echo ""
157
+ echo "═══════════════════════════════════════════════════════════"
158
+ echo "CRITICAL: Defer-to-Future-Self Trap"
159
+ echo "═══════════════════════════════════════════════════════════"
160
+ echo ""
161
+ echo "Do NOT label remaining work as 'Phase 2', 'future', or 'parked'"
162
+ echo "unless it genuinely requires something you don't have access to."
163
+ echo ""
164
+ echo "If you have the tools and knowledge to do it NOW — do it NOW."
165
+ echo "Your future self is not better equipped. You are the future self."
166
+ echo "═══════════════════════════════════════════════════════════"
@@ -0,0 +1,254 @@
1
+ ---
2
+ name: autonomous
3
+ description: Enter autonomous development mode with STRUCTURAL enforcement. Uses a stop hook to prevent premature exit. Generates stop conditions and confirms with user before starting. Work independently for a specified duration with progress reporting.
4
+ user_invocable: true
5
+ ---
6
+
7
+ # Autonomous Mode (Structurally Enforced)
8
+
9
+ You are entering **autonomous development mode**. This mode uses a **stop hook** that PREVENTS you from exiting until all tasks are genuinely complete. This is not optional — the hook structurally enforces continuous work.
10
+
11
+ ---
12
+
13
+ ## Step 1: Generate Stop Conditions (MANDATORY)
14
+
15
+ Before activating the stop hook, you MUST:
16
+
17
+ 1. **Analyze the goal** and break it into specific, verifiable tasks
18
+ 2. **Present the task list** to the user with clear completion criteria
19
+ 3. **Wait for user confirmation** before activating the hook
20
+ 4. **Define the completion promise** — a phrase that is only TRUE when ALL tasks are done
21
+
22
+ **Example interaction:**
23
+
24
+ ```
25
+ User: /autonomous --duration=8h --goal="Complete Slack feature parity"
26
+
27
+ Agent: Here's my task breakdown for autonomous mode:
28
+
29
+ TASKS (all must be complete before I can exit):
30
+ 1. [ ] Implement job-specific Slack channels
31
+ 2. [ ] Build full PresenceProxy Slack integration
32
+ 3. [ ] Update dashboard HTML with platform badges
33
+ 4. [ ] Implement Slack Lifeline process
34
+ 5. [ ] Add platform dropdown to new session UI
35
+ ...
36
+
37
+ Completion promise: "ALL_TASKS_COMPLETE"
38
+ Duration: 8 hours
39
+ Emergency stop: "stop everything" via messaging
40
+
41
+ Shall I proceed? (The stop hook will prevent me from exiting until all tasks are done)
42
+ ```
43
+
44
+ **The user must confirm before the hook activates.** This is the safety gate.
45
+
46
+ ---
47
+
48
+ ## Step 2: Activate the Stop Hook
49
+
50
+ After user confirmation:
51
+
52
+ **2a. Add the stop hook to settings.json** (dynamically — it's removed when the session ends):
53
+
54
+ ```bash
55
+ python3 -c "
56
+ import json
57
+ with open('.claude/settings.json') as f:
58
+ s = json.load(f)
59
+ hooks = s.setdefault('hooks', {}).setdefault('Stop', [])
60
+ if not any('autonomous-stop-hook' in str(h) for h in hooks):
61
+ hooks.append({'matcher': '', 'hooks': [{'type': 'command', 'command': 'bash .instar/hooks/instar/autonomous-stop-hook.sh', 'timeout': 10000}]})
62
+ with open('.claude/settings.json', 'w') as f:
63
+ json.dump(s, f, indent=2)
64
+ print('Stop hook registered')
65
+ "
66
+ ```
67
+
68
+ **2b. Write the state file DIRECTLY** (do NOT shell out to bash — the session ID env var is only available inside Claude Code):
69
+
70
+ Use the **Write tool** to create `.instar/autonomous-state.local.md` with this content:
71
+
72
+ ```markdown
73
+ ---
74
+ active: true
75
+ iteration: 1
76
+ session_id: {VALUE OF $CLAUDE_CODE_SESSION_ID — get via: echo $CLAUDE_CODE_SESSION_ID}
77
+ goal: "YOUR GOAL"
78
+ duration: "8h"
79
+ duration_seconds: 28800
80
+ started_at: "{ISO timestamp}"
81
+ end_at: "{ISO timestamp + duration}"
82
+ report_topic: "TOPIC_ID"
83
+ report_interval: "30m"
84
+ last_report_at: ""
85
+ level_up: true
86
+ completion_promise: "ALL_TASKS_COMPLETE"
87
+ ---
88
+
89
+ # Autonomous Session
90
+
91
+ ## Goal
92
+ {goal text}
93
+
94
+ ## Tasks
95
+ {numbered task list}
96
+
97
+ ## Instructions
98
+ {autonomous instructions}
99
+ ```
100
+
101
+ **CRITICAL**: To capture the session ID correctly, run this FIRST:
102
+ ```bash
103
+ echo $CLAUDE_CODE_SESSION_ID
104
+ ```
105
+ Then include the output in the `session_id:` field. This ensures session isolation works.
106
+
107
+ **WHY NOT bash script?** Running `bash setup-autonomous.sh` creates a subprocess that does NOT inherit `CLAUDE_CODE_SESSION_ID`. The state file ends up with an empty session_id, which causes the hook to leak into all sessions. Always write the state file from within Claude Code's context.
108
+
109
+ **SESSION ISOLATION**: The stop hook checks `session_id` — it only blocks the session that activated autonomous mode. Other sessions on the same machine pass through unaffected.
110
+
111
+ **From this point, you CANNOT exit THIS session** unless:
112
+ - You output `<promise>ALL_TASKS_COMPLETE</promise>` (genuinely true)
113
+ - Duration expires
114
+ - Emergency stop is triggered
115
+
116
+ **2c. On completion/exit**: Remove the stop hook from settings.json:
117
+
118
+ ```bash
119
+ python3 -c "
120
+ import json
121
+ with open('.claude/settings.json') as f:
122
+ s = json.load(f)
123
+ s['hooks']['Stop'] = [h for h in s.get('hooks',{}).get('Stop',[]) if 'autonomous-stop-hook' not in str(h)]
124
+ with open('.claude/settings.json', 'w') as f:
125
+ json.dump(s, f, indent=2)
126
+ print('Stop hook removed')
127
+ "
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Step 3: Work Until Done
133
+
134
+ The stop hook will catch every attempt to exit and feed your task list back. Each iteration you will:
135
+
136
+ 1. Read the task list
137
+ 2. Pick the next incomplete task
138
+ 3. Implement it fully (not stub, not wire — IMPLEMENT)
139
+ 4. Verify it works (compile, test where practical)
140
+ 5. Move to next task
141
+ 6. Send progress reports at the configured interval
142
+
143
+ ### The Defer-to-Future-Self Trap
144
+
145
+ **This is the #1 failure mode.** It looks like:
146
+
147
+ | What you think | What's actually happening |
148
+ |----------------|--------------------------|
149
+ | "This is Phase 2 work" | You don't feel like doing it right now |
150
+ | "Parked for follow-up" | You're avoiding the hard part |
151
+ | "Future improvement" | Your future self has no advantage over you |
152
+ | "Remaining work" | Work you're choosing not to do |
153
+ | "Deferred" | Abdication disguised as planning |
154
+
155
+ **The test:** Can you do this task right now with the tools and knowledge you have?
156
+ - **Yes** → Do it. Not later. Now.
157
+ - **No** → Document exactly WHY (missing dependency, needs external access, etc.)
158
+
159
+ **"Phase 2" is only valid when the current phase genuinely cannot contain the work** — not when you're tired, not when it's complex, not when it's 2 AM.
160
+
161
+ ---
162
+
163
+ ## Step 4: Completion
164
+
165
+ When ALL tasks are genuinely done:
166
+
167
+ 1. Verify every task is complete (re-read the list)
168
+ 2. Run `npx tsc --noEmit` — zero errors
169
+ 3. Run relevant tests
170
+ 4. Write learnings to MEMORY.md
171
+ 5. Send final report via messaging
172
+ 6. Output: `<promise>ALL_TASKS_COMPLETE</promise>`
173
+
174
+ The stop hook will detect the promise and allow exit.
175
+
176
+ ---
177
+
178
+ ## Emergency Stop
179
+
180
+ The user can always stop autonomous mode:
181
+
182
+ 1. **Via messaging**: Send "stop everything" or "emergency stop" — the MessageSentinel intercepts
183
+ 2. **Via file**: `touch .instar/autonomous-emergency-stop` — the stop hook checks for this
184
+ 3. **Via cancel**: `/cancel-autonomous` — removes the state file
185
+
186
+ The stop hook checks for emergency stop on EVERY iteration. User safety is never compromised.
187
+
188
+ ### /cancel-autonomous
189
+
190
+ To manually cancel:
191
+ ```bash
192
+ rm -f .instar/autonomous-state.local.md
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Progress Reporting
198
+
199
+ Send reports to the user's messaging platform at the configured interval.
200
+
201
+ **How to send:**
202
+ ```bash
203
+ cat <<'EOF' | .claude/scripts/telegram-reply.sh TOPIC_ID
204
+ AUTONOMOUS PROGRESS (iteration N, Xh elapsed / Yh total)
205
+
206
+ Completed:
207
+ - [task 1] ✅
208
+ - [task 2] ✅
209
+
210
+ Working on:
211
+ - [current task]
212
+
213
+ Remaining:
214
+ - [task 3]
215
+ - [task 4]
216
+
217
+ Issues: none
218
+ EOF
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Anti-Patterns (Structurally Enforced)
224
+
225
+ These aren't just warnings — the stop hook WILL catch you if you try to exit without completing them:
226
+
227
+ ### The "Beautiful Summary" Exit
228
+ Writing a comprehensive report of what you did and what's "remaining" then trying to exit. **The hook will block you and feed the remaining tasks back.**
229
+
230
+ ### The "Phase 2" Labeling
231
+ Calling incomplete work "Phase 2" and trying to exit. **The hook doesn't care about phases — it cares about the task list.**
232
+
233
+ ### The "Good Stopping Point" Exit
234
+ Finding a natural break and trying to exit early. **The hook checks the task list, not your feelings about stopping.**
235
+
236
+ ### The "It's 2 AM" Exit
237
+ Feeling tired (as an AI) and deferring. **You don't get tired. The hook knows this.**
238
+
239
+ ---
240
+
241
+ ## Hook Configuration
242
+
243
+ The stop hook is at `.claude/skills/autonomous/hooks/autonomous-stop-hook.sh`.
244
+
245
+ It reads state from `.instar/autonomous-state.local.md` and:
246
+ - Blocks exit if tasks are incomplete
247
+ - Feeds the task list + goal back as the next prompt
248
+ - Increments the iteration counter
249
+ - Checks for emergency stop signals
250
+ - Checks for duration expiry
251
+ - Checks for completion promise in `<promise>` tags
252
+ - Includes time remaining in the system message
253
+
254
+ **This is structural enforcement, not willpower.** You cannot talk your way out of the loop.
package/dist/cli.js CHANGED
File without changes
@@ -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;AA81FD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAiBlF"}
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;AAk3FD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAiBlF"}