oh-my-claude-sisyphus 3.8.5 → 3.8.7
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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/dist/__tests__/installer.test.js +1 -1
- package/dist/installer/hooks.d.ts +1 -1
- package/dist/installer/hooks.d.ts.map +1 -1
- package/dist/installer/hooks.js +6 -9
- package/dist/installer/hooks.js.map +1 -1
- package/dist/installer/index.d.ts +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +3 -3
- package/dist/installer/index.js.map +1 -1
- package/docs/MIGRATION.md +7 -3
- package/docs/REFERENCE.md +8 -33
- package/package.json +1 -1
- package/scripts/persistent-mode.mjs +202 -317
- package/skills/omc-setup/SKILL.md +6 -25
- package/templates/hooks/persistent-mode.mjs +220 -184
|
@@ -446,34 +446,15 @@ Ask user: "Would you like to install the OMC CLI for standalone analytics? (Reco
|
|
|
446
446
|
1. **Yes (Recommended)** - Install CLI tools globally for `omc stats`, `omc agents`, etc.
|
|
447
447
|
2. **No** - Skip CLI installation, use only plugin skills
|
|
448
448
|
|
|
449
|
-
###
|
|
449
|
+
### CLI Installation Note
|
|
450
450
|
|
|
451
|
-
|
|
452
|
-
# Check for bun (preferred) or npm
|
|
453
|
-
if command -v bun &> /dev/null; then
|
|
454
|
-
echo "Installing OMC CLI via bun..."
|
|
455
|
-
bun install -g oh-my-claude-sisyphus
|
|
456
|
-
elif command -v npm &> /dev/null; then
|
|
457
|
-
echo "Installing OMC CLI via npm..."
|
|
458
|
-
npm install -g oh-my-claude-sisyphus
|
|
459
|
-
else
|
|
460
|
-
echo "ERROR: Neither bun nor npm found. Please install Node.js or Bun first."
|
|
461
|
-
exit 1
|
|
462
|
-
fi
|
|
463
|
-
|
|
464
|
-
# Verify installation
|
|
465
|
-
if command -v omc &> /dev/null; then
|
|
466
|
-
echo "✓ OMC CLI installed successfully!"
|
|
467
|
-
echo " Try: omc stats, omc agents, omc tui"
|
|
468
|
-
else
|
|
469
|
-
echo "⚠ CLI installed but 'omc' not in PATH."
|
|
470
|
-
echo " You may need to restart your terminal or add npm/bun global bin to PATH."
|
|
471
|
-
fi
|
|
472
|
-
```
|
|
451
|
+
The CLI (`omc` command) is **no longer supported** via npm/bun global install.
|
|
473
452
|
|
|
474
|
-
|
|
453
|
+
All functionality is available through the plugin system:
|
|
454
|
+
- Use `/oh-my-claudecode:help` for guidance
|
|
455
|
+
- Use `/oh-my-claudecode:doctor` for diagnostics
|
|
475
456
|
|
|
476
|
-
Skip this step
|
|
457
|
+
Skip this step - the plugin provides all features.
|
|
477
458
|
|
|
478
459
|
## Step 4: Verify Plugin Installation
|
|
479
460
|
|
|
@@ -1,23 +1,16 @@
|
|
|
1
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
2
|
|
|
10
3
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
4
|
+
* OMC Persistent Mode Hook (Node.js)
|
|
5
|
+
* Minimal continuation enforcer for all OMC modes.
|
|
6
|
+
* Stripped down for reliability — no optional imports, no PRD, no notepad pruning.
|
|
7
|
+
*
|
|
8
|
+
* Supported modes: ralph, autopilot, ultrapilot, swarm, ultrawork, ecomode, ultraqa, pipeline
|
|
14
9
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId);
|
|
20
|
-
}
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { homedir } from 'os';
|
|
21
14
|
|
|
22
15
|
async function readStdin() {
|
|
23
16
|
const chunks = [];
|
|
@@ -36,19 +29,43 @@ function readJsonFile(path) {
|
|
|
36
29
|
}
|
|
37
30
|
}
|
|
38
31
|
|
|
32
|
+
function writeJsonFile(path, data) {
|
|
33
|
+
try {
|
|
34
|
+
// Ensure directory exists
|
|
35
|
+
const dir = path.substring(0, path.lastIndexOf('/'));
|
|
36
|
+
if (dir && !existsSync(dir)) {
|
|
37
|
+
mkdirSync(dir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
40
|
+
return true;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
/**
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
* Read state file from local or global location, tracking the source.
|
|
48
|
+
*/
|
|
49
|
+
function readStateFile(stateDir, globalStateDir, filename) {
|
|
50
|
+
const localPath = join(stateDir, filename);
|
|
51
|
+
const globalPath = join(globalStateDir, filename);
|
|
52
|
+
|
|
53
|
+
let state = readJsonFile(localPath);
|
|
54
|
+
if (state) return { state, path: localPath };
|
|
55
|
+
|
|
56
|
+
state = readJsonFile(globalPath);
|
|
57
|
+
if (state) return { state, path: globalPath };
|
|
58
|
+
|
|
59
|
+
return { state: null, path: localPath }; // Default to local for new writes
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Count incomplete Tasks from Claude Code's native Task system.
|
|
49
64
|
*/
|
|
50
65
|
function countIncompleteTasks(sessionId) {
|
|
51
|
-
if (!sessionId ||
|
|
66
|
+
if (!sessionId || typeof sessionId !== 'string') return 0;
|
|
67
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId)) return 0;
|
|
68
|
+
|
|
52
69
|
const taskDir = join(homedir(), '.claude', 'tasks', sessionId);
|
|
53
70
|
if (!existsSync(taskDir)) return 0;
|
|
54
71
|
|
|
@@ -59,49 +76,39 @@ function countIncompleteTasks(sessionId) {
|
|
|
59
76
|
try {
|
|
60
77
|
const content = readFileSync(join(taskDir, file), 'utf-8');
|
|
61
78
|
const task = JSON.parse(content);
|
|
62
|
-
// Match TypeScript isTaskIncomplete(): only pending/in_progress are incomplete
|
|
63
|
-
// 'deleted' and 'completed' are both treated as done
|
|
64
79
|
if (task.status === 'pending' || task.status === 'in_progress') count++;
|
|
65
|
-
} catch { /* skip
|
|
80
|
+
} catch { /* skip */ }
|
|
66
81
|
}
|
|
67
|
-
} catch { /*
|
|
82
|
+
} catch { /* skip */ }
|
|
68
83
|
return count;
|
|
69
84
|
}
|
|
70
85
|
|
|
71
|
-
function writeJsonFile(path, data) {
|
|
72
|
-
try {
|
|
73
|
-
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
74
|
-
return true;
|
|
75
|
-
} catch {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
86
|
function countIncompleteTodos(todosDir, projectDir) {
|
|
81
87
|
let count = 0;
|
|
82
88
|
|
|
83
|
-
// Check global todos
|
|
84
89
|
if (existsSync(todosDir)) {
|
|
85
90
|
try {
|
|
86
91
|
const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
|
|
87
92
|
for (const file of files) {
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
try {
|
|
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 : []);
|
|
90
97
|
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
91
|
-
}
|
|
98
|
+
} catch { /* skip */ }
|
|
92
99
|
}
|
|
93
|
-
} catch {}
|
|
100
|
+
} catch { /* skip */ }
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
// Check project todos
|
|
97
103
|
for (const path of [
|
|
98
104
|
join(projectDir, '.omc', 'todos.json'),
|
|
99
105
|
join(projectDir, '.claude', 'todos.json')
|
|
100
106
|
]) {
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
try {
|
|
108
|
+
const data = readJsonFile(path);
|
|
109
|
+
const todos = Array.isArray(data) ? data : (Array.isArray(data?.todos) ? data.todos : []);
|
|
103
110
|
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
104
|
-
}
|
|
111
|
+
} catch { /* skip */ }
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
return count;
|
|
@@ -113,178 +120,205 @@ async function main() {
|
|
|
113
120
|
let data = {};
|
|
114
121
|
try { data = JSON.parse(input); } catch {}
|
|
115
122
|
|
|
116
|
-
const stopReason = data.stop_reason || data.stopReason || '';
|
|
117
|
-
const userRequested = data.user_requested || data.userRequested || false;
|
|
118
|
-
const sessionId = data.sessionId || data.session_id || '';
|
|
119
|
-
|
|
120
|
-
// Check for user abort - skip all continuation enforcement
|
|
121
|
-
// NOTE: Abort patterns are assumed - verify against actual Claude Code API values
|
|
122
|
-
if (userRequested || /abort|cancel|interrupt|ctrl_c|manual_stop/i.test(stopReason)) {
|
|
123
|
-
console.log(JSON.stringify({ continue: true }));
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
123
|
const directory = data.directory || process.cwd();
|
|
124
|
+
const sessionId = data.sessionId || data.session_id || '';
|
|
128
125
|
const todosDir = join(homedir(), '.claude', 'todos');
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
const stateDir = join(directory, '.omc', 'state');
|
|
127
|
+
const globalStateDir = join(homedir(), '.omc', 'state');
|
|
128
|
+
|
|
129
|
+
// Read all mode states (local-first with fallback to global)
|
|
130
|
+
const ralph = readStateFile(stateDir, globalStateDir, 'ralph-state.json');
|
|
131
|
+
const autopilot = readStateFile(stateDir, globalStateDir, 'autopilot-state.json');
|
|
132
|
+
const ultrapilot = readStateFile(stateDir, globalStateDir, 'ultrapilot-state.json');
|
|
133
|
+
const ultrawork = readStateFile(stateDir, globalStateDir, 'ultrawork-state.json');
|
|
134
|
+
const ecomode = readStateFile(stateDir, globalStateDir, 'ecomode-state.json');
|
|
135
|
+
const ultraqa = readStateFile(stateDir, globalStateDir, 'ultraqa-state.json');
|
|
136
|
+
const pipeline = readStateFile(stateDir, globalStateDir, 'pipeline-state.json');
|
|
137
|
+
|
|
138
|
+
// Swarm uses swarm-summary.json (not swarm-state.json) + marker file
|
|
139
|
+
const swarmMarker = existsSync(join(stateDir, 'swarm-active.marker'));
|
|
140
|
+
const swarmSummary = readJsonFile(join(stateDir, 'swarm-summary.json'));
|
|
141
|
+
|
|
142
|
+
// Count incomplete items
|
|
144
143
|
const taskCount = countIncompleteTasks(sessionId);
|
|
145
|
-
const
|
|
144
|
+
const todoCount = countIncompleteTodos(todosDir, directory);
|
|
145
|
+
const totalIncomplete = taskCount + todoCount;
|
|
146
146
|
|
|
147
|
-
// Priority 1: Ralph Loop
|
|
148
|
-
if (
|
|
149
|
-
const iteration =
|
|
150
|
-
const maxIter =
|
|
147
|
+
// Priority 1: Ralph Loop (explicit persistence mode)
|
|
148
|
+
if (ralph.state?.active) {
|
|
149
|
+
const iteration = ralph.state.iteration || 1;
|
|
150
|
+
const maxIter = ralph.state.max_iterations || 100;
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const maxAttempts = verificationState.max_verification_attempts || 3;
|
|
152
|
+
if (iteration < maxIter) {
|
|
153
|
+
ralph.state.iteration = iteration + 1;
|
|
154
|
+
writeJsonFile(ralph.path, ralph.state);
|
|
156
155
|
|
|
157
156
|
console.log(JSON.stringify({
|
|
158
|
-
|
|
159
|
-
reason:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
**Original Task:**
|
|
166
|
-
${verificationState.original_task || ralphState.prompt || 'No task specified'}
|
|
167
|
-
|
|
168
|
-
**Completion Claim:**
|
|
169
|
-
${verificationState.completion_claim || 'Task marked complete'}
|
|
170
|
-
|
|
171
|
-
${verificationState.oracle_feedback ? `**Previous Oracle Feedback (rejected):**
|
|
172
|
-
${verificationState.oracle_feedback}
|
|
173
|
-
` : ''}
|
|
174
|
-
|
|
175
|
-
## MANDATORY VERIFICATION STEPS
|
|
157
|
+
continue: false,
|
|
158
|
+
reason: `[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}` : ''}`
|
|
159
|
+
}));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
176
163
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
164
|
+
// Priority 2: Autopilot (high-level orchestration)
|
|
165
|
+
if (autopilot.state?.active) {
|
|
166
|
+
const phase = autopilot.state.phase || 'unknown';
|
|
167
|
+
if (phase !== 'complete') {
|
|
168
|
+
const newCount = (autopilot.state.reinforcement_count || 0) + 1;
|
|
169
|
+
if (newCount <= 20) {
|
|
170
|
+
autopilot.state.reinforcement_count = newCount;
|
|
171
|
+
writeJsonFile(autopilot.path, autopilot.state);
|
|
172
|
+
|
|
173
|
+
console.log(JSON.stringify({
|
|
174
|
+
continue: false,
|
|
175
|
+
reason: `[AUTOPILOT - Phase: ${phase}] Autopilot not complete. Continue working.`
|
|
176
|
+
}));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
// Priority 3: Ultrapilot (parallel autopilot)
|
|
183
|
+
if (ultrapilot.state?.active) {
|
|
184
|
+
const workers = ultrapilot.state.workers || [];
|
|
185
|
+
const incomplete = workers.filter(w => w.status !== 'complete' && w.status !== 'failed').length;
|
|
186
|
+
if (incomplete > 0) {
|
|
187
|
+
const newCount = (ultrapilot.state.reinforcement_count || 0) + 1;
|
|
188
|
+
if (newCount <= 20) {
|
|
189
|
+
ultrapilot.state.reinforcement_count = newCount;
|
|
190
|
+
writeJsonFile(ultrapilot.path, ultrapilot.state);
|
|
191
|
+
|
|
192
|
+
console.log(JSON.stringify({
|
|
193
|
+
continue: false,
|
|
194
|
+
reason: `[ULTRAPILOT] ${incomplete} workers still running. Continue.`
|
|
195
|
+
}));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
188
200
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
201
|
+
// Priority 4: Swarm (coordinated agents with SQLite)
|
|
202
|
+
if (swarmMarker && swarmSummary?.active) {
|
|
203
|
+
const pending = (swarmSummary.tasks_pending || 0) + (swarmSummary.tasks_claimed || 0);
|
|
204
|
+
if (pending > 0) {
|
|
205
|
+
const newCount = (swarmSummary.reinforcement_count || 0) + 1;
|
|
206
|
+
if (newCount <= 15) {
|
|
207
|
+
swarmSummary.reinforcement_count = newCount;
|
|
208
|
+
writeJsonFile(join(stateDir, 'swarm-summary.json'), swarmSummary);
|
|
209
|
+
|
|
210
|
+
console.log(JSON.stringify({
|
|
211
|
+
continue: false,
|
|
212
|
+
reason: `[SWARM ACTIVE] ${pending} tasks remain. Continue working.`
|
|
213
|
+
}));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
192
218
|
|
|
193
|
-
|
|
219
|
+
// Priority 5: Pipeline (sequential stages)
|
|
220
|
+
if (pipeline.state?.active) {
|
|
221
|
+
const currentStage = pipeline.state.current_stage || 0;
|
|
222
|
+
const totalStages = pipeline.state.stages?.length || 0;
|
|
223
|
+
if (currentStage < totalStages) {
|
|
224
|
+
const newCount = (pipeline.state.reinforcement_count || 0) + 1;
|
|
225
|
+
if (newCount <= 15) {
|
|
226
|
+
pipeline.state.reinforcement_count = newCount;
|
|
227
|
+
writeJsonFile(pipeline.path, pipeline.state);
|
|
228
|
+
|
|
229
|
+
console.log(JSON.stringify({
|
|
230
|
+
continue: false,
|
|
231
|
+
reason: `[PIPELINE - Stage ${currentStage + 1}/${totalStages}] Pipeline not complete. Continue.`
|
|
232
|
+
}));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
194
237
|
|
|
195
|
-
|
|
238
|
+
// Priority 6: UltraQA (QA cycling)
|
|
239
|
+
if (ultraqa.state?.active) {
|
|
240
|
+
const cycle = ultraqa.state.cycle || 1;
|
|
241
|
+
const maxCycles = ultraqa.state.max_cycles || 10;
|
|
242
|
+
if (cycle < maxCycles && !ultraqa.state.all_passing) {
|
|
243
|
+
ultraqa.state.cycle = cycle + 1;
|
|
244
|
+
writeJsonFile(ultraqa.path, ultraqa.state);
|
|
196
245
|
|
|
197
|
-
|
|
198
|
-
|
|
246
|
+
console.log(JSON.stringify({
|
|
247
|
+
continue: false,
|
|
248
|
+
reason: `[ULTRAQA - Cycle ${cycle + 1}/${maxCycles}] Tests not all passing. Continue fixing.`
|
|
199
249
|
}));
|
|
200
250
|
return;
|
|
201
251
|
}
|
|
252
|
+
}
|
|
202
253
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
254
|
+
// Priority 7: Ultrawork with incomplete todos/tasks
|
|
255
|
+
if (ultrawork.state?.active && totalIncomplete > 0) {
|
|
256
|
+
const newCount = (ultrawork.state.reinforcement_count || 0) + 1;
|
|
257
|
+
const maxReinforcements = ultrawork.state.max_reinforcements || 15;
|
|
207
258
|
|
|
259
|
+
if (newCount > maxReinforcements) {
|
|
208
260
|
console.log(JSON.stringify({
|
|
209
|
-
|
|
210
|
-
reason:
|
|
211
|
-
|
|
212
|
-
[RALPH LOOP - ITERATION ${newIter}/${maxIter}]
|
|
213
|
-
|
|
214
|
-
Your previous attempt did not output the completion promise. The work is NOT done yet.
|
|
215
|
-
|
|
216
|
-
CRITICAL INSTRUCTIONS:
|
|
217
|
-
1. Review your progress and the original task
|
|
218
|
-
2. Check your todo list - are ALL items marked complete?
|
|
219
|
-
3. Continue from where you left off
|
|
220
|
-
4. When FULLY complete, output: <promise>${ralphState.completion_promise || 'TASK_COMPLETE'}</promise>
|
|
221
|
-
5. Do NOT stop until the task is truly done
|
|
222
|
-
|
|
223
|
-
${ralphState.prompt ? `Original task: ${ralphState.prompt}` : ''}
|
|
224
|
-
|
|
225
|
-
</ralph-loop-continuation>
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
`
|
|
261
|
+
continue: true,
|
|
262
|
+
reason: `[ULTRAWORK ESCAPE] Max reinforcements reached. Allowing stop.`
|
|
229
263
|
}));
|
|
230
264
|
return;
|
|
231
265
|
}
|
|
232
|
-
}
|
|
233
266
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
ultraworkState.reinforcement_count = newCount;
|
|
238
|
-
ultraworkState.last_checked_at = new Date().toISOString();
|
|
239
|
-
|
|
240
|
-
writeJsonFile(join(directory, '.omc', 'ultrawork-state.json'), ultraworkState);
|
|
267
|
+
ultrawork.state.reinforcement_count = newCount;
|
|
268
|
+
ultrawork.state.last_checked_at = new Date().toISOString();
|
|
269
|
+
writeJsonFile(ultrawork.path, ultrawork.state);
|
|
241
270
|
|
|
271
|
+
const itemType = taskCount > 0 ? 'Tasks' : 'todos';
|
|
242
272
|
console.log(JSON.stringify({
|
|
243
|
-
|
|
244
|
-
reason:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Your ultrawork session is NOT complete. ${totalIncomplete} incomplete items remain.
|
|
249
|
-
|
|
250
|
-
REMEMBER THE ULTRAWORK RULES:
|
|
251
|
-
- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially
|
|
252
|
-
- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent)
|
|
253
|
-
- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each
|
|
254
|
-
- **VERIFY**: Check ALL requirements met before done
|
|
255
|
-
- **NO Premature Stopping**: ALL TODOs must be complete
|
|
273
|
+
continue: false,
|
|
274
|
+
reason: `[ULTRAWORK #${newCount}] ${totalIncomplete} incomplete ${itemType}. Continue working.\n${ultrawork.state.original_prompt ? `Task: ${ultrawork.state.original_prompt}` : ''}`
|
|
275
|
+
}));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
256
278
|
|
|
257
|
-
|
|
279
|
+
// Priority 8: Ecomode with incomplete todos/tasks
|
|
280
|
+
if (ecomode.state?.active && totalIncomplete > 0) {
|
|
281
|
+
const newCount = (ecomode.state.reinforcement_count || 0) + 1;
|
|
282
|
+
const maxReinforcements = ecomode.state.max_reinforcements || 15;
|
|
258
283
|
|
|
259
|
-
|
|
284
|
+
if (newCount > maxReinforcements) {
|
|
285
|
+
console.log(JSON.stringify({
|
|
286
|
+
continue: true,
|
|
287
|
+
reason: `[ECOMODE ESCAPE] Max reinforcements reached. Allowing stop.`
|
|
288
|
+
}));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
260
291
|
|
|
261
|
-
|
|
292
|
+
ecomode.state.reinforcement_count = newCount;
|
|
293
|
+
writeJsonFile(ecomode.path, ecomode.state);
|
|
262
294
|
|
|
263
|
-
|
|
264
|
-
|
|
295
|
+
const itemType = taskCount > 0 ? 'Tasks' : 'todos';
|
|
296
|
+
console.log(JSON.stringify({
|
|
297
|
+
continue: false,
|
|
298
|
+
reason: `[ECOMODE #${newCount}] ${totalIncomplete} incomplete ${itemType}. Continue working.`
|
|
265
299
|
}));
|
|
266
300
|
return;
|
|
267
301
|
}
|
|
268
302
|
|
|
269
|
-
// Priority
|
|
303
|
+
// Priority 9: Generic Task/Todo continuation (no specific mode)
|
|
270
304
|
if (totalIncomplete > 0) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
[SYSTEM REMINDER - CONTINUATION]
|
|
277
|
-
|
|
278
|
-
Incomplete ${itemType} remain (${totalIncomplete} remaining). Continue working on the next pending item.
|
|
279
|
-
|
|
280
|
-
- Proceed without asking for permission
|
|
281
|
-
- Mark each item complete when finished
|
|
282
|
-
- Do not stop until all items are done
|
|
305
|
+
const contFile = join(stateDir, 'continuation-count.json');
|
|
306
|
+
let contState = readJsonFile(contFile) || { count: 0 };
|
|
307
|
+
contState.count = (contState.count || 0) + 1;
|
|
308
|
+
writeJsonFile(contFile, contState);
|
|
283
309
|
|
|
284
|
-
|
|
310
|
+
if (contState.count > 15) {
|
|
311
|
+
console.log(JSON.stringify({
|
|
312
|
+
continue: true,
|
|
313
|
+
reason: `[CONTINUATION ESCAPE] Max continuations reached. Allowing stop.`
|
|
314
|
+
}));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
285
317
|
|
|
286
|
-
|
|
287
|
-
|
|
318
|
+
const itemType = taskCount > 0 ? 'Tasks' : 'todos';
|
|
319
|
+
console.log(JSON.stringify({
|
|
320
|
+
continue: false,
|
|
321
|
+
reason: `[CONTINUATION ${contState.count}/15] ${totalIncomplete} incomplete ${itemType}. Continue working.`
|
|
288
322
|
}));
|
|
289
323
|
return;
|
|
290
324
|
}
|
|
@@ -292,6 +326,8 @@ Incomplete ${itemType} remain (${totalIncomplete} remaining). Continue working o
|
|
|
292
326
|
// No blocking needed
|
|
293
327
|
console.log(JSON.stringify({ continue: true }));
|
|
294
328
|
} catch (error) {
|
|
329
|
+
// On any error, allow stop rather than blocking forever
|
|
330
|
+
console.error(`[persistent-mode] Error: ${error.message}`);
|
|
295
331
|
console.log(JSON.stringify({ continue: true }));
|
|
296
332
|
}
|
|
297
333
|
}
|