oh-my-claude-sisyphus 3.6.3 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +16 -0
  2. package/dist/__tests__/delegation-enforcement-levels.test.d.ts +9 -0
  3. package/dist/__tests__/delegation-enforcement-levels.test.d.ts.map +1 -0
  4. package/dist/__tests__/delegation-enforcement-levels.test.js +550 -0
  5. package/dist/__tests__/delegation-enforcement-levels.test.js.map +1 -0
  6. package/dist/__tests__/installer.test.js +1 -1
  7. package/dist/__tests__/rate-limit-wait/daemon.test.d.ts +5 -0
  8. package/dist/__tests__/rate-limit-wait/daemon.test.d.ts.map +1 -0
  9. package/dist/__tests__/rate-limit-wait/daemon.test.js +313 -0
  10. package/dist/__tests__/rate-limit-wait/daemon.test.js.map +1 -0
  11. package/dist/__tests__/rate-limit-wait/integration.test.d.ts +8 -0
  12. package/dist/__tests__/rate-limit-wait/integration.test.d.ts.map +1 -0
  13. package/dist/__tests__/rate-limit-wait/integration.test.js +329 -0
  14. package/dist/__tests__/rate-limit-wait/integration.test.js.map +1 -0
  15. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts +5 -0
  16. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts.map +1 -0
  17. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js +167 -0
  18. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js.map +1 -0
  19. package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts +5 -0
  20. package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts.map +1 -0
  21. package/dist/__tests__/rate-limit-wait/tmux-detector.test.js +295 -0
  22. package/dist/__tests__/rate-limit-wait/tmux-detector.test.js.map +1 -0
  23. package/dist/cli/commands/wait.d.ts +52 -0
  24. package/dist/cli/commands/wait.d.ts.map +1 -0
  25. package/dist/cli/commands/wait.js +229 -0
  26. package/dist/cli/commands/wait.js.map +1 -0
  27. package/dist/cli/index.js +54 -0
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/features/rate-limit-wait/daemon.d.ts +52 -0
  30. package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -0
  31. package/dist/features/rate-limit-wait/daemon.js +545 -0
  32. package/dist/features/rate-limit-wait/daemon.js.map +1 -0
  33. package/dist/features/rate-limit-wait/index.d.ts +16 -0
  34. package/dist/features/rate-limit-wait/index.d.ts.map +1 -0
  35. package/dist/features/rate-limit-wait/index.js +18 -0
  36. package/dist/features/rate-limit-wait/index.js.map +1 -0
  37. package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts +22 -0
  38. package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts.map +1 -0
  39. package/dist/features/rate-limit-wait/rate-limit-monitor.js +99 -0
  40. package/dist/features/rate-limit-wait/rate-limit-monitor.js.map +1 -0
  41. package/dist/features/rate-limit-wait/tmux-detector.d.ts +59 -0
  42. package/dist/features/rate-limit-wait/tmux-detector.d.ts.map +1 -0
  43. package/dist/features/rate-limit-wait/tmux-detector.js +304 -0
  44. package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -0
  45. package/dist/features/rate-limit-wait/types.d.ts +121 -0
  46. package/dist/features/rate-limit-wait/types.d.ts.map +1 -0
  47. package/dist/features/rate-limit-wait/types.js +8 -0
  48. package/dist/features/rate-limit-wait/types.js.map +1 -0
  49. package/dist/hooks/bridge.d.ts +1 -1
  50. package/dist/hooks/bridge.d.ts.map +1 -1
  51. package/dist/hooks/bridge.js +50 -4
  52. package/dist/hooks/bridge.js.map +1 -1
  53. package/dist/hooks/index.d.ts +5 -0
  54. package/dist/hooks/index.d.ts.map +1 -1
  55. package/dist/hooks/index.js +15 -0
  56. package/dist/hooks/index.js.map +1 -1
  57. package/dist/hooks/omc-orchestrator/audit.d.ts +2 -1
  58. package/dist/hooks/omc-orchestrator/audit.d.ts.map +1 -1
  59. package/dist/hooks/omc-orchestrator/audit.js.map +1 -1
  60. package/dist/hooks/omc-orchestrator/index.d.ts +7 -0
  61. package/dist/hooks/omc-orchestrator/index.d.ts.map +1 -1
  62. package/dist/hooks/omc-orchestrator/index.js +95 -8
  63. package/dist/hooks/omc-orchestrator/index.js.map +1 -1
  64. package/dist/hooks/permission-handler/__tests__/index.test.d.ts +2 -0
  65. package/dist/hooks/permission-handler/__tests__/index.test.d.ts.map +1 -0
  66. package/dist/hooks/permission-handler/__tests__/index.test.js +244 -0
  67. package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -0
  68. package/dist/hooks/permission-handler/index.d.ts +42 -0
  69. package/dist/hooks/permission-handler/index.d.ts.map +1 -0
  70. package/dist/hooks/permission-handler/index.js +111 -0
  71. package/dist/hooks/permission-handler/index.js.map +1 -0
  72. package/dist/hooks/pre-compact/index.d.ts +82 -0
  73. package/dist/hooks/pre-compact/index.d.ts.map +1 -0
  74. package/dist/hooks/pre-compact/index.js +265 -0
  75. package/dist/hooks/pre-compact/index.js.map +1 -0
  76. package/dist/hooks/session-end/index.d.ts +50 -0
  77. package/dist/hooks/session-end/index.d.ts.map +1 -0
  78. package/dist/hooks/session-end/index.js +207 -0
  79. package/dist/hooks/session-end/index.js.map +1 -0
  80. package/dist/hooks/setup/index.d.ts +66 -0
  81. package/dist/hooks/setup/index.d.ts.map +1 -0
  82. package/dist/hooks/setup/index.js +299 -0
  83. package/dist/hooks/setup/index.js.map +1 -0
  84. package/dist/hooks/setup/types.d.ts +25 -0
  85. package/dist/hooks/setup/types.d.ts.map +1 -0
  86. package/dist/hooks/setup/types.js +5 -0
  87. package/dist/hooks/setup/types.js.map +1 -0
  88. package/dist/hooks/subagent-tracker/index.d.ts +68 -29
  89. package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
  90. package/dist/hooks/subagent-tracker/index.js +301 -131
  91. package/dist/hooks/subagent-tracker/index.js.map +1 -1
  92. package/dist/installer/index.d.ts +1 -1
  93. package/dist/installer/index.js +1 -1
  94. package/hooks/hooks.json +83 -1
  95. package/package.json +3 -1
  96. package/scripts/permission-handler.mjs +23 -0
  97. package/scripts/pre-compact.mjs +23 -0
  98. package/scripts/session-end.mjs +23 -0
  99. package/scripts/setup-init.mjs +23 -0
  100. package/scripts/setup-maintenance.mjs +23 -0
  101. package/scripts/subagent-tracker.mjs +35 -0
  102. package/templates/hooks/keyword-detector.mjs +198 -0
  103. package/templates/hooks/keyword-detector.sh +102 -0
  104. package/templates/hooks/persistent-mode.mjs +249 -0
  105. package/templates/hooks/persistent-mode.sh +187 -0
  106. package/templates/hooks/post-tool-use.mjs +133 -0
  107. package/templates/hooks/post-tool-use.sh +90 -0
  108. package/templates/hooks/pre-tool-use.mjs +145 -0
  109. package/templates/hooks/pre-tool-use.sh +113 -0
  110. package/templates/hooks/session-start.mjs +100 -0
  111. package/templates/hooks/session-start.sh +62 -0
  112. package/templates/hooks/stop-continuation.mjs +80 -0
  113. package/templates/hooks/stop-continuation.sh +40 -0
  114. package/templates/rules/README.md +40 -0
  115. package/templates/rules/coding-style.md +74 -0
  116. package/templates/rules/git-workflow.md +41 -0
  117. package/templates/rules/performance.md +40 -0
  118. package/templates/rules/security.md +41 -0
  119. package/templates/rules/testing.md +42 -0
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ // OMC Persistent Mode Hook (Node.js)
3
+ // Unified handler for ultrawork, ralph-loop, and todo continuation
4
+ // Cross-platform: Windows, macOS, Linux
5
+
6
+ import { existsSync, readFileSync, writeFileSync, readdirSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { homedir } from 'os';
9
+
10
+ async function readStdin() {
11
+ const chunks = [];
12
+ for await (const chunk of process.stdin) {
13
+ chunks.push(chunk);
14
+ }
15
+ return Buffer.concat(chunks).toString('utf-8');
16
+ }
17
+
18
+ function readJsonFile(path) {
19
+ try {
20
+ if (!existsSync(path)) return null;
21
+ return JSON.parse(readFileSync(path, 'utf-8'));
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ function writeJsonFile(path, data) {
28
+ try {
29
+ writeFileSync(path, JSON.stringify(data, null, 2));
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ function countIncompleteTodos(todosDir, projectDir) {
37
+ let count = 0;
38
+
39
+ // Check global todos
40
+ if (existsSync(todosDir)) {
41
+ try {
42
+ const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
43
+ for (const file of files) {
44
+ const todos = readJsonFile(join(todosDir, file));
45
+ if (Array.isArray(todos)) {
46
+ count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
47
+ }
48
+ }
49
+ } catch {}
50
+ }
51
+
52
+ // Check project todos
53
+ for (const path of [
54
+ join(projectDir, '.omc', 'todos.json'),
55
+ join(projectDir, '.claude', 'todos.json')
56
+ ]) {
57
+ const todos = readJsonFile(path);
58
+ if (Array.isArray(todos)) {
59
+ count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
60
+ }
61
+ }
62
+
63
+ return count;
64
+ }
65
+
66
+ async function main() {
67
+ try {
68
+ const input = await readStdin();
69
+ let data = {};
70
+ try { data = JSON.parse(input); } catch {}
71
+
72
+ const stopReason = data.stop_reason || data.stopReason || '';
73
+ const userRequested = data.user_requested || data.userRequested || false;
74
+
75
+ // Check for user abort - skip all continuation enforcement
76
+ // NOTE: Abort patterns are assumed - verify against actual Claude Code API values
77
+ if (userRequested || /abort|cancel|interrupt|ctrl_c|manual_stop/i.test(stopReason)) {
78
+ console.log(JSON.stringify({ continue: true }));
79
+ return;
80
+ }
81
+
82
+ const directory = data.directory || process.cwd();
83
+ const todosDir = join(homedir(), '.claude', 'todos');
84
+
85
+ // Check for ultrawork state
86
+ let ultraworkState = readJsonFile(join(directory, '.omc', 'ultrawork-state.json'))
87
+ || readJsonFile(join(homedir(), '.claude', 'ultrawork-state.json'));
88
+
89
+ // Check for ralph loop state
90
+ const ralphState = readJsonFile(join(directory, '.omc', 'ralph-state.json'));
91
+
92
+ // Check for verification state (oracle verification)
93
+ const verificationState = readJsonFile(join(directory, '.omc', 'ralph-verification.json'));
94
+
95
+ // Count incomplete todos
96
+ const incompleteCount = countIncompleteTodos(todosDir, directory);
97
+
98
+ // Priority 1: Ralph Loop with Oracle Verification
99
+ if (ralphState?.active) {
100
+ const iteration = ralphState.iteration || 1;
101
+ const maxIter = ralphState.max_iterations || 10;
102
+
103
+ // Check if oracle verification is pending
104
+ if (verificationState?.pending) {
105
+ const attempt = (verificationState.verification_attempts || 0) + 1;
106
+ const maxAttempts = verificationState.max_verification_attempts || 3;
107
+
108
+ console.log(JSON.stringify({
109
+ continue: false,
110
+ reason: `<ralph-verification>
111
+
112
+ [ORACLE VERIFICATION REQUIRED - Attempt ${attempt}/${maxAttempts}]
113
+
114
+ The agent claims the task is complete. Before accepting, YOU MUST verify with Oracle.
115
+
116
+ **Original Task:**
117
+ ${verificationState.original_task || ralphState.prompt || 'No task specified'}
118
+
119
+ **Completion Claim:**
120
+ ${verificationState.completion_claim || 'Task marked complete'}
121
+
122
+ ${verificationState.oracle_feedback ? `**Previous Oracle Feedback (rejected):**
123
+ ${verificationState.oracle_feedback}
124
+ ` : ''}
125
+
126
+ ## MANDATORY VERIFICATION STEPS
127
+
128
+ 1. **Spawn Oracle Agent** for verification:
129
+ \`\`\`
130
+ Task(subagent_type="oracle", prompt="Verify this task completion claim...")
131
+ \`\`\`
132
+
133
+ 2. **Oracle must check:**
134
+ - Are ALL requirements from the original task met?
135
+ - Is the implementation complete, not partial?
136
+ - Are there any obvious bugs or issues?
137
+ - Does the code compile/run without errors?
138
+ - Are tests passing (if applicable)?
139
+
140
+ 3. **Based on Oracle's response:**
141
+ - If APPROVED: Output \`<oracle-approved>VERIFIED_COMPLETE</oracle-approved>\`
142
+ - If REJECTED: Continue working on the identified issues
143
+
144
+ DO NOT output the completion promise again until Oracle approves.
145
+
146
+ </ralph-verification>
147
+
148
+ ---
149
+ `
150
+ }));
151
+ return;
152
+ }
153
+
154
+ if (iteration < maxIter) {
155
+ const newIter = iteration + 1;
156
+ ralphState.iteration = newIter;
157
+ writeJsonFile(join(directory, '.omc', 'ralph-state.json'), ralphState);
158
+
159
+ console.log(JSON.stringify({
160
+ continue: false,
161
+ reason: `<ralph-loop-continuation>
162
+
163
+ [RALPH LOOP - ITERATION ${newIter}/${maxIter}]
164
+
165
+ Your previous attempt did not output the completion promise. The work is NOT done yet.
166
+
167
+ CRITICAL INSTRUCTIONS:
168
+ 1. Review your progress and the original task
169
+ 2. Check your todo list - are ALL items marked complete?
170
+ 3. Continue from where you left off
171
+ 4. When FULLY complete, output: <promise>${ralphState.completion_promise || 'TASK_COMPLETE'}</promise>
172
+ 5. Do NOT stop until the task is truly done
173
+
174
+ ${ralphState.prompt ? `Original task: ${ralphState.prompt}` : ''}
175
+
176
+ </ralph-loop-continuation>
177
+
178
+ ---
179
+ `
180
+ }));
181
+ return;
182
+ }
183
+ }
184
+
185
+ // Priority 2: Ultrawork with incomplete todos
186
+ if (ultraworkState?.active && incompleteCount > 0) {
187
+ const newCount = (ultraworkState.reinforcement_count || 0) + 1;
188
+ ultraworkState.reinforcement_count = newCount;
189
+ ultraworkState.last_checked_at = new Date().toISOString();
190
+
191
+ writeJsonFile(join(directory, '.omc', 'ultrawork-state.json'), ultraworkState);
192
+
193
+ console.log(JSON.stringify({
194
+ continue: false,
195
+ reason: `<ultrawork-persistence>
196
+
197
+ [ULTRAWORK MODE STILL ACTIVE - Reinforcement #${newCount}]
198
+
199
+ Your ultrawork session is NOT complete. ${incompleteCount} incomplete todos remain.
200
+
201
+ REMEMBER THE ULTRAWORK RULES:
202
+ - **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially
203
+ - **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent)
204
+ - **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each
205
+ - **VERIFY**: Check ALL requirements met before done
206
+ - **NO Premature Stopping**: ALL TODOs must be complete
207
+
208
+ Continue working on the next pending task. DO NOT STOP until all tasks are marked complete.
209
+
210
+ ${ultraworkState.original_prompt ? `Original task: ${ultraworkState.original_prompt}` : ''}
211
+
212
+ </ultrawork-persistence>
213
+
214
+ ---
215
+ `
216
+ }));
217
+ return;
218
+ }
219
+
220
+ // Priority 3: Todo Continuation
221
+ if (incompleteCount > 0) {
222
+ console.log(JSON.stringify({
223
+ continue: false,
224
+ reason: `<todo-continuation>
225
+
226
+ [SYSTEM REMINDER - TODO CONTINUATION]
227
+
228
+ Incomplete tasks remain in your todo list (${incompleteCount} remaining). Continue working on the next pending task.
229
+
230
+ - Proceed without asking for permission
231
+ - Mark each task complete when finished
232
+ - Do not stop until all tasks are done
233
+
234
+ </todo-continuation>
235
+
236
+ ---
237
+ `
238
+ }));
239
+ return;
240
+ }
241
+
242
+ // No blocking needed
243
+ console.log(JSON.stringify({ continue: true }));
244
+ } catch (error) {
245
+ console.log(JSON.stringify({ continue: true }));
246
+ }
247
+ }
248
+
249
+ main();
@@ -0,0 +1,187 @@
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
+ # Read stdin
7
+ INPUT=$(cat)
8
+
9
+ # Get session ID and directory
10
+ SESSION_ID=""
11
+ DIRECTORY=""
12
+ if command -v jq &> /dev/null; then
13
+ SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
14
+ DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
15
+ fi
16
+
17
+ # Default to current directory
18
+ if [ -z "$DIRECTORY" ]; then
19
+ DIRECTORY=$(pwd)
20
+ fi
21
+
22
+ # Extract stop reason for abort detection
23
+ STOP_REASON=""
24
+ USER_REQUESTED=""
25
+ if command -v jq &> /dev/null; then
26
+ STOP_REASON=$(echo "$INPUT" | jq -r '.stop_reason // .stopReason // ""' 2>/dev/null)
27
+ USER_REQUESTED=$(echo "$INPUT" | jq -r '.user_requested // .userRequested // "false"' 2>/dev/null)
28
+ fi
29
+
30
+ # Check for user abort before continuation checks
31
+ # NOTE: Abort patterns are assumed - verify against actual Claude Code API values
32
+ if [ "$USER_REQUESTED" = "true" ] || echo "$STOP_REASON" | grep -qiE "(abort|cancel|interrupt|ctrl_c|manual_stop)"; then
33
+ echo '{"continue": true}'
34
+ exit 0
35
+ fi
36
+
37
+ # Check for active ultrawork state
38
+ ULTRAWORK_STATE=""
39
+ if [ -f "$DIRECTORY/.omc/ultrawork-state.json" ]; then
40
+ ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/ultrawork-state.json" 2>/dev/null)
41
+ elif [ -f "$HOME/.claude/ultrawork-state.json" ]; then
42
+ ULTRAWORK_STATE=$(cat "$HOME/.claude/ultrawork-state.json" 2>/dev/null)
43
+ fi
44
+
45
+ # Check for active ralph loop
46
+ RALPH_STATE=""
47
+ if [ -f "$DIRECTORY/.omc/ralph-state.json" ]; then
48
+ RALPH_STATE=$(cat "$DIRECTORY/.omc/ralph-state.json" 2>/dev/null)
49
+ fi
50
+
51
+ # Check for verification state (oracle verification)
52
+ VERIFICATION_STATE=""
53
+ if [ -f "$DIRECTORY/.omc/ralph-verification.json" ]; then
54
+ VERIFICATION_STATE=$(cat "$DIRECTORY/.omc/ralph-verification.json" 2>/dev/null)
55
+ fi
56
+
57
+ # Check for incomplete todos
58
+ INCOMPLETE_COUNT=0
59
+ TODOS_DIR="$HOME/.claude/todos"
60
+ if [ -d "$TODOS_DIR" ]; then
61
+ for todo_file in "$TODOS_DIR"/*.json; do
62
+ if [ -f "$todo_file" ]; then
63
+ if command -v jq &> /dev/null; then
64
+ COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
65
+ INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
66
+ else
67
+ # Fallback: count "pending" or "in_progress" occurrences
68
+ COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\|in_progress"' "$todo_file" 2>/dev/null) || COUNT=0
69
+ INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
70
+ fi
71
+ fi
72
+ done
73
+ fi
74
+
75
+ # Check project todos as well
76
+ for todo_path in "$DIRECTORY/.omc/todos.json" "$DIRECTORY/.claude/todos.json"; do
77
+ if [ -f "$todo_path" ]; then
78
+ if command -v jq &> /dev/null; then
79
+ COUNT=$(jq 'if type == "array" then [.[] | select(.status != "completed" and .status != "cancelled")] | length else 0 end' "$todo_path" 2>/dev/null || echo "0")
80
+ INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
81
+ else
82
+ # Fallback: count "pending" or "in_progress" occurrences
83
+ COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\|in_progress"' "$todo_path" 2>/dev/null) || COUNT=0
84
+ INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
85
+ fi
86
+ fi
87
+ done
88
+
89
+ # Priority 1: Ralph Loop with Oracle Verification
90
+ if [ -n "$RALPH_STATE" ]; then
91
+ IS_ACTIVE=$(echo "$RALPH_STATE" | jq -r '.active // false' 2>/dev/null)
92
+ if [ "$IS_ACTIVE" = "true" ]; then
93
+ ITERATION=$(echo "$RALPH_STATE" | jq -r '.iteration // 1' 2>/dev/null)
94
+ MAX_ITER=$(echo "$RALPH_STATE" | jq -r '.max_iterations // 10' 2>/dev/null)
95
+ PROMISE=$(echo "$RALPH_STATE" | jq -r '.completion_promise // "TASK_COMPLETE"' 2>/dev/null)
96
+ PROMPT=$(echo "$RALPH_STATE" | jq -r '.prompt // ""' 2>/dev/null)
97
+
98
+ # Check if oracle verification is pending
99
+ if [ -n "$VERIFICATION_STATE" ]; then
100
+ IS_PENDING=$(echo "$VERIFICATION_STATE" | jq -r '.pending // false' 2>/dev/null)
101
+ if [ "$IS_PENDING" = "true" ]; then
102
+ ATTEMPT=$(echo "$VERIFICATION_STATE" | jq -r '.verification_attempts // 0' 2>/dev/null)
103
+ MAX_ATTEMPTS=$(echo "$VERIFICATION_STATE" | jq -r '.max_verification_attempts // 3' 2>/dev/null)
104
+ ORIGINAL_TASK=$(echo "$VERIFICATION_STATE" | jq -r '.original_task // ""' 2>/dev/null)
105
+ COMPLETION_CLAIM=$(echo "$VERIFICATION_STATE" | jq -r '.completion_claim // ""' 2>/dev/null)
106
+ ORACLE_FEEDBACK=$(echo "$VERIFICATION_STATE" | jq -r '.oracle_feedback // ""' 2>/dev/null)
107
+ NEXT_ATTEMPT=$((ATTEMPT + 1))
108
+
109
+ FEEDBACK_SECTION=""
110
+ if [ -n "$ORACLE_FEEDBACK" ] && [ "$ORACLE_FEEDBACK" != "null" ]; then
111
+ FEEDBACK_SECTION="\\n**Previous Oracle Feedback (rejected):**\\n$ORACLE_FEEDBACK\\n"
112
+ fi
113
+
114
+ cat << EOF
115
+ {"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"}
116
+ EOF
117
+ exit 0
118
+ fi
119
+ fi
120
+
121
+ if [ "$ITERATION" -lt "$MAX_ITER" ]; then
122
+ # Increment iteration
123
+ NEW_ITER=$((ITERATION + 1))
124
+ echo "$RALPH_STATE" | jq ".iteration = $NEW_ITER" > "$DIRECTORY/.omc/ralph-state.json" 2>/dev/null
125
+
126
+ cat << EOF
127
+ {"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"}
128
+ EOF
129
+ exit 0
130
+ fi
131
+ fi
132
+ fi
133
+
134
+ # Priority 2: Ultrawork Mode with incomplete todos
135
+ if [ -n "$ULTRAWORK_STATE" ] && [ "$INCOMPLETE_COUNT" -gt 0 ]; then
136
+ # Check if active (with jq fallback)
137
+ IS_ACTIVE=""
138
+ if command -v jq &> /dev/null; then
139
+ IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
140
+ else
141
+ # Fallback: grep for "active": true
142
+ if echo "$ULTRAWORK_STATE" | grep -q '"active"[[:space:]]*:[[:space:]]*true'; then
143
+ IS_ACTIVE="true"
144
+ fi
145
+ fi
146
+
147
+ if [ "$IS_ACTIVE" = "true" ]; then
148
+ # Get reinforcement count (with fallback)
149
+ REINFORCE_COUNT=0
150
+ if command -v jq &> /dev/null; then
151
+ REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | jq -r '.reinforcement_count // 0' 2>/dev/null)
152
+ else
153
+ REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | grep -oP '"reinforcement_count"[[:space:]]*:[[:space:]]*\K[0-9]+' 2>/dev/null) || REINFORCE_COUNT=0
154
+ fi
155
+ NEW_COUNT=$((REINFORCE_COUNT + 1))
156
+
157
+ # Get original prompt (with fallback)
158
+ ORIGINAL_PROMPT=""
159
+ if command -v jq &> /dev/null; then
160
+ ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
161
+ else
162
+ ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | grep -oP '"original_prompt"[[:space:]]*:[[:space:]]*"\K[^"]+' 2>/dev/null) || ORIGINAL_PROMPT=""
163
+ fi
164
+
165
+ # Update state file (best effort)
166
+ if command -v jq &> /dev/null; then
167
+ echo "$ULTRAWORK_STATE" | jq ".reinforcement_count = $NEW_COUNT | .last_checked_at = \"$(date -Iseconds)\"" > "$DIRECTORY/.omc/ultrawork-state.json" 2>/dev/null
168
+ fi
169
+
170
+ cat << EOF
171
+ {"continue": false, "reason": "<ultrawork-persistence>\\n\\n[ULTRAWORK MODE STILL ACTIVE - Reinforcement #$NEW_COUNT]\\n\\nYour ultrawork session is NOT complete. $INCOMPLETE_COUNT incomplete todos 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 task. DO NOT STOP until all tasks are marked complete.\\n\\nOriginal task: $ORIGINAL_PROMPT\\n\\n</ultrawork-persistence>\\n\\n---\\n"}
172
+ EOF
173
+ exit 0
174
+ fi
175
+ fi
176
+
177
+ # Priority 3: Todo Continuation (baseline)
178
+ if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
179
+ cat << EOF
180
+ {"continue": false, "reason": "<todo-continuation>\\n\\n[SYSTEM REMINDER - TODO CONTINUATION]\\n\\nIncomplete tasks remain in your todo list ($INCOMPLETE_COUNT 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\\n\\n</todo-continuation>\\n\\n---\\n"}
181
+ EOF
182
+ exit 0
183
+ fi
184
+
185
+ # No blocking needed
186
+ echo '{"continue": true}'
187
+ exit 0
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ // OMC Post-Tool-Use Hook (Node.js)
3
+ // Processes <remember> tags from Task agent output
4
+ // Saves to .omc/notepad.md for compaction-resilient memory
5
+
6
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
7
+ import { join } from 'path';
8
+
9
+ // Constants
10
+ const NOTEPAD_TEMPLATE = '# Notepad\n' +
11
+ '<!-- Auto-managed by OMC. Manual edits preserved in MANUAL section. -->\n\n' +
12
+ '## Priority Context\n' +
13
+ '<!-- ALWAYS loaded. Keep under 500 chars. Critical discoveries only. -->\n\n' +
14
+ '## Working Memory\n' +
15
+ '<!-- Session notes. Auto-pruned after 7 days. -->\n\n' +
16
+ '## MANUAL\n' +
17
+ '<!-- User content. Never auto-pruned. -->\n';
18
+
19
+ // Read all stdin
20
+ async function readStdin() {
21
+ const chunks = [];
22
+ for await (const chunk of process.stdin) {
23
+ chunks.push(chunk);
24
+ }
25
+ return Buffer.concat(chunks).toString('utf-8');
26
+ }
27
+
28
+ // Initialize notepad.md if needed
29
+ function initNotepad(directory) {
30
+ const sisyphusDir = join(directory, '.omc');
31
+ const notepadPath = join(sisyphusDir, 'notepad.md');
32
+
33
+ if (!existsSync(sisyphusDir)) {
34
+ try { mkdirSync(sisyphusDir, { recursive: true }); } catch {}
35
+ }
36
+
37
+ if (!existsSync(notepadPath)) {
38
+ try { writeFileSync(notepadPath, NOTEPAD_TEMPLATE); } catch {}
39
+ }
40
+
41
+ return notepadPath;
42
+ }
43
+
44
+ // Set priority context
45
+ function setPriorityContext(notepadPath, content) {
46
+ try {
47
+ let notepad = readFileSync(notepadPath, 'utf-8');
48
+
49
+ // Find and replace Priority Context section
50
+ const priorityMatch = notepad.match(/## Priority Context[\s\S]*?(?=## Working Memory)/);
51
+ if (priorityMatch) {
52
+ const newPriority = '## Priority Context\n' +
53
+ '<!-- ALWAYS loaded. Keep under 500 chars. Critical discoveries only. -->\n' +
54
+ content.trim() + '\n\n';
55
+ notepad = notepad.replace(priorityMatch[0], newPriority);
56
+ writeFileSync(notepadPath, notepad);
57
+ }
58
+ } catch {}
59
+ }
60
+
61
+ // Add working memory entry
62
+ function addWorkingMemoryEntry(notepadPath, content) {
63
+ try {
64
+ let notepad = readFileSync(notepadPath, 'utf-8');
65
+
66
+ const timestamp = new Date().toISOString().slice(0, 16).replace('T', ' ');
67
+ const entry = '### ' + timestamp + '\n' + content.trim() + '\n\n';
68
+
69
+ // Insert before MANUAL section
70
+ const manualIndex = notepad.indexOf('## MANUAL');
71
+ if (manualIndex !== -1) {
72
+ notepad = notepad.slice(0, manualIndex) + entry + notepad.slice(manualIndex);
73
+ writeFileSync(notepadPath, notepad);
74
+ }
75
+ } catch {}
76
+ }
77
+
78
+ // Process remember tags
79
+ function processRememberTags(output, notepadPath) {
80
+ if (!output) return;
81
+
82
+ // Process priority remember tags
83
+ const priorityRegex = /<remember\s+priority>([\s\S]*?)<\/remember>/gi;
84
+ let match;
85
+ while ((match = priorityRegex.exec(output)) !== null) {
86
+ const content = match[1].trim();
87
+ if (content) {
88
+ setPriorityContext(notepadPath, content);
89
+ }
90
+ }
91
+
92
+ // Process regular remember tags
93
+ const regularRegex = /<remember>([\s\S]*?)<\/remember>/gi;
94
+ while ((match = regularRegex.exec(output)) !== null) {
95
+ const content = match[1].trim();
96
+ if (content) {
97
+ addWorkingMemoryEntry(notepadPath, content);
98
+ }
99
+ }
100
+ }
101
+
102
+ async function main() {
103
+ try {
104
+ const input = await readStdin();
105
+ const data = JSON.parse(input);
106
+
107
+ const toolName = data.toolName || '';
108
+ const toolOutput = data.toolOutput || '';
109
+ const directory = data.directory || process.cwd();
110
+
111
+ // Only process Task tool output
112
+ if (toolName !== 'Task' && toolName !== 'task') {
113
+ console.log(JSON.stringify({ continue: true }));
114
+ return;
115
+ }
116
+
117
+ // Check for remember tags
118
+ if (!toolOutput.includes('<remember')) {
119
+ console.log(JSON.stringify({ continue: true }));
120
+ return;
121
+ }
122
+
123
+ // Initialize notepad and process tags
124
+ const notepadPath = initNotepad(directory);
125
+ processRememberTags(toolOutput, notepadPath);
126
+
127
+ console.log(JSON.stringify({ continue: true }));
128
+ } catch (error) {
129
+ console.log(JSON.stringify({ continue: true }));
130
+ }
131
+ }
132
+
133
+ main();
@@ -0,0 +1,90 @@
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