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.
- package/README.md +16 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts +9 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts.map +1 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js +550 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js.map +1 -0
- package/dist/__tests__/installer.test.js +1 -1
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js +313 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts +8 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js +329 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js +167 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js +295 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js.map +1 -0
- package/dist/cli/commands/wait.d.ts +52 -0
- package/dist/cli/commands/wait.d.ts.map +1 -0
- package/dist/cli/commands/wait.js +229 -0
- package/dist/cli/commands/wait.js.map +1 -0
- package/dist/cli/index.js +54 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/features/rate-limit-wait/daemon.d.ts +52 -0
- package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/daemon.js +545 -0
- package/dist/features/rate-limit-wait/daemon.js.map +1 -0
- package/dist/features/rate-limit-wait/index.d.ts +16 -0
- package/dist/features/rate-limit-wait/index.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/index.js +18 -0
- package/dist/features/rate-limit-wait/index.js.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts +22 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js +99 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts +59 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.js +304 -0
- package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -0
- package/dist/features/rate-limit-wait/types.d.ts +121 -0
- package/dist/features/rate-limit-wait/types.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/types.js +8 -0
- package/dist/features/rate-limit-wait/types.js.map +1 -0
- package/dist/hooks/bridge.d.ts +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +50 -4
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +15 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts +2 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.js.map +1 -1
- package/dist/hooks/omc-orchestrator/index.d.ts +7 -0
- package/dist/hooks/omc-orchestrator/index.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/index.js +95 -8
- package/dist/hooks/omc-orchestrator/index.js.map +1 -1
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts +2 -0
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts.map +1 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js +244 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -0
- package/dist/hooks/permission-handler/index.d.ts +42 -0
- package/dist/hooks/permission-handler/index.d.ts.map +1 -0
- package/dist/hooks/permission-handler/index.js +111 -0
- package/dist/hooks/permission-handler/index.js.map +1 -0
- package/dist/hooks/pre-compact/index.d.ts +82 -0
- package/dist/hooks/pre-compact/index.d.ts.map +1 -0
- package/dist/hooks/pre-compact/index.js +265 -0
- package/dist/hooks/pre-compact/index.js.map +1 -0
- package/dist/hooks/session-end/index.d.ts +50 -0
- package/dist/hooks/session-end/index.d.ts.map +1 -0
- package/dist/hooks/session-end/index.js +207 -0
- package/dist/hooks/session-end/index.js.map +1 -0
- package/dist/hooks/setup/index.d.ts +66 -0
- package/dist/hooks/setup/index.d.ts.map +1 -0
- package/dist/hooks/setup/index.js +299 -0
- package/dist/hooks/setup/index.js.map +1 -0
- package/dist/hooks/setup/types.d.ts +25 -0
- package/dist/hooks/setup/types.d.ts.map +1 -0
- package/dist/hooks/setup/types.js +5 -0
- package/dist/hooks/setup/types.js.map +1 -0
- package/dist/hooks/subagent-tracker/index.d.ts +68 -29
- package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
- package/dist/hooks/subagent-tracker/index.js +301 -131
- package/dist/hooks/subagent-tracker/index.js.map +1 -1
- package/dist/installer/index.d.ts +1 -1
- package/dist/installer/index.js +1 -1
- package/hooks/hooks.json +83 -1
- package/package.json +3 -1
- package/scripts/permission-handler.mjs +23 -0
- package/scripts/pre-compact.mjs +23 -0
- package/scripts/session-end.mjs +23 -0
- package/scripts/setup-init.mjs +23 -0
- package/scripts/setup-maintenance.mjs +23 -0
- package/scripts/subagent-tracker.mjs +35 -0
- package/templates/hooks/keyword-detector.mjs +198 -0
- package/templates/hooks/keyword-detector.sh +102 -0
- package/templates/hooks/persistent-mode.mjs +249 -0
- package/templates/hooks/persistent-mode.sh +187 -0
- package/templates/hooks/post-tool-use.mjs +133 -0
- package/templates/hooks/post-tool-use.sh +90 -0
- package/templates/hooks/pre-tool-use.mjs +145 -0
- package/templates/hooks/pre-tool-use.sh +113 -0
- package/templates/hooks/session-start.mjs +100 -0
- package/templates/hooks/session-start.sh +62 -0
- package/templates/hooks/stop-continuation.mjs +80 -0
- package/templates/hooks/stop-continuation.sh +40 -0
- package/templates/rules/README.md +40 -0
- package/templates/rules/coding-style.md +74 -0
- package/templates/rules/git-workflow.md +41 -0
- package/templates/rules/performance.md +40 -0
- package/templates/rules/security.md +41 -0
- 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
|