oh-my-claude-sisyphus 3.8.3 → 3.8.4

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.
@@ -10,7 +10,7 @@
10
10
  {
11
11
  "name": "oh-my-claudecode",
12
12
  "description": "Claude Code native multi-agent orchestration with intelligent model routing, 28 agent variants, and 30 powerful skills. Zero learning curve. Maximum power.",
13
- "version": "3.8.3",
13
+ "version": "3.8.4",
14
14
  "author": {
15
15
  "name": "Yeachan Heo",
16
16
  "email": "hurrc04@gmail.com"
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "oh-my-claudecode",
3
- "version": "3.8.3",
3
+ "version": "3.8.4",
4
4
  "description": "Multi-agent orchestration system for Claude Code",
5
5
  "author": {
6
6
  "name": "oh-my-claudecode contributors"
7
7
  },
8
- "repository": "https://github.com/nicobailon/oh-my-claudecode",
9
- "homepage": "https://github.com/nicobailon/oh-my-claudecode",
8
+ "repository": "https://github.com/Yeachan-Heo/oh-my-claudecode",
9
+ "homepage": "https://github.com/Yeachan-Heo/oh-my-claudecode",
10
10
  "license": "MIT",
11
11
  "keywords": [
12
12
  "claude-code",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-claude-sisyphus",
3
- "version": "3.8.3",
3
+ "version": "3.8.4",
4
4
  "description": "Multi-agent orchestration system for Claude Code - Inspired by oh-my-opencode",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -3,7 +3,7 @@
3
3
  // Restores persistent mode states when session starts
4
4
  // Cross-platform: Windows, macOS, Linux
5
5
 
6
- import { existsSync, readFileSync, readdirSync } from 'fs';
6
+ import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { homedir } from 'os';
9
9
 
@@ -24,6 +24,76 @@ function readJsonFile(path) {
24
24
  }
25
25
  }
26
26
 
27
+ function writeJsonFile(path, data) {
28
+ try {
29
+ const dir = join(path, '..');
30
+ if (!existsSync(dir)) {
31
+ mkdirSync(dir, { recursive: true });
32
+ }
33
+ writeFileSync(path, JSON.stringify(data, null, 2), 'utf-8');
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ async function checkForUpdates(currentVersion) {
41
+ const cacheFile = join(homedir(), '.omc', 'update-check.json');
42
+ const now = Date.now();
43
+ const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
44
+
45
+ // Check cache first
46
+ const cached = readJsonFile(cacheFile);
47
+ if (cached && cached.timestamp && (now - cached.timestamp) < CACHE_DURATION) {
48
+ return cached.updateAvailable ? cached : null;
49
+ }
50
+
51
+ // Fetch latest version from npm
52
+ try {
53
+ const controller = new AbortController();
54
+ const timeoutId = setTimeout(() => controller.abort(), 2000);
55
+
56
+ const response = await fetch('https://registry.npmjs.org/oh-my-claude-sisyphus/latest', {
57
+ signal: controller.signal
58
+ });
59
+ clearTimeout(timeoutId);
60
+
61
+ if (!response.ok) {
62
+ throw new Error('Network response was not ok');
63
+ }
64
+
65
+ const data = await response.json();
66
+ const latestVersion = data.version;
67
+
68
+ const updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
69
+
70
+ const cacheData = {
71
+ timestamp: now,
72
+ latestVersion,
73
+ currentVersion,
74
+ updateAvailable
75
+ };
76
+
77
+ writeJsonFile(cacheFile, cacheData);
78
+
79
+ return updateAvailable ? cacheData : null;
80
+ } catch (error) {
81
+ // Silent fail - network unavailable or timeout
82
+ return null;
83
+ }
84
+ }
85
+
86
+ function compareVersions(v1, v2) {
87
+ const parts1 = v1.replace(/^v/, '').split('.').map(Number);
88
+ const parts2 = v2.replace(/^v/, '').split('.').map(Number);
89
+
90
+ for (let i = 0; i < 3; i++) {
91
+ const diff = (parts1[i] || 0) - (parts2[i] || 0);
92
+ if (diff !== 0) return diff;
93
+ }
94
+ return 0;
95
+ }
96
+
27
97
  function countIncompleteTodos(todosDir) {
28
98
  let count = 0;
29
99
  if (existsSync(todosDir)) {
@@ -49,6 +119,30 @@ async function main() {
49
119
  const directory = data.directory || process.cwd();
50
120
  const messages = [];
51
121
 
122
+ // Check for updates (non-blocking)
123
+ const packageJsonPath = join(directory, 'package.json');
124
+ let currentVersion = '3.8.4'; // fallback
125
+ const packageJson = readJsonFile(packageJsonPath);
126
+ if (packageJson?.version) {
127
+ currentVersion = packageJson.version;
128
+ }
129
+
130
+ const updateInfo = await checkForUpdates(currentVersion);
131
+ if (updateInfo) {
132
+ messages.push(`<session-restore>
133
+
134
+ [OMC UPDATE AVAILABLE]
135
+
136
+ A new version of oh-my-claudecode is available: v${updateInfo.latestVersion} (current: ${updateInfo.currentVersion})
137
+
138
+ To update, run: claude /install-plugin oh-my-claudecode
139
+
140
+ </session-restore>
141
+
142
+ ---
143
+ `);
144
+ }
145
+
52
146
  // Check for ultrawork state
53
147
  const ultraworkState = readJsonFile(join(directory, '.omc', 'ultrawork-state.json'))
54
148
  || readJsonFile(join(homedir(), '.claude', 'ultrawork-state.json'));
@@ -1,268 +0,0 @@
1
- #!/bin/bash
2
- # OMC Keyword Detector Hook (Bash)
3
- # Detects magic keywords and invokes skill tools
4
- # Linux/macOS compatible
5
- #
6
- # Supported keywords (in priority order):
7
- # 1. cancel: Stop active modes
8
- # 2. ralph: Persistence mode until task completion
9
- # 3. autopilot: Full autonomous execution
10
- # 4. ultrapilot: Parallel autopilot
11
- # 5. ultrawork/ulw: Maximum parallel execution
12
- # 6. ecomode/eco: Token-efficient execution
13
- # 7. swarm: N coordinated agents
14
- # 8. pipeline: Sequential agent chaining
15
- # 9. ralplan: Iterative planning with consensus
16
- # 10. plan: Planning interview mode
17
- # 11. tdd: Test-driven development
18
- # 12. research: Research orchestration
19
- # 13. ultrathink/think: Extended reasoning
20
- # 14. deepsearch: Codebase search (restricted patterns)
21
- # 15. analyze: Analysis mode (restricted patterns)
22
-
23
- # Read stdin (JSON input from Claude Code)
24
- INPUT=$(cat)
25
-
26
- # Extract directory from input
27
- DIRECTORY=""
28
- if command -v jq &> /dev/null; then
29
- DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
30
- fi
31
- if [ -z "$DIRECTORY" ] || [ "$DIRECTORY" = "null" ]; then
32
- DIRECTORY=$(pwd)
33
- fi
34
-
35
- # Extract the prompt text - try multiple JSON paths
36
- PROMPT=""
37
- if command -v jq &> /dev/null; then
38
- # Try to extract from various possible JSON structures
39
- PROMPT=$(echo "$INPUT" | jq -r '
40
- if .prompt then .prompt
41
- elif .message.content then .message.content
42
- elif .parts then ([.parts[] | select(.type == "text") | .text] | join(" "))
43
- else ""
44
- end
45
- ' 2>/dev/null)
46
- fi
47
-
48
- # Fallback: simple grep extraction if jq fails
49
- if [ -z "$PROMPT" ] || [ "$PROMPT" = "null" ]; then
50
- PROMPT=$(echo "$INPUT" | grep -oP '"(prompt|content|text)"\s*:\s*"\K[^"]+' | head -1)
51
- fi
52
-
53
- # Exit if no prompt found
54
- if [ -z "$PROMPT" ]; then
55
- echo '{"continue": true}'
56
- exit 0
57
- fi
58
-
59
- # Remove code blocks before checking keywords (prevents false positives)
60
- PROMPT_NO_CODE=$(echo "$PROMPT" | sed 's/```[^`]*```//g' | sed 's/`[^`]*`//g')
61
-
62
- # Convert to lowercase for case-insensitive matching
63
- PROMPT_LOWER=$(echo "$PROMPT_NO_CODE" | tr '[:upper:]' '[:lower:]')
64
-
65
- # Create a skill invocation message that tells Claude to use the Skill tool
66
- create_skill_invocation() {
67
- local skill_name="$1"
68
- local original_prompt="$2"
69
- local args="$3"
70
-
71
- local args_section=""
72
- if [ -n "$args" ]; then
73
- args_section="\\nArguments: $args"
74
- fi
75
-
76
- local skill_upper=$(echo "$skill_name" | tr '[:lower:]' '[:upper:]')
77
-
78
- cat << EOF
79
- [MAGIC KEYWORD: ${skill_upper}]
80
-
81
- You MUST invoke the skill using the Skill tool:
82
-
83
- Skill: oh-my-claudecode:${skill_name}${args_section}
84
-
85
- User request:
86
- ${original_prompt}
87
-
88
- IMPORTANT: Invoke the skill IMMEDIATELY. Do not proceed without loading the skill instructions.
89
- EOF
90
- }
91
-
92
- # Clear state files for cancel operation
93
- clear_state_files() {
94
- local directory="$1"
95
- local modes=("ralph" "autopilot" "ultrapilot" "ultrawork" "ecomode" "swarm" "pipeline")
96
-
97
- for mode in "${modes[@]}"; do
98
- rm -f "$directory/.omc/state/${mode}-state.json" 2>/dev/null
99
- rm -f "$HOME/.omc/state/${mode}-state.json" 2>/dev/null
100
- done
101
- }
102
-
103
- # Activate state for a mode
104
- activate_state() {
105
- local directory="$1"
106
- local prompt="$2"
107
- local state_name="$3"
108
-
109
- # Create directories
110
- mkdir -p "$directory/.omc/state" 2>/dev/null
111
- mkdir -p "$HOME/.omc/state" 2>/dev/null
112
-
113
- # Escape prompt for JSON
114
- local prompt_escaped=$(echo "$prompt" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ')
115
- local timestamp=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S%z)
116
-
117
- local state_json="{
118
- \"active\": true,
119
- \"started_at\": \"$timestamp\",
120
- \"original_prompt\": \"$prompt_escaped\",
121
- \"reinforcement_count\": 0,
122
- \"last_checked_at\": \"$timestamp\"
123
- }"
124
-
125
- # Write state to both local and global locations
126
- echo "$state_json" > "$directory/.omc/state/${state_name}-state.json" 2>/dev/null
127
- echo "$state_json" > "$HOME/.omc/state/${state_name}-state.json" 2>/dev/null
128
- }
129
-
130
- # Output JSON with skill invocation message
131
- output_skill() {
132
- local skill_name="$1"
133
- local prompt="$2"
134
- local args="$3"
135
-
136
- local message=$(create_skill_invocation "$skill_name" "$prompt" "$args")
137
- # Escape for JSON: backslashes, quotes, and newlines
138
- local escaped_message=$(echo "$message" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//')
139
-
140
- echo "{\"continue\": true, \"message\": \"$escaped_message\"}"
141
- }
142
-
143
- # Priority 1: Cancel (BEFORE other modes - clears states)
144
- if echo "$PROMPT_LOWER" | grep -qE '\b(stop|cancel|abort)\b'; then
145
- clear_state_files "$DIRECTORY"
146
- output_skill "cancel" "$PROMPT"
147
- exit 0
148
- fi
149
-
150
- # Priority 2: Ralph keywords
151
- if echo "$PROMPT_LOWER" | grep -qE '\b(ralph|don'\''t stop|must complete|until done)\b'; then
152
- activate_state "$DIRECTORY" "$PROMPT" "ralph"
153
- activate_state "$DIRECTORY" "$PROMPT" "ultrawork"
154
- output_skill "ralph" "$PROMPT"
155
- exit 0
156
- fi
157
-
158
- # Priority 3: Autopilot keywords
159
- if echo "$PROMPT_LOWER" | grep -qE '\b(autopilot|auto pilot|auto-pilot|autonomous|full auto|fullsend)\b' || \
160
- echo "$PROMPT_LOWER" | grep -qE '\bbuild\s+me\s+' || \
161
- echo "$PROMPT_LOWER" | grep -qE '\bcreate\s+me\s+' || \
162
- echo "$PROMPT_LOWER" | grep -qE '\bmake\s+me\s+' || \
163
- echo "$PROMPT_LOWER" | grep -qE '\bi\s+want\s+a\s+' || \
164
- echo "$PROMPT_LOWER" | grep -qE '\bi\s+want\s+an\s+' || \
165
- echo "$PROMPT_LOWER" | grep -qE '\bhandle\s+it\s+all\b' || \
166
- echo "$PROMPT_LOWER" | grep -qE '\bend\s+to\s+end\b' || \
167
- echo "$PROMPT_LOWER" | grep -qE '\be2e\s+this\b'; then
168
- activate_state "$DIRECTORY" "$PROMPT" "autopilot"
169
- output_skill "autopilot" "$PROMPT"
170
- exit 0
171
- fi
172
-
173
- # Priority 4: Ultrapilot
174
- if echo "$PROMPT_LOWER" | grep -qE '\b(ultrapilot|ultra-pilot)\b' || \
175
- echo "$PROMPT_LOWER" | grep -qE '\bparallel\s+build\b' || \
176
- echo "$PROMPT_LOWER" | grep -qE '\bswarm\s+build\b'; then
177
- activate_state "$DIRECTORY" "$PROMPT" "ultrapilot"
178
- output_skill "ultrapilot" "$PROMPT"
179
- exit 0
180
- fi
181
-
182
- # Priority 5: Ultrawork keywords
183
- if echo "$PROMPT_LOWER" | grep -qE '\b(ultrawork|ulw|uw)\b'; then
184
- activate_state "$DIRECTORY" "$PROMPT" "ultrawork"
185
- output_skill "ultrawork" "$PROMPT"
186
- exit 0
187
- fi
188
-
189
- # Priority 6: Ecomode keywords
190
- if echo "$PROMPT_LOWER" | grep -qE '\b(eco|ecomode|eco-mode|efficient|save-tokens|budget)\b'; then
191
- activate_state "$DIRECTORY" "$PROMPT" "ecomode"
192
- output_skill "ecomode" "$PROMPT"
193
- exit 0
194
- fi
195
-
196
- # Priority 7: Swarm - parse N from "swarm N agents"
197
- SWARM_MATCH=$(echo "$PROMPT_LOWER" | grep -oE '\bswarm\s+[0-9]+\s+agents?\b' | grep -oE '[0-9]+')
198
- if [ -n "$SWARM_MATCH" ]; then
199
- output_skill "swarm" "$PROMPT" "$SWARM_MATCH"
200
- exit 0
201
- fi
202
- if echo "$PROMPT_LOWER" | grep -qE '\bcoordinated\s+agents\b'; then
203
- output_skill "swarm" "$PROMPT" "3"
204
- exit 0
205
- fi
206
-
207
- # Priority 8: Pipeline
208
- if echo "$PROMPT_LOWER" | grep -qE '\b(pipeline)\b' || \
209
- echo "$PROMPT_LOWER" | grep -qE '\bchain\s+agents\b'; then
210
- output_skill "pipeline" "$PROMPT"
211
- exit 0
212
- fi
213
-
214
- # Priority 9: Ralplan keyword (before plan to avoid false match)
215
- if echo "$PROMPT_LOWER" | grep -qE '\b(ralplan)\b'; then
216
- output_skill "ralplan" "$PROMPT"
217
- exit 0
218
- fi
219
-
220
- # Priority 10: Plan keywords
221
- if echo "$PROMPT_LOWER" | grep -qE '\b(plan this|plan the)\b'; then
222
- output_skill "plan" "$PROMPT"
223
- exit 0
224
- fi
225
-
226
- # Priority 11: TDD
227
- if echo "$PROMPT_LOWER" | grep -qE '\b(tdd)\b' || \
228
- echo "$PROMPT_LOWER" | grep -qE '\btest\s+first\b' || \
229
- echo "$PROMPT_LOWER" | grep -qE '\bred\s+green\b'; then
230
- output_skill "tdd" "$PROMPT"
231
- exit 0
232
- fi
233
-
234
- # Priority 12: Research
235
- if echo "$PROMPT_LOWER" | grep -qE '\b(research)\b' || \
236
- echo "$PROMPT_LOWER" | grep -qE '\banalyze\s+data\b' || \
237
- echo "$PROMPT_LOWER" | grep -qE '\bstatistics\b'; then
238
- output_skill "research" "$PROMPT"
239
- exit 0
240
- fi
241
-
242
- # Priority 13: Ultrathink/think keywords (keep inline message)
243
- if echo "$PROMPT_LOWER" | grep -qE '\b(ultrathink|think hard|think deeply)\b'; then
244
- cat << 'EOF'
245
- {"continue": true, "message": "<think-mode>\n\n**ULTRATHINK MODE ENABLED** - Extended reasoning activated.\n\nYou are now in deep thinking mode. Take your time to:\n1. Thoroughly analyze the problem from multiple angles\n2. Consider edge cases and potential issues\n3. Think through the implications of each approach\n4. Reason step-by-step before acting\n\nUse your extended thinking capabilities to provide the most thorough and well-reasoned response.\n\n</think-mode>\n\n---\n"}
246
- EOF
247
- exit 0
248
- fi
249
-
250
- # Priority 14: Deepsearch (RESTRICTED patterns)
251
- if echo "$PROMPT_LOWER" | grep -qE '\b(deepsearch)\b' || \
252
- echo "$PROMPT_LOWER" | grep -qE '\bsearch\s+(the\s+)?(codebase|code|files|project)\b' || \
253
- echo "$PROMPT_LOWER" | grep -qE '\bfind\s+(in\s+)?(codebase|code|all\s+files)\b'; then
254
- output_skill "deepsearch" "$PROMPT"
255
- exit 0
256
- fi
257
-
258
- # Priority 15: Analyze (RESTRICTED patterns)
259
- if echo "$PROMPT_LOWER" | grep -qE '\bdeep\s*analyze\b' || \
260
- echo "$PROMPT_LOWER" | grep -qE '\binvestigate\s+(the|this|why)\b' || \
261
- echo "$PROMPT_LOWER" | grep -qE '\bdebug\s+(the|this|why)\b'; then
262
- output_skill "analyze" "$PROMPT"
263
- exit 0
264
- fi
265
-
266
- # No keywords detected - continue without modification
267
- echo '{"continue": true}'
268
- exit 0
@@ -1,244 +0,0 @@
1
- #!/bin/bash
2
- # OMC Persistent Mode Hook
3
- # Unified handler for ultrawork, ralph-loop, and todo continuation
4
- # Prevents stopping when work remains incomplete
5
-
6
- # Validate session ID to prevent path traversal attacks
7
- # Returns 0 (success) for valid, 1 for invalid
8
- is_valid_session_id() {
9
- local id="$1"
10
- if [ -z "$id" ]; then
11
- return 1
12
- fi
13
- # Allow alphanumeric, hyphens, and underscores only
14
- # Must not start with dot or hyphen, max 256 chars
15
- if echo "$id" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$'; then
16
- return 0
17
- fi
18
- return 1
19
- }
20
-
21
- # Read stdin
22
- INPUT=$(cat)
23
-
24
- # Get session ID and directory
25
- SESSION_ID=""
26
- DIRECTORY=""
27
- if command -v jq &> /dev/null; then
28
- SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
29
- DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
30
- fi
31
-
32
- # Default to current directory
33
- if [ -z "$DIRECTORY" ]; then
34
- DIRECTORY=$(pwd)
35
- fi
36
-
37
- # Check for incomplete tasks in new Task system (priority over todos)
38
- TASKS_DIR="$HOME/.claude/tasks"
39
- TASK_COUNT=0
40
- JQ_AVAILABLE=false
41
- if command -v jq &> /dev/null; then
42
- JQ_AVAILABLE=true
43
- fi
44
-
45
- if [ -n "$SESSION_ID" ] && is_valid_session_id "$SESSION_ID" && [ -d "$TASKS_DIR/$SESSION_ID" ]; then
46
- for task_file in "$TASKS_DIR/$SESSION_ID"/*.json; do
47
- if [ -f "$task_file" ] && [ "$(basename "$task_file")" != ".lock" ]; then
48
- if [ "$JQ_AVAILABLE" = "true" ]; then
49
- STATUS=$(jq -r '.status // "pending"' "$task_file" 2>/dev/null)
50
- # Match TypeScript isTaskIncomplete(): only pending/in_progress are incomplete
51
- # 'deleted' and 'completed' are both treated as done
52
- if [ "$STATUS" = "pending" ] || [ "$STATUS" = "in_progress" ]; then
53
- TASK_COUNT=$((TASK_COUNT + 1))
54
- fi
55
- else
56
- # Fallback: grep for incomplete status values (pending or in_progress)
57
- # This is less accurate but provides basic functionality
58
- if grep -qE '"status"[[:space:]]*:[[:space:]]*"(pending|in_progress)"' "$task_file" 2>/dev/null; then
59
- TASK_COUNT=$((TASK_COUNT + 1))
60
- fi
61
- fi
62
- fi
63
- done
64
-
65
- # Warn if using fallback (only once per invocation, to stderr)
66
- if [ "$JQ_AVAILABLE" = "false" ] && [ "$TASK_COUNT" -gt 0 ]; then
67
- echo "[OMC WARNING] jq not installed - Task counting may be less accurate. Install jq for best results." >&2
68
- fi
69
- fi
70
-
71
- # Extract stop reason for abort detection
72
- STOP_REASON=""
73
- USER_REQUESTED=""
74
- if command -v jq &> /dev/null; then
75
- STOP_REASON=$(echo "$INPUT" | jq -r '.stop_reason // .stopReason // ""' 2>/dev/null)
76
- USER_REQUESTED=$(echo "$INPUT" | jq -r '.user_requested // .userRequested // "false"' 2>/dev/null)
77
- fi
78
-
79
- # Check for user abort before continuation checks
80
- # NOTE: Abort patterns are assumed - verify against actual Claude Code API values
81
- if [ "$USER_REQUESTED" = "true" ] || echo "$STOP_REASON" | grep -qiE "(abort|cancel|interrupt|ctrl_c|manual_stop)"; then
82
- echo '{"continue": true}'
83
- exit 0
84
- fi
85
-
86
- # Check for active ultrawork state
87
- ULTRAWORK_STATE=""
88
- if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ]; then
89
- ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null)
90
- elif [ -f "$HOME/.omc/state/ultrawork-state.json" ]; then
91
- ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
92
- fi
93
-
94
- # Check for active ralph loop
95
- RALPH_STATE=""
96
- if [ -f "$DIRECTORY/.omc/state/ralph-state.json" ]; then
97
- RALPH_STATE=$(cat "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null)
98
- fi
99
-
100
- # Check for verification state (oracle verification)
101
- VERIFICATION_STATE=""
102
- if [ -f "$DIRECTORY/.omc/state/ralph-verification.json" ]; then
103
- VERIFICATION_STATE=$(cat "$DIRECTORY/.omc/state/ralph-verification.json" 2>/dev/null)
104
- fi
105
-
106
- # Check for incomplete todos
107
- INCOMPLETE_COUNT=0
108
- TODOS_DIR="$HOME/.claude/todos"
109
- if [ -d "$TODOS_DIR" ]; then
110
- for todo_file in "$TODOS_DIR"/*.json; do
111
- if [ -f "$todo_file" ]; then
112
- if command -v jq &> /dev/null; then
113
- COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
114
- INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
115
- else
116
- # Fallback: count "pending" or "in_progress" occurrences
117
- COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\|in_progress"' "$todo_file" 2>/dev/null) || COUNT=0
118
- INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
119
- fi
120
- fi
121
- done
122
- fi
123
-
124
- # Check project todos as well
125
- for todo_path in "$DIRECTORY/.omc/todos.json" "$DIRECTORY/.claude/todos.json"; do
126
- if [ -f "$todo_path" ]; then
127
- if command -v jq &> /dev/null; then
128
- COUNT=$(jq 'if type == "array" then [.[] | select(.status != "completed" and .status != "cancelled")] | length else 0 end' "$todo_path" 2>/dev/null || echo "0")
129
- INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
130
- else
131
- # Fallback: count "pending" or "in_progress" occurrences
132
- COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\|in_progress"' "$todo_path" 2>/dev/null) || COUNT=0
133
- INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
134
- fi
135
- fi
136
- done
137
-
138
- # Combine Task and todo counts
139
- TOTAL_INCOMPLETE=$((TASK_COUNT + INCOMPLETE_COUNT))
140
-
141
- # Priority 1: Ralph Loop with Oracle Verification
142
- if [ -n "$RALPH_STATE" ]; then
143
- IS_ACTIVE=$(echo "$RALPH_STATE" | jq -r '.active // false' 2>/dev/null)
144
- if [ "$IS_ACTIVE" = "true" ]; then
145
- ITERATION=$(echo "$RALPH_STATE" | jq -r '.iteration // 1' 2>/dev/null)
146
- MAX_ITER=$(echo "$RALPH_STATE" | jq -r '.max_iterations // 10' 2>/dev/null)
147
- PROMISE=$(echo "$RALPH_STATE" | jq -r '.completion_promise // "TASK_COMPLETE"' 2>/dev/null)
148
- PROMPT=$(echo "$RALPH_STATE" | jq -r '.prompt // ""' 2>/dev/null)
149
-
150
- # Check if oracle verification is pending
151
- if [ -n "$VERIFICATION_STATE" ]; then
152
- IS_PENDING=$(echo "$VERIFICATION_STATE" | jq -r '.pending // false' 2>/dev/null)
153
- if [ "$IS_PENDING" = "true" ]; then
154
- ATTEMPT=$(echo "$VERIFICATION_STATE" | jq -r '.verification_attempts // 0' 2>/dev/null)
155
- MAX_ATTEMPTS=$(echo "$VERIFICATION_STATE" | jq -r '.max_verification_attempts // 3' 2>/dev/null)
156
- ORIGINAL_TASK=$(echo "$VERIFICATION_STATE" | jq -r '.original_task // ""' 2>/dev/null)
157
- COMPLETION_CLAIM=$(echo "$VERIFICATION_STATE" | jq -r '.completion_claim // ""' 2>/dev/null)
158
- ORACLE_FEEDBACK=$(echo "$VERIFICATION_STATE" | jq -r '.oracle_feedback // ""' 2>/dev/null)
159
- NEXT_ATTEMPT=$((ATTEMPT + 1))
160
-
161
- FEEDBACK_SECTION=""
162
- if [ -n "$ORACLE_FEEDBACK" ] && [ "$ORACLE_FEEDBACK" != "null" ]; then
163
- FEEDBACK_SECTION="\\n**Previous Oracle Feedback (rejected):**\\n$ORACLE_FEEDBACK\\n"
164
- fi
165
-
166
- cat << EOF
167
- {"continue": false, "reason": "<ralph-verification>\\n\\n[ORACLE VERIFICATION REQUIRED - Attempt $NEXT_ATTEMPT/$MAX_ATTEMPTS]\\n\\nThe agent claims the task is complete. Before accepting, YOU MUST verify with Oracle.\\n\\n**Original Task:**\\n$ORIGINAL_TASK\\n\\n**Completion Claim:**\\n$COMPLETION_CLAIM\\n$FEEDBACK_SECTION\\n## MANDATORY VERIFICATION STEPS\\n\\n1. **Spawn Oracle Agent** for verification:\\n \`\`\`\\n Task(subagent_type=\"oracle\", prompt=\"Verify this task completion claim...\")\\n \`\`\`\\n\\n2. **Oracle must check:**\\n - Are ALL requirements from the original task met?\\n - Is the implementation complete, not partial?\\n - Are there any obvious bugs or issues?\\n - Does the code compile/run without errors?\\n - Are tests passing (if applicable)?\\n\\n3. **Based on Oracle's response:**\\n - If APPROVED: Output \`<oracle-approved>VERIFIED_COMPLETE</oracle-approved>\`\\n - If REJECTED: Continue working on the identified issues\\n\\nDO NOT output the completion promise again until Oracle approves.\\n\\n</ralph-verification>\\n\\n---\\n"}
168
- EOF
169
- exit 0
170
- fi
171
- fi
172
-
173
- if [ "$ITERATION" -lt "$MAX_ITER" ]; then
174
- # Increment iteration
175
- NEW_ITER=$((ITERATION + 1))
176
- echo "$RALPH_STATE" | jq ".iteration = $NEW_ITER" > "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null
177
-
178
- cat << EOF
179
- {"continue": false, "reason": "<ralph-loop-continuation>\\n\\n[RALPH LOOP - ITERATION $NEW_ITER/$MAX_ITER]\\n\\nYour previous attempt did not output the completion promise. The work is NOT done yet.\\n\\nCRITICAL INSTRUCTIONS:\\n1. Review your progress and the original task\\n2. Check your todo list - are ALL items marked complete?\\n3. Continue from where you left off\\n4. When FULLY complete, output: <promise>$PROMISE</promise>\\n5. Do NOT stop until the task is truly done\\n\\nOriginal task: $PROMPT\\n\\n</ralph-loop-continuation>\\n\\n---\\n"}
180
- EOF
181
- exit 0
182
- fi
183
- fi
184
- fi
185
-
186
- # Priority 2: Ultrawork Mode with incomplete todos
187
- if [ -n "$ULTRAWORK_STATE" ] && [ "$TOTAL_INCOMPLETE" -gt 0 ]; then
188
- # Check if active (with jq fallback)
189
- IS_ACTIVE=""
190
- if command -v jq &> /dev/null; then
191
- IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
192
- else
193
- # Fallback: grep for "active": true
194
- if echo "$ULTRAWORK_STATE" | grep -q '"active"[[:space:]]*:[[:space:]]*true'; then
195
- IS_ACTIVE="true"
196
- fi
197
- fi
198
-
199
- if [ "$IS_ACTIVE" = "true" ]; then
200
- # Get reinforcement count (with fallback)
201
- REINFORCE_COUNT=0
202
- if command -v jq &> /dev/null; then
203
- REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | jq -r '.reinforcement_count // 0' 2>/dev/null)
204
- else
205
- REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | grep -oP '"reinforcement_count"[[:space:]]*:[[:space:]]*\K[0-9]+' 2>/dev/null) || REINFORCE_COUNT=0
206
- fi
207
- NEW_COUNT=$((REINFORCE_COUNT + 1))
208
-
209
- # Get original prompt (with fallback)
210
- ORIGINAL_PROMPT=""
211
- if command -v jq &> /dev/null; then
212
- ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
213
- else
214
- ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | grep -oP '"original_prompt"[[:space:]]*:[[:space:]]*"\K[^"]+' 2>/dev/null) || ORIGINAL_PROMPT=""
215
- fi
216
-
217
- # Update state file (best effort)
218
- if command -v jq &> /dev/null; then
219
- echo "$ULTRAWORK_STATE" | jq ".reinforcement_count = $NEW_COUNT | .last_checked_at = \"$(date -Iseconds)\"" > "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null
220
- fi
221
-
222
- cat << EOF
223
- {"continue": false, "reason": "<ultrawork-persistence>\\n\\n[ULTRAWORK MODE STILL ACTIVE - Reinforcement #$NEW_COUNT]\\n\\nYour ultrawork session is NOT complete. $TOTAL_INCOMPLETE incomplete items remain.\\n\\nREMEMBER THE ULTRAWORK RULES:\\n- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially\\n- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent)\\n- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each\\n- **VERIFY**: Check ALL requirements met before done\\n- **NO Premature Stopping**: ALL TODOs must be complete\\n\\nContinue working on the next pending item. DO NOT STOP until all items are marked complete.\\n\\nOriginal task: $ORIGINAL_PROMPT\\n\\n</ultrawork-persistence>\\n\\n---\\n"}
224
- EOF
225
- exit 0
226
- fi
227
- fi
228
-
229
- # Priority 3: Todo/Task Continuation (baseline)
230
- if [ "$TOTAL_INCOMPLETE" -gt 0 ]; then
231
- if [ "$TASK_COUNT" -gt 0 ]; then
232
- ITEM_TYPE="Tasks"
233
- else
234
- ITEM_TYPE="todos"
235
- fi
236
- cat << EOF
237
- {"continue": false, "reason": "<todo-continuation>\\n\\n[SYSTEM REMINDER - CONTINUATION]\\n\\nIncomplete $ITEM_TYPE remain ($TOTAL_INCOMPLETE remaining). Continue working on the next pending item.\\n\\n- Proceed without asking for permission\\n- Mark each item complete when finished\\n- Do not stop until all items are done\\n\\n</todo-continuation>\\n\\n---\\n"}
238
- EOF
239
- exit 0
240
- fi
241
-
242
- # No blocking needed
243
- echo '{"continue": true}'
244
- exit 0
@@ -1,90 +0,0 @@
1
- #!/bin/bash
2
- # OMC Post-Tool-Use Hook
3
- # Processes <remember> tags from Task agent output
4
- # Saves to .omc/notepad.md for compaction-resilient memory
5
-
6
- # Read stdin
7
- INPUT=$(cat)
8
-
9
- # Get directory and tool info
10
- DIRECTORY=""
11
- TOOL_NAME=""
12
- TOOL_OUTPUT=""
13
- if command -v jq &> /dev/null; then
14
- DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
15
- TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName // ""' 2>/dev/null)
16
- TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.toolOutput // ""' 2>/dev/null)
17
- else
18
- # Fallback: use grep/sed for extraction
19
- DIRECTORY=$(echo "$INPUT" | grep -oP '"directory"\s*:\s*"\K[^"]+' | head -1)
20
- TOOL_NAME=$(echo "$INPUT" | grep -oP '"toolName"\s*:\s*"\K[^"]+' | head -1)
21
- TOOL_OUTPUT=$(echo "$INPUT" | grep -oP '"toolOutput"\s*:\s*"\K[^"]+' | head -1)
22
- fi
23
-
24
- if [ -z "$DIRECTORY" ]; then
25
- DIRECTORY=$(pwd)
26
- fi
27
-
28
- # Only process Task tool output
29
- if [ "$TOOL_NAME" != "Task" ] && [ "$TOOL_NAME" != "task" ]; then
30
- echo '{"continue": true}'
31
- exit 0
32
- fi
33
-
34
- # Check for <remember> tags
35
- if ! echo "$TOOL_OUTPUT" | grep -q '<remember'; then
36
- echo '{"continue": true}'
37
- exit 0
38
- fi
39
-
40
- # Create .omc directory if needed
41
- OMC_DIR="$DIRECTORY/.omc"
42
- NOTEPAD_FILE="$OMC_DIR/notepad.md"
43
- mkdir -p "$OMC_DIR" 2>/dev/null
44
-
45
- # Initialize notepad.md if it doesn't exist
46
- if [ ! -f "$NOTEPAD_FILE" ]; then
47
- cat > "$NOTEPAD_FILE" << 'NOTEPAD_INIT'
48
- # Notepad
49
- <!-- Auto-managed by OMC. Manual edits preserved in MANUAL section. -->
50
-
51
- ## Priority Context
52
- <!-- ALWAYS loaded. Keep under 500 chars. Critical discoveries only. -->
53
-
54
- ## Working Memory
55
- <!-- Session notes. Auto-pruned after 7 days. -->
56
-
57
- ## MANUAL
58
- <!-- User content. Never auto-pruned. -->
59
- NOTEPAD_INIT
60
- fi
61
-
62
- # Process priority remember tags
63
- PRIORITY_CONTENT=$(echo "$TOOL_OUTPUT" | grep -oP '<remember\s+priority>\K[\s\S]*?(?=</remember>)' | head -1)
64
- if [ -n "$PRIORITY_CONTENT" ]; then
65
- # Read current notepad
66
- NOTEPAD_CONTENT=$(cat "$NOTEPAD_FILE")
67
- # Replace Priority Context section
68
- NEW_NOTEPAD=$(echo "$NOTEPAD_CONTENT" | sed '/## Priority Context/,/## Working Memory/{
69
- /## Priority Context/!{/## Working Memory/!d}
70
- }' | sed "/## Priority Context/a\\<!-- ALWAYS loaded. Keep under 500 chars. Critical discoveries only. -->\\n$PRIORITY_CONTENT")
71
- echo "$NEW_NOTEPAD" > "$NOTEPAD_FILE"
72
- fi
73
-
74
- # Process regular remember tags
75
- while IFS= read -r CONTENT; do
76
- if [ -n "$CONTENT" ]; then
77
- TIMESTAMP=$(date '+%Y-%m-%d %H:%M')
78
- # Append to Working Memory section (before MANUAL)
79
- sed -i "/## MANUAL/i\\### $TIMESTAMP\\n$CONTENT\\n" "$NOTEPAD_FILE" 2>/dev/null || {
80
- # macOS sed fallback
81
- sed -i '' "/## MANUAL/i\\
82
- ### $TIMESTAMP\\
83
- $CONTENT\\
84
- " "$NOTEPAD_FILE"
85
- }
86
- fi
87
- done < <(echo "$TOOL_OUTPUT" | grep -oP '<remember>\K[\s\S]*?(?=</remember>)')
88
-
89
- echo '{"continue": true}'
90
- exit 0
@@ -1,113 +0,0 @@
1
- #!/bin/bash
2
- # OMC Pre-Tool-Use Hook
3
- # Enforces delegation by warning when orchestrator attempts direct source file edits
4
-
5
- # Read stdin (JSON input from Claude Code)
6
- INPUT=$(cat)
7
-
8
- # Extract tool name and file path
9
- TOOL_NAME=""
10
- FILE_PATH=""
11
- if command -v jq &> /dev/null; then
12
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // ""' 2>/dev/null)
13
- FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // .toolInput.file_path // .toolInput.filePath // ""' 2>/dev/null)
14
- else
15
- TOOL_NAME=$(echo "$INPUT" | grep -oP '"tool_name"\s*:\s*"\K[^"]+' | head -1)
16
- if [ -z "$TOOL_NAME" ]; then
17
- TOOL_NAME=$(echo "$INPUT" | grep -oP '"toolName"\s*:\s*"\K[^"]+' | head -1)
18
- fi
19
- FILE_PATH=$(echo "$INPUT" | grep -oP '"file_path"\s*:\s*"\K[^"]+' | head -1)
20
- if [ -z "$FILE_PATH" ]; then
21
- FILE_PATH=$(echo "$INPUT" | grep -oP '"filePath"\s*:\s*"\K[^"]+' | head -1)
22
- fi
23
- fi
24
-
25
- # Handle Bash tool separately - check for file modification patterns
26
- if [ "$TOOL_NAME" = "Bash" ] || [ "$TOOL_NAME" = "bash" ]; then
27
- # Extract command
28
- COMMAND=""
29
- if command -v jq &> /dev/null; then
30
- COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // .toolInput.command // ""' 2>/dev/null)
31
- else
32
- COMMAND=$(echo "$INPUT" | grep -oP '"command"\s*:\s*"\K[^"]+' | head -1)
33
- fi
34
-
35
- # Check for file modification patterns
36
- if echo "$COMMAND" | grep -qE '(sed\s+-i|>\s*[^&]|>>\s*|tee\s+|cat\s+.*>\s*|echo\s+.*>\s*|printf\s+.*>\s*)'; then
37
- # Check if modifying source files
38
- SOURCE_PATTERN='\.(ts|tsx|js|jsx|mjs|cjs|py|pyw|go|rs|java|kt|scala|c|cpp|cc|h|hpp|rb|php|svelte|vue|graphql|gql|sh|bash|zsh)'
39
- if echo "$COMMAND" | grep -qE "$SOURCE_PATTERN"; then
40
- # Might be modifying source files - warn
41
- WARNING="[DELEGATION NOTICE] Bash command may modify source files: $COMMAND
42
-
43
- Recommended: Delegate to executor agent instead:
44
- Task(subagent_type=\"oh-my-claudecode:executor\", model=\"sonnet\", prompt=\"...\")
45
-
46
- This is a soft warning. Operation will proceed."
47
- WARNING_ESCAPED=$(echo "$WARNING" | jq -Rs . 2>/dev/null || echo "\"$WARNING\"")
48
- echo "{\"continue\": true, \"message\": $WARNING_ESCAPED}"
49
- exit 0
50
- fi
51
- fi
52
- # Bash command is OK
53
- echo '{"continue": true}'
54
- exit 0
55
- fi
56
-
57
- # Only check Edit and Write tools
58
- if [ "$TOOL_NAME" != "Edit" ] && [ "$TOOL_NAME" != "Write" ] && \
59
- [ "$TOOL_NAME" != "edit" ] && [ "$TOOL_NAME" != "write" ]; then
60
- echo '{"continue": true}'
61
- exit 0
62
- fi
63
-
64
- # No file path? Allow
65
- if [ -z "$FILE_PATH" ]; then
66
- echo '{"continue": true}'
67
- exit 0
68
- fi
69
-
70
- # Check allowed paths (always OK)
71
- if [[ "$FILE_PATH" == *".omc/"* ]] || \
72
- [[ "$FILE_PATH" == *".claude/"* ]] || \
73
- [[ "$FILE_PATH" == *"/.claude/"* ]] || \
74
- [[ "$FILE_PATH" == "CLAUDE.md" ]] || \
75
- [[ "$FILE_PATH" == *"/CLAUDE.md" ]] || \
76
- [[ "$FILE_PATH" == "AGENTS.md" ]] || \
77
- [[ "$FILE_PATH" == *"/AGENTS.md" ]]; then
78
- echo '{"continue": true}'
79
- exit 0
80
- fi
81
-
82
- # Check if source file extension (should warn)
83
- EXT="${FILE_PATH##*.}"
84
- EXT_LOWER=$(echo "$EXT" | tr '[:upper:]' '[:lower:]')
85
-
86
- SOURCE_EXTS="ts tsx js jsx mjs cjs py pyw go rs java kt scala c cpp cc h hpp rb php svelte vue graphql gql sh bash zsh"
87
-
88
- IS_SOURCE=false
89
- for src_ext in $SOURCE_EXTS; do
90
- if [ "$EXT_LOWER" = "$src_ext" ]; then
91
- IS_SOURCE=true
92
- break
93
- fi
94
- done
95
-
96
- if [ "$IS_SOURCE" = true ]; then
97
- # Emit warning but allow (soft enforcement)
98
- WARNING="[DELEGATION NOTICE] Direct $TOOL_NAME on source file: $FILE_PATH
99
-
100
- Recommended: Delegate to executor agent instead:
101
- Task(subagent_type=\"oh-my-claudecode:executor\", model=\"sonnet\", prompt=\"...\")
102
-
103
- This is a soft warning. Operation will proceed."
104
-
105
- # Escape for JSON
106
- WARNING_ESCAPED=$(echo "$WARNING" | jq -Rs .)
107
- echo "{\"continue\": true, \"message\": $WARNING_ESCAPED}"
108
- exit 0
109
- fi
110
-
111
- # Not a source file, allow without warning
112
- echo '{"continue": true}'
113
- exit 0
@@ -1,62 +0,0 @@
1
- #!/bin/bash
2
- # OMC Session Start Hook
3
- # Restores persistent mode states and injects context when session starts
4
-
5
- # Read stdin
6
- INPUT=$(cat)
7
-
8
- # Get directory
9
- DIRECTORY=""
10
- if command -v jq &> /dev/null; then
11
- DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
12
- fi
13
-
14
- if [ -z "$DIRECTORY" ]; then
15
- DIRECTORY=$(pwd)
16
- fi
17
-
18
- MESSAGES=""
19
-
20
- # Check for active ultrawork state
21
- if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ] || [ -f "$HOME/.omc/state/ultrawork-state.json" ]; then
22
- if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ]; then
23
- ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null)
24
- else
25
- ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
26
- fi
27
-
28
- IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
29
- if [ "$IS_ACTIVE" = "true" ]; then
30
- STARTED_AT=$(echo "$ULTRAWORK_STATE" | jq -r '.started_at // ""' 2>/dev/null)
31
- PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
32
- MESSAGES="$MESSAGES<session-restore>\\n\\n[ULTRAWORK MODE RESTORED]\\n\\nYou have an active ultrawork session from $STARTED_AT.\\nOriginal task: $PROMPT\\n\\nContinue working in ultrawork mode until all tasks are complete.\\n\\n</session-restore>\\n\\n---\\n\\n"
33
- fi
34
- fi
35
-
36
- # Check for incomplete todos
37
- INCOMPLETE_COUNT=0
38
- TODOS_DIR="$HOME/.claude/todos"
39
- if [ -d "$TODOS_DIR" ]; then
40
- for todo_file in "$TODOS_DIR"/*.json; do
41
- if [ -f "$todo_file" ]; then
42
- if command -v jq &> /dev/null; then
43
- COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
44
- INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
45
- fi
46
- fi
47
- done
48
- fi
49
-
50
- if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
51
- MESSAGES="$MESSAGES<session-restore>\\n\\n[PENDING TASKS DETECTED]\\n\\nYou have $INCOMPLETE_COUNT incomplete tasks from a previous session.\\nPlease continue working on these tasks.\\n\\n</session-restore>\\n\\n---\\n\\n"
52
- fi
53
-
54
- # Output message if we have any
55
- if [ -n "$MESSAGES" ]; then
56
- # Escape for JSON
57
- MESSAGES_ESCAPED=$(echo "$MESSAGES" | sed 's/"/\\"/g')
58
- echo "{\"continue\": true, \"message\": \"$MESSAGES_ESCAPED\"}"
59
- else
60
- echo '{"continue": true}'
61
- fi
62
- exit 0
@@ -1,93 +0,0 @@
1
- #!/bin/bash
2
- # OMC Stop Continuation Hook
3
- # Checks for incomplete todos and injects continuation prompt
4
- # Ported from oh-my-opencode's todo-continuation-enforcer
5
-
6
- # Validate session ID to prevent path traversal attacks
7
- is_valid_session_id() {
8
- local id="$1"
9
- if [ -z "$id" ]; then
10
- return 1
11
- fi
12
- if echo "$id" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$'; then
13
- return 0
14
- fi
15
- return 1
16
- }
17
-
18
- # Read stdin
19
- INPUT=$(cat)
20
-
21
- # Get session ID if available
22
- SESSION_ID=""
23
- if command -v jq &> /dev/null; then
24
- SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
25
- fi
26
-
27
- # Check for incomplete tasks in new Task system
28
- TASKS_DIR="$HOME/.claude/tasks"
29
- TASK_COUNT=0
30
- JQ_AVAILABLE=false
31
- if command -v jq &> /dev/null; then
32
- JQ_AVAILABLE=true
33
- fi
34
-
35
- if [ -n "$SESSION_ID" ] && is_valid_session_id "$SESSION_ID" && [ -d "$TASKS_DIR/$SESSION_ID" ]; then
36
- for task_file in "$TASKS_DIR/$SESSION_ID"/*.json; do
37
- if [ -f "$task_file" ] && [ "$(basename "$task_file")" != ".lock" ]; then
38
- if [ "$JQ_AVAILABLE" = "true" ]; then
39
- STATUS=$(jq -r '.status // "pending"' "$task_file" 2>/dev/null)
40
- # Match TypeScript isTaskIncomplete(): only pending/in_progress are incomplete
41
- # 'deleted' and 'completed' are both treated as done
42
- if [ "$STATUS" = "pending" ] || [ "$STATUS" = "in_progress" ]; then
43
- TASK_COUNT=$((TASK_COUNT + 1))
44
- fi
45
- else
46
- # Fallback: grep for incomplete status values (pending or in_progress)
47
- if grep -qE '"status"[[:space:]]*:[[:space:]]*"(pending|in_progress)"' "$task_file" 2>/dev/null; then
48
- TASK_COUNT=$((TASK_COUNT + 1))
49
- fi
50
- fi
51
- fi
52
- done
53
-
54
- if [ "$JQ_AVAILABLE" = "false" ] && [ "$TASK_COUNT" -gt 0 ]; then
55
- echo "[OMC WARNING] jq not installed - Task counting may be less accurate." >&2
56
- fi
57
- fi
58
-
59
- # Check for incomplete todos in the Claude todos directory
60
- TODOS_DIR="$HOME/.claude/todos"
61
- if [ -d "$TODOS_DIR" ]; then
62
- # Look for any todo files with incomplete items
63
- INCOMPLETE_COUNT=0
64
- for todo_file in "$TODOS_DIR"/*.json; do
65
- if [ -f "$todo_file" ]; then
66
- if command -v jq &> /dev/null; then
67
- COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
68
- INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
69
- fi
70
- fi
71
- done
72
-
73
- # Combine task and todo counts
74
- TOTAL_INCOMPLETE=$((TASK_COUNT + INCOMPLETE_COUNT))
75
-
76
- if [ "$TOTAL_INCOMPLETE" -gt 0 ]; then
77
- # Use Task terminology if we have tasks, otherwise todos
78
- if [ "$TASK_COUNT" -gt 0 ]; then
79
- cat << EOF
80
- {"continue": false, "reason": "[SYSTEM REMINDER - TASK CONTINUATION]\\n\\nIncomplete Tasks remain ($TOTAL_INCOMPLETE remaining). Continue working on the next pending Task.\\n\\n- Proceed without asking for permission\\n- Mark each Task complete when finished\\n- Do not stop until all Tasks are done"}
81
- EOF
82
- else
83
- cat << EOF
84
- {"continue": false, "reason": "[SYSTEM REMINDER - TODO CONTINUATION]\\n\\nIncomplete tasks remain in your todo list ($TOTAL_INCOMPLETE remaining). Continue working on the next pending task.\\n\\n- Proceed without asking for permission\\n- Mark each task complete when finished\\n- Do not stop until all tasks are done"}
85
- EOF
86
- fi
87
- exit 0
88
- fi
89
- fi
90
-
91
- # No incomplete todos - allow stop
92
- echo '{"continue": true}'
93
- exit 0