oh-my-claude-sisyphus 3.8.11 → 3.8.12
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/.claude-plugin/plugin.json +1 -1
- package/dist/features/continuation-enforcement.js +1 -1
- package/dist/features/continuation-enforcement.js.map +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +6 -29
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/persistent-mode/index.d.ts +2 -1
- package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
- package/dist/hooks/persistent-mode/index.js +7 -17
- package/dist/hooks/persistent-mode/index.js.map +1 -1
- package/dist/hooks/todo-continuation/index.d.ts.map +1 -1
- package/dist/hooks/todo-continuation/index.js +2 -15
- package/dist/hooks/todo-continuation/index.js.map +1 -1
- package/dist/installer/hooks.d.ts +8 -111
- package/dist/installer/hooks.d.ts.map +1 -1
- package/dist/installer/hooks.js +11 -124
- package/dist/installer/hooks.js.map +1 -1
- package/dist/installer/index.d.ts +2 -10
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +10 -23
- package/dist/installer/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/persistent-mode.mjs +26 -53
- package/templates/hooks/persistent-mode.mjs +26 -53
- package/templates/hooks/stop-continuation.mjs +6 -158
- package/hooks/keyword-detector.sh +0 -102
- package/hooks/persistent-mode.sh +0 -172
- package/hooks/session-start.sh +0 -62
- package/hooks/stop-continuation.sh +0 -40
- package/scripts/claude-sisyphus.sh +0 -9
- package/scripts/install.sh +0 -1673
- package/scripts/keyword-detector.sh +0 -71
- package/scripts/persistent-mode.sh +0 -311
- package/scripts/post-tool-verifier.sh +0 -196
- package/scripts/pre-tool-enforcer.sh +0 -76
- package/scripts/sisyphus-aliases.sh +0 -18
- package/scripts/stop-continuation.sh +0 -31
|
@@ -83,23 +83,20 @@ function countIncompleteTasks(sessionId) {
|
|
|
83
83
|
return count;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function countIncompleteTodos(
|
|
86
|
+
function countIncompleteTodos(sessionId, projectDir) {
|
|
87
87
|
let count = 0;
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
// Session-specific todos only (no global scan)
|
|
90
|
+
if (sessionId && typeof sessionId === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId)) {
|
|
91
|
+
const sessionTodoPath = join(homedir(), '.claude', 'todos', `${sessionId}.json`);
|
|
90
92
|
try {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const content = readFileSync(join(todosDir, file), 'utf-8');
|
|
95
|
-
const data = JSON.parse(content);
|
|
96
|
-
const todos = Array.isArray(data) ? data : (Array.isArray(data?.todos) ? data.todos : []);
|
|
97
|
-
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
98
|
-
} catch { /* skip */ }
|
|
99
|
-
}
|
|
93
|
+
const data = readJsonFile(sessionTodoPath);
|
|
94
|
+
const todos = Array.isArray(data) ? data : (Array.isArray(data?.todos) ? data.todos : []);
|
|
95
|
+
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
100
96
|
} catch { /* skip */ }
|
|
101
97
|
}
|
|
102
98
|
|
|
99
|
+
// Project-local todos only
|
|
103
100
|
for (const path of [
|
|
104
101
|
join(projectDir, '.omc', 'todos.json'),
|
|
105
102
|
join(projectDir, '.claude', 'todos.json')
|
|
@@ -171,7 +168,6 @@ async function main() {
|
|
|
171
168
|
|
|
172
169
|
const directory = data.directory || process.cwd();
|
|
173
170
|
const sessionId = data.sessionId || data.session_id || '';
|
|
174
|
-
const todosDir = join(homedir(), '.claude', 'todos');
|
|
175
171
|
const stateDir = join(directory, '.omc', 'state');
|
|
176
172
|
const globalStateDir = join(homedir(), '.omc', 'state');
|
|
177
173
|
|
|
@@ -202,9 +198,9 @@ async function main() {
|
|
|
202
198
|
const swarmMarker = existsSync(join(stateDir, 'swarm-active.marker'));
|
|
203
199
|
const swarmSummary = readJsonFile(join(stateDir, 'swarm-summary.json'));
|
|
204
200
|
|
|
205
|
-
// Count incomplete items
|
|
201
|
+
// Count incomplete items (session-specific + project-local only)
|
|
206
202
|
const taskCount = countIncompleteTasks(sessionId);
|
|
207
|
-
const todoCount = countIncompleteTodos(
|
|
203
|
+
const todoCount = countIncompleteTodos(sessionId, directory);
|
|
208
204
|
const totalIncomplete = taskCount + todoCount;
|
|
209
205
|
|
|
210
206
|
// Priority 1: Ralph Loop (explicit persistence mode)
|
|
@@ -217,8 +213,8 @@ async function main() {
|
|
|
217
213
|
writeJsonFile(ralph.path, ralph.state);
|
|
218
214
|
|
|
219
215
|
console.log(JSON.stringify({
|
|
220
|
-
continue:
|
|
221
|
-
|
|
216
|
+
continue: true,
|
|
217
|
+
message: `[RALPH LOOP - ITERATION ${iteration + 1}/${maxIter}] Work is NOT done. Continue. When complete, output: <promise>${ralph.state.completion_promise || 'DONE'}</promise>\n${ralph.state.prompt ? `Task: ${ralph.state.prompt}` : ''}`
|
|
222
218
|
}));
|
|
223
219
|
return;
|
|
224
220
|
}
|
|
@@ -234,8 +230,8 @@ async function main() {
|
|
|
234
230
|
writeJsonFile(autopilot.path, autopilot.state);
|
|
235
231
|
|
|
236
232
|
console.log(JSON.stringify({
|
|
237
|
-
continue:
|
|
238
|
-
|
|
233
|
+
continue: true,
|
|
234
|
+
message: `[AUTOPILOT - Phase: ${phase}] Autopilot not complete. Continue working.`
|
|
239
235
|
}));
|
|
240
236
|
return;
|
|
241
237
|
}
|
|
@@ -253,8 +249,8 @@ async function main() {
|
|
|
253
249
|
writeJsonFile(ultrapilot.path, ultrapilot.state);
|
|
254
250
|
|
|
255
251
|
console.log(JSON.stringify({
|
|
256
|
-
continue:
|
|
257
|
-
|
|
252
|
+
continue: true,
|
|
253
|
+
message: `[ULTRAPILOT] ${incomplete} workers still running. Continue.`
|
|
258
254
|
}));
|
|
259
255
|
return;
|
|
260
256
|
}
|
|
@@ -271,8 +267,8 @@ async function main() {
|
|
|
271
267
|
writeJsonFile(join(stateDir, 'swarm-summary.json'), swarmSummary);
|
|
272
268
|
|
|
273
269
|
console.log(JSON.stringify({
|
|
274
|
-
continue:
|
|
275
|
-
|
|
270
|
+
continue: true,
|
|
271
|
+
message: `[SWARM ACTIVE] ${pending} tasks remain. Continue working.`
|
|
276
272
|
}));
|
|
277
273
|
return;
|
|
278
274
|
}
|
|
@@ -290,8 +286,8 @@ async function main() {
|
|
|
290
286
|
writeJsonFile(pipeline.path, pipeline.state);
|
|
291
287
|
|
|
292
288
|
console.log(JSON.stringify({
|
|
293
|
-
continue:
|
|
294
|
-
|
|
289
|
+
continue: true,
|
|
290
|
+
message: `[PIPELINE - Stage ${currentStage + 1}/${totalStages}] Pipeline not complete. Continue.`
|
|
295
291
|
}));
|
|
296
292
|
return;
|
|
297
293
|
}
|
|
@@ -307,8 +303,8 @@ async function main() {
|
|
|
307
303
|
writeJsonFile(ultraqa.path, ultraqa.state);
|
|
308
304
|
|
|
309
305
|
console.log(JSON.stringify({
|
|
310
|
-
continue:
|
|
311
|
-
|
|
306
|
+
continue: true,
|
|
307
|
+
message: `[ULTRAQA - Cycle ${cycle + 1}/${maxCycles}] Tests not all passing. Continue fixing.`
|
|
312
308
|
}));
|
|
313
309
|
return;
|
|
314
310
|
}
|
|
@@ -342,8 +338,8 @@ async function main() {
|
|
|
342
338
|
}
|
|
343
339
|
|
|
344
340
|
console.log(JSON.stringify({
|
|
345
|
-
continue:
|
|
346
|
-
reason
|
|
341
|
+
continue: true,
|
|
342
|
+
message: reason
|
|
347
343
|
}));
|
|
348
344
|
return;
|
|
349
345
|
}
|
|
@@ -371,31 +367,8 @@ async function main() {
|
|
|
371
367
|
}
|
|
372
368
|
|
|
373
369
|
console.log(JSON.stringify({
|
|
374
|
-
continue:
|
|
375
|
-
reason
|
|
376
|
-
}));
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Priority 9: Generic Task/Todo continuation (no specific mode)
|
|
381
|
-
if (totalIncomplete > 0) {
|
|
382
|
-
const contFile = join(stateDir, 'continuation-count.json');
|
|
383
|
-
let contState = readJsonFile(contFile) || { count: 0 };
|
|
384
|
-
contState.count = (contState.count || 0) + 1;
|
|
385
|
-
writeJsonFile(contFile, contState);
|
|
386
|
-
|
|
387
|
-
if (contState.count > 15) {
|
|
388
|
-
console.log(JSON.stringify({
|
|
389
|
-
continue: true,
|
|
390
|
-
reason: `[CONTINUATION ESCAPE] Max continuations reached. Allowing stop.`
|
|
391
|
-
}));
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const itemType = taskCount > 0 ? 'Tasks' : 'todos';
|
|
396
|
-
console.log(JSON.stringify({
|
|
397
|
-
continue: false,
|
|
398
|
-
reason: `[CONTINUATION ${contState.count}/15] ${totalIncomplete} incomplete ${itemType}. Continue working.`
|
|
370
|
+
continue: true,
|
|
371
|
+
message: reason
|
|
399
372
|
}));
|
|
400
373
|
return;
|
|
401
374
|
}
|
|
@@ -1,167 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// OMC Stop Continuation Hook (
|
|
3
|
-
//
|
|
4
|
-
// Cross-platform: Windows, macOS, Linux
|
|
2
|
+
// OMC Stop Continuation Hook (Simplified)
|
|
3
|
+
// Always allows stop - soft enforcement via message injection only.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { homedir } from 'os';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Validates session ID to prevent path traversal attacks.
|
|
12
|
-
* @param {string} sessionId
|
|
13
|
-
* @returns {boolean}
|
|
14
|
-
*/
|
|
15
|
-
function isValidSessionId(sessionId) {
|
|
16
|
-
if (!sessionId || typeof sessionId !== 'string') return false;
|
|
17
|
-
return /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Count incomplete tasks in the new Task system.
|
|
22
|
-
*
|
|
23
|
-
* SYNC NOTICE: This function is intentionally duplicated across:
|
|
24
|
-
* - templates/hooks/persistent-mode.mjs
|
|
25
|
-
* - templates/hooks/stop-continuation.mjs
|
|
26
|
-
* - src/hooks/todo-continuation/index.ts (as checkIncompleteTasks)
|
|
27
|
-
*
|
|
28
|
-
* Templates cannot import shared modules (they're standalone scripts).
|
|
29
|
-
* When modifying this logic, update ALL THREE files to maintain consistency.
|
|
30
|
-
*/
|
|
31
|
-
function countIncompleteTasks(sessionId) {
|
|
32
|
-
if (!sessionId || !isValidSessionId(sessionId)) return 0;
|
|
33
|
-
const taskDir = join(homedir(), '.claude', 'tasks', sessionId);
|
|
34
|
-
if (!existsSync(taskDir)) return 0;
|
|
35
|
-
|
|
36
|
-
let count = 0;
|
|
37
|
-
try {
|
|
38
|
-
const files = readdirSync(taskDir).filter(f => f.endsWith('.json') && f !== '.lock');
|
|
39
|
-
for (const file of files) {
|
|
40
|
-
try {
|
|
41
|
-
const content = readFileSync(join(taskDir, file), 'utf-8');
|
|
42
|
-
const task = JSON.parse(content);
|
|
43
|
-
// Match TypeScript isTaskIncomplete(): only pending/in_progress are incomplete
|
|
44
|
-
// 'deleted' and 'completed' are both treated as done
|
|
45
|
-
if (task.status === 'pending' || task.status === 'in_progress') count++;
|
|
46
|
-
} catch { /* skip invalid files */ }
|
|
47
|
-
}
|
|
48
|
-
} catch { /* dir read error */ }
|
|
49
|
-
return count;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Read all stdin
|
|
53
|
-
async function readStdin() {
|
|
5
|
+
// Consume stdin (required for hook protocol)
|
|
6
|
+
async function main() {
|
|
54
7
|
const chunks = [];
|
|
55
8
|
for await (const chunk of process.stdin) {
|
|
56
9
|
chunks.push(chunk);
|
|
57
10
|
}
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Detect if stop was triggered by context-limit related reasons.
|
|
63
|
-
* See: https://github.com/Yeachan-Heo/oh-my-claudecode/issues/213
|
|
64
|
-
*/
|
|
65
|
-
function isContextLimitStop(data) {
|
|
66
|
-
const reason = (data.stop_reason || data.stopReason || '').toLowerCase();
|
|
67
|
-
const endTurnReason = (data.end_turn_reason || data.endTurnReason || '').toLowerCase();
|
|
68
|
-
const contextPatterns = [
|
|
69
|
-
'context_limit', 'context_window', 'context_exceeded', 'context_full',
|
|
70
|
-
'max_context', 'token_limit', 'max_tokens', 'conversation_too_long', 'input_too_long',
|
|
71
|
-
];
|
|
72
|
-
return contextPatterns.some(p => reason.includes(p) || endTurnReason.includes(p));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function isUserAbort(data) {
|
|
76
|
-
if (data.user_requested || data.userRequested) return true;
|
|
77
|
-
const reason = (data.stop_reason || data.stopReason || '').toLowerCase();
|
|
78
|
-
const abortPatterns = [
|
|
79
|
-
'user_cancel', 'user_interrupt', 'ctrl_c', 'manual_stop',
|
|
80
|
-
'aborted', 'abort', 'cancel', 'interrupt',
|
|
81
|
-
];
|
|
82
|
-
return abortPatterns.some(p => reason.includes(p));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Main
|
|
86
|
-
async function main() {
|
|
87
|
-
try {
|
|
88
|
-
// Read stdin to get sessionId and consume it
|
|
89
|
-
const input = await readStdin();
|
|
90
|
-
|
|
91
|
-
// Parse sessionId from input
|
|
92
|
-
let data = {};
|
|
93
|
-
try {
|
|
94
|
-
data = JSON.parse(input);
|
|
95
|
-
} catch { /* invalid JSON - continue with empty data */ }
|
|
96
|
-
|
|
97
|
-
// Never block context-limit or user-abort stops
|
|
98
|
-
if (isContextLimitStop(data) || isUserAbort(data)) {
|
|
99
|
-
console.log(JSON.stringify({ continue: true }));
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const sessionId = data.sessionId || data.session_id || '';
|
|
104
|
-
|
|
105
|
-
// Count incomplete Task system tasks
|
|
106
|
-
const taskCount = countIncompleteTasks(sessionId);
|
|
107
|
-
|
|
108
|
-
// Check for incomplete todos
|
|
109
|
-
const todosDir = join(homedir(), '.claude', 'todos');
|
|
110
|
-
|
|
111
|
-
if (!existsSync(todosDir)) {
|
|
112
|
-
console.log(JSON.stringify({ continue: true }));
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
let incompleteCount = 0;
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
|
|
120
|
-
|
|
121
|
-
for (const file of files) {
|
|
122
|
-
try {
|
|
123
|
-
const content = readFileSync(join(todosDir, file), 'utf-8');
|
|
124
|
-
const todos = JSON.parse(content);
|
|
125
|
-
|
|
126
|
-
if (Array.isArray(todos)) {
|
|
127
|
-
const incomplete = todos.filter(
|
|
128
|
-
t => t.status !== 'completed' && t.status !== 'cancelled'
|
|
129
|
-
);
|
|
130
|
-
incompleteCount += incomplete.length;
|
|
131
|
-
}
|
|
132
|
-
} catch {
|
|
133
|
-
// Skip files that can't be parsed
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
} catch {
|
|
137
|
-
// Directory read error - allow continuation
|
|
138
|
-
console.log(JSON.stringify({ continue: true }));
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Combine both counts
|
|
143
|
-
const totalIncomplete = taskCount + incompleteCount;
|
|
144
|
-
|
|
145
|
-
if (totalIncomplete > 0) {
|
|
146
|
-
const sourceLabel = taskCount > 0 ? 'Task' : 'todo';
|
|
147
|
-
const reason = `[SYSTEM REMINDER - ${sourceLabel.toUpperCase()} CONTINUATION]
|
|
148
|
-
|
|
149
|
-
Incomplete ${sourceLabel}s remain (${totalIncomplete} remaining). Continue working on the next pending ${sourceLabel}.
|
|
150
|
-
|
|
151
|
-
- Proceed without asking for permission
|
|
152
|
-
- Mark each ${sourceLabel} complete when finished
|
|
153
|
-
- Do not stop until all ${sourceLabel}s are done`;
|
|
154
|
-
|
|
155
|
-
console.log(JSON.stringify({ decision: "block", reason }));
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// No incomplete tasks or todos - allow stop
|
|
160
|
-
console.log(JSON.stringify({ continue: true }));
|
|
161
|
-
} catch (error) {
|
|
162
|
-
// On any error, allow continuation
|
|
163
|
-
console.log(JSON.stringify({ continue: true }));
|
|
164
|
-
}
|
|
11
|
+
// Always allow stop
|
|
12
|
+
console.log(JSON.stringify({ continue: true }));
|
|
165
13
|
}
|
|
166
14
|
|
|
167
15
|
main();
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Sisyphus Keyword Detector Hook
|
|
3
|
-
# Detects ultrawork/ultrathink/search/analyze keywords and injects enhanced mode messages
|
|
4
|
-
# Also activates persistent ultrawork state when ultrawork keyword is detected
|
|
5
|
-
|
|
6
|
-
# Read stdin (JSON input from Claude Code)
|
|
7
|
-
INPUT=$(cat)
|
|
8
|
-
|
|
9
|
-
# Extract directory from input
|
|
10
|
-
DIRECTORY=""
|
|
11
|
-
if command -v jq &> /dev/null; then
|
|
12
|
-
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
13
|
-
fi
|
|
14
|
-
if [ -z "$DIRECTORY" ] || [ "$DIRECTORY" = "null" ]; then
|
|
15
|
-
DIRECTORY=$(pwd)
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
# Extract the prompt text - try multiple JSON paths
|
|
19
|
-
PROMPT=""
|
|
20
|
-
if command -v jq &> /dev/null; then
|
|
21
|
-
# Try to extract from various possible JSON structures
|
|
22
|
-
PROMPT=$(echo "$INPUT" | jq -r '
|
|
23
|
-
if .prompt then .prompt
|
|
24
|
-
elif .message.content then .message.content
|
|
25
|
-
elif .parts then ([.parts[] | select(.type == "text") | .text] | join(" "))
|
|
26
|
-
else ""
|
|
27
|
-
end
|
|
28
|
-
' 2>/dev/null)
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
# Fallback: simple grep extraction if jq fails
|
|
32
|
-
if [ -z "$PROMPT" ] || [ "$PROMPT" = "null" ]; then
|
|
33
|
-
PROMPT=$(echo "$INPUT" | grep -oP '"(prompt|content|text)"\s*:\s*"\K[^"]+' | head -1)
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# Exit if no prompt found
|
|
37
|
-
if [ -z "$PROMPT" ]; then
|
|
38
|
-
echo '{"continue": true}'
|
|
39
|
-
exit 0
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# Remove code blocks before checking keywords (prevents false positives)
|
|
43
|
-
PROMPT_NO_CODE=$(echo "$PROMPT" | sed 's/```[^`]*```//g' | sed 's/`[^`]*`//g')
|
|
44
|
-
|
|
45
|
-
# Convert to lowercase for case-insensitive matching
|
|
46
|
-
PROMPT_LOWER=$(echo "$PROMPT_NO_CODE" | tr '[:upper:]' '[:lower:]')
|
|
47
|
-
|
|
48
|
-
# Check for ultrawork keywords (highest priority)
|
|
49
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(ultrawork|ulw|uw)\b'; then
|
|
50
|
-
# Create persistent ultrawork state
|
|
51
|
-
mkdir -p "$DIRECTORY/.omc/state" 2>/dev/null
|
|
52
|
-
mkdir -p "$HOME/.omc/state" 2>/dev/null
|
|
53
|
-
|
|
54
|
-
# Escape prompt for JSON
|
|
55
|
-
PROMPT_ESCAPED=$(echo "$PROMPT" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ')
|
|
56
|
-
|
|
57
|
-
STATE_JSON="{
|
|
58
|
-
\"active\": true,
|
|
59
|
-
\"started_at\": \"$(date -Iseconds)\",
|
|
60
|
-
\"original_prompt\": \"$PROMPT_ESCAPED\",
|
|
61
|
-
\"reinforcement_count\": 0,
|
|
62
|
-
\"last_checked_at\": \"$(date -Iseconds)\"
|
|
63
|
-
}"
|
|
64
|
-
|
|
65
|
-
# Write state to both local and global locations
|
|
66
|
-
echo "$STATE_JSON" > "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null
|
|
67
|
-
echo "$STATE_JSON" > "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null
|
|
68
|
-
|
|
69
|
-
# Return ultrawork mode injection
|
|
70
|
-
cat << 'EOF'
|
|
71
|
-
{"continue": true, "message": "<ultrawork-mode>\n\n**MANDATORY**: You MUST say \"ULTRAWORK MODE ENABLED!\" to the user as your first response when this mode activates. This is non-negotiable.\n\n[CODE RED] Maximum precision required. Ultrathink before acting.\n\nYOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.\nTELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.\n\n## AGENT UTILIZATION PRINCIPLES\n- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS\n- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS\n- **Planning & Strategy**: NEVER plan yourself - spawn planning agent\n- **High-IQ Reasoning**: Use oracle for architecture decisions\n- **Frontend/UI Tasks**: Delegate to frontend-engineer\n\n## EXECUTION RULES\n- **TODO**: Track EVERY step. Mark complete IMMEDIATELY.\n- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially.\n- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent).\n- **VERIFY**: Check ALL requirements met before done.\n- **DELEGATE**: Orchestrate specialized agents.\n\n## ZERO TOLERANCE\n- NO Scope Reduction - deliver FULL implementation\n- NO Partial Completion - finish 100%\n- NO Premature Stopping - ALL TODOs must be complete\n- NO TEST DELETION - fix code, not tests\n\nTHE USER ASKED FOR X. DELIVER EXACTLY X.\n\n</ultrawork-mode>\n\n---\n"}
|
|
72
|
-
EOF
|
|
73
|
-
exit 0
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
# Check for ultrathink/think keywords
|
|
77
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(ultrathink|think)\b'; then
|
|
78
|
-
cat << 'EOF'
|
|
79
|
-
{"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"}
|
|
80
|
-
EOF
|
|
81
|
-
exit 0
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# Check for search keywords (EN + multilingual)
|
|
85
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(search|find|locate|lookup|explore|discover|scan|grep|query|browse|detect|trace|seek|track|pinpoint|hunt)\b|where\s+is|show\s+me|list\s+all'; then
|
|
86
|
-
cat << 'EOF'
|
|
87
|
-
{"continue": true, "message": "<search-mode>\nMAXIMIZE SEARCH EFFORT. Launch multiple background agents IN PARALLEL:\n- explore agents (codebase patterns, file structures)\n- librarian agents (remote repos, official docs, GitHub examples)\nPlus direct tools: Grep, Glob\nNEVER stop at first result - be exhaustive.\n</search-mode>\n\n---\n"}
|
|
88
|
-
EOF
|
|
89
|
-
exit 0
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
# Check for analyze keywords
|
|
93
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(analyze|analyse|investigate|examine|research|study|deep.?dive|inspect|audit|evaluate|assess|review|diagnose|scrutinize|dissect|debug|comprehend|interpret|breakdown|understand)\b|why\s+is|how\s+does|how\s+to'; then
|
|
94
|
-
cat << 'EOF'
|
|
95
|
-
{"continue": true, "message": "<analyze-mode>\nANALYSIS MODE. Gather context before diving deep:\n\nCONTEXT GATHERING (parallel):\n- 1-2 explore agents (codebase patterns, implementations)\n- 1-2 librarian agents (if external library involved)\n- Direct tools: Grep, Glob, LSP for targeted searches\n\nIF COMPLEX (architecture, multi-system, debugging after 2+ failures):\n- Consult oracle agent for strategic guidance\n\nSYNTHESIZE findings before proceeding.\n</analyze-mode>\n\n---\n"}
|
|
96
|
-
EOF
|
|
97
|
-
exit 0
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# No keywords detected - continue without modification
|
|
101
|
-
echo '{"continue": true}'
|
|
102
|
-
exit 0
|
package/hooks/persistent-mode.sh
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Sisyphus 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
|
-
# Check for active ultrawork state
|
|
23
|
-
ULTRAWORK_STATE=""
|
|
24
|
-
if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ]; then
|
|
25
|
-
ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
26
|
-
elif [ -f "$HOME/.omc/state/ultrawork-state.json" ]; then
|
|
27
|
-
ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
# Check for active ralph loop
|
|
31
|
-
RALPH_STATE=""
|
|
32
|
-
if [ -f "$DIRECTORY/.omc/state/ralph-state.json" ]; then
|
|
33
|
-
RALPH_STATE=$(cat "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null)
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# Check for verification state (oracle verification)
|
|
37
|
-
VERIFICATION_STATE=""
|
|
38
|
-
if [ -f "$DIRECTORY/.omc/state/ralph-verification.json" ]; then
|
|
39
|
-
VERIFICATION_STATE=$(cat "$DIRECTORY/.omc/state/ralph-verification.json" 2>/dev/null)
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# Check for incomplete todos
|
|
43
|
-
INCOMPLETE_COUNT=0
|
|
44
|
-
TODOS_DIR="$HOME/.claude/todos"
|
|
45
|
-
if [ -d "$TODOS_DIR" ]; then
|
|
46
|
-
for todo_file in "$TODOS_DIR"/*.json; do
|
|
47
|
-
if [ -f "$todo_file" ]; then
|
|
48
|
-
if command -v jq &> /dev/null; then
|
|
49
|
-
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
50
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
51
|
-
else
|
|
52
|
-
# Fallback: count "pending" or "in_progress" occurrences
|
|
53
|
-
COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\|in_progress"' "$todo_file" 2>/dev/null) || COUNT=0
|
|
54
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
55
|
-
fi
|
|
56
|
-
fi
|
|
57
|
-
done
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
# Check project todos as well
|
|
61
|
-
for todo_path in "$DIRECTORY/.omc/todos.json" "$DIRECTORY/.claude/todos.json"; do
|
|
62
|
-
if [ -f "$todo_path" ]; then
|
|
63
|
-
if command -v jq &> /dev/null; then
|
|
64
|
-
COUNT=$(jq 'if type == "array" then [.[] | select(.status != "completed" and .status != "cancelled")] | length else 0 end' "$todo_path" 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_path" 2>/dev/null) || COUNT=0
|
|
69
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
70
|
-
fi
|
|
71
|
-
fi
|
|
72
|
-
done
|
|
73
|
-
|
|
74
|
-
# Priority 1: Ralph Loop with Oracle Verification
|
|
75
|
-
if [ -n "$RALPH_STATE" ]; then
|
|
76
|
-
IS_ACTIVE=$(echo "$RALPH_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
77
|
-
if [ "$IS_ACTIVE" = "true" ]; then
|
|
78
|
-
ITERATION=$(echo "$RALPH_STATE" | jq -r '.iteration // 1' 2>/dev/null)
|
|
79
|
-
MAX_ITER=$(echo "$RALPH_STATE" | jq -r '.max_iterations // 10' 2>/dev/null)
|
|
80
|
-
PROMISE=$(echo "$RALPH_STATE" | jq -r '.completion_promise // "TASK_COMPLETE"' 2>/dev/null)
|
|
81
|
-
PROMPT=$(echo "$RALPH_STATE" | jq -r '.prompt // ""' 2>/dev/null)
|
|
82
|
-
|
|
83
|
-
# Check if oracle verification is pending
|
|
84
|
-
if [ -n "$VERIFICATION_STATE" ]; then
|
|
85
|
-
IS_PENDING=$(echo "$VERIFICATION_STATE" | jq -r '.pending // false' 2>/dev/null)
|
|
86
|
-
if [ "$IS_PENDING" = "true" ]; then
|
|
87
|
-
ATTEMPT=$(echo "$VERIFICATION_STATE" | jq -r '.verification_attempts // 0' 2>/dev/null)
|
|
88
|
-
MAX_ATTEMPTS=$(echo "$VERIFICATION_STATE" | jq -r '.max_verification_attempts // 3' 2>/dev/null)
|
|
89
|
-
ORIGINAL_TASK=$(echo "$VERIFICATION_STATE" | jq -r '.original_task // ""' 2>/dev/null)
|
|
90
|
-
COMPLETION_CLAIM=$(echo "$VERIFICATION_STATE" | jq -r '.completion_claim // ""' 2>/dev/null)
|
|
91
|
-
ORACLE_FEEDBACK=$(echo "$VERIFICATION_STATE" | jq -r '.oracle_feedback // ""' 2>/dev/null)
|
|
92
|
-
NEXT_ATTEMPT=$((ATTEMPT + 1))
|
|
93
|
-
|
|
94
|
-
FEEDBACK_SECTION=""
|
|
95
|
-
if [ -n "$ORACLE_FEEDBACK" ] && [ "$ORACLE_FEEDBACK" != "null" ]; then
|
|
96
|
-
FEEDBACK_SECTION="\n**Previous Oracle Feedback (rejected):**\n$ORACLE_FEEDBACK\n"
|
|
97
|
-
fi
|
|
98
|
-
|
|
99
|
-
cat << EOF
|
|
100
|
-
{"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"}
|
|
101
|
-
EOF
|
|
102
|
-
exit 0
|
|
103
|
-
fi
|
|
104
|
-
fi
|
|
105
|
-
|
|
106
|
-
if [ "$ITERATION" -lt "$MAX_ITER" ]; then
|
|
107
|
-
# Increment iteration
|
|
108
|
-
NEW_ITER=$((ITERATION + 1))
|
|
109
|
-
echo "$RALPH_STATE" | jq ".iteration = $NEW_ITER" > "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null
|
|
110
|
-
|
|
111
|
-
cat << EOF
|
|
112
|
-
{"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"}
|
|
113
|
-
EOF
|
|
114
|
-
exit 0
|
|
115
|
-
fi
|
|
116
|
-
fi
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
# Priority 2: Ultrawork Mode with incomplete todos
|
|
120
|
-
if [ -n "$ULTRAWORK_STATE" ] && [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
121
|
-
# Check if active (with jq fallback)
|
|
122
|
-
IS_ACTIVE=""
|
|
123
|
-
if command -v jq &> /dev/null; then
|
|
124
|
-
IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
125
|
-
else
|
|
126
|
-
# Fallback: grep for "active": true
|
|
127
|
-
if echo "$ULTRAWORK_STATE" | grep -q '"active"[[:space:]]*:[[:space:]]*true'; then
|
|
128
|
-
IS_ACTIVE="true"
|
|
129
|
-
fi
|
|
130
|
-
fi
|
|
131
|
-
|
|
132
|
-
if [ "$IS_ACTIVE" = "true" ]; then
|
|
133
|
-
# Get reinforcement count (with fallback)
|
|
134
|
-
REINFORCE_COUNT=0
|
|
135
|
-
if command -v jq &> /dev/null; then
|
|
136
|
-
REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | jq -r '.reinforcement_count // 0' 2>/dev/null)
|
|
137
|
-
else
|
|
138
|
-
REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | grep -oP '"reinforcement_count"[[:space:]]*:[[:space:]]*\K[0-9]+' 2>/dev/null) || REINFORCE_COUNT=0
|
|
139
|
-
fi
|
|
140
|
-
NEW_COUNT=$((REINFORCE_COUNT + 1))
|
|
141
|
-
|
|
142
|
-
# Get original prompt (with fallback)
|
|
143
|
-
ORIGINAL_PROMPT=""
|
|
144
|
-
if command -v jq &> /dev/null; then
|
|
145
|
-
ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
|
|
146
|
-
else
|
|
147
|
-
ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | grep -oP '"original_prompt"[[:space:]]*:[[:space:]]*"\K[^"]+' 2>/dev/null) || ORIGINAL_PROMPT=""
|
|
148
|
-
fi
|
|
149
|
-
|
|
150
|
-
# Update state file (best effort)
|
|
151
|
-
if command -v jq &> /dev/null; then
|
|
152
|
-
echo "$ULTRAWORK_STATE" | jq ".reinforcement_count = $NEW_COUNT | .last_checked_at = \"$(date -Iseconds)\"" > "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null
|
|
153
|
-
fi
|
|
154
|
-
|
|
155
|
-
cat << EOF
|
|
156
|
-
{"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"}
|
|
157
|
-
EOF
|
|
158
|
-
exit 0
|
|
159
|
-
fi
|
|
160
|
-
fi
|
|
161
|
-
|
|
162
|
-
# Priority 3: Todo Continuation (baseline)
|
|
163
|
-
if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
164
|
-
cat << EOF
|
|
165
|
-
{"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"}
|
|
166
|
-
EOF
|
|
167
|
-
exit 0
|
|
168
|
-
fi
|
|
169
|
-
|
|
170
|
-
# No blocking needed
|
|
171
|
-
echo '{"continue": true}'
|
|
172
|
-
exit 0
|
package/hooks/session-start.sh
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Sisyphus 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
|