opencode-auto-loop 0.1.4 → 0.1.6
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/commands/auto-loop.md +18 -4
- package/package.json +1 -1
- package/skills/auto-loop/SKILL.md +19 -2
- package/skills/auto-loop-help/SKILL.md +9 -0
- package/src/index.ts +151 -25
package/commands/auto-loop.md
CHANGED
|
@@ -4,26 +4,30 @@ description: "Start Auto Loop - auto-continues until task completion. Use: /auto
|
|
|
4
4
|
|
|
5
5
|
# Auto Loop
|
|
6
6
|
|
|
7
|
-
Parse `$ARGUMENTS` for the task description and
|
|
7
|
+
Parse `$ARGUMENTS` for the task description and optional flags:
|
|
8
8
|
|
|
9
9
|
- If `$ARGUMENTS` contains `--max <number>`, extract that number as **maxIterations** and remove it from the task string.
|
|
10
10
|
- Otherwise, use **maxIterations**: 25
|
|
11
|
+
- If `$ARGUMENTS` contains `--ralph`, set **forceLoop** to `true` and remove it from the task string. Force mode ignores all completion signals and runs for the full maxIterations.
|
|
11
12
|
|
|
12
13
|
Invoke the `auto-loop` tool with:
|
|
13
14
|
|
|
14
15
|
- **task**: the extracted task description
|
|
15
16
|
- **maxIterations**: the extracted or default value
|
|
17
|
+
- **forceLoop**: `true` if `--ralph` was present, omit otherwise
|
|
16
18
|
|
|
17
19
|
Examples:
|
|
18
20
|
- `/auto-loop Build a REST API` → task="Build a REST API", maxIterations=25
|
|
19
21
|
- `/auto-loop Build a REST API --max 50` → task="Build a REST API", maxIterations=50
|
|
20
22
|
- `/auto-loop --max 10 Fix all lint errors` → task="Fix all lint errors", maxIterations=10
|
|
23
|
+
- `/auto-loop --ralph Fix all lint errors` → task="Fix all lint errors", maxIterations=25, forceLoop=true
|
|
24
|
+
- `/auto-loop --ralph --max 10 Fix all lint errors` → task="Fix all lint errors", maxIterations=10, forceLoop=true
|
|
21
25
|
|
|
22
26
|
After the tool confirms the loop is active, **immediately begin working on the task**. Do not just acknowledge — start doing the work right away.
|
|
23
27
|
|
|
24
28
|
## Progress Tracking
|
|
25
29
|
|
|
26
|
-
Before going idle, you MUST output structured progress so the plugin knows where you left off:
|
|
30
|
+
Before going idle, you MUST output structured progress AND a status line so the plugin knows where you left off:
|
|
27
31
|
|
|
28
32
|
```markdown
|
|
29
33
|
## Completed
|
|
@@ -31,15 +35,25 @@ Before going idle, you MUST output structured progress so the plugin knows where
|
|
|
31
35
|
|
|
32
36
|
## Next Steps
|
|
33
37
|
- [ ] What needs to be done next (in priority order)
|
|
38
|
+
|
|
39
|
+
STATUS: IN_PROGRESS
|
|
34
40
|
```
|
|
35
41
|
|
|
36
42
|
## Completion
|
|
37
43
|
|
|
38
|
-
When the task is FULLY completed
|
|
44
|
+
When the task is FULLY completed with NO remaining next steps, signal completion:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
STATUS: COMPLETE
|
|
39
48
|
|
|
40
49
|
<promise>DONE</promise>
|
|
50
|
+
```
|
|
41
51
|
|
|
42
|
-
**IMPORTANT:**
|
|
52
|
+
**IMPORTANT:**
|
|
53
|
+
- ONLY output `STATUS: COMPLETE` and the DONE signal when the task is COMPLETELY and VERIFIABLY finished.
|
|
54
|
+
- Do NOT output the DONE signal if there are ANY unchecked items (`- [ ]`) in your Next Steps — the plugin WILL reject it.
|
|
55
|
+
- If `STATUS: IN_PROGRESS` is present alongside a DONE signal, the plugin will reject it.
|
|
56
|
+
- Do NOT output false promises to escape the loop.
|
|
43
57
|
|
|
44
58
|
## Cancellation
|
|
45
59
|
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ After the tool confirms the loop is active, **immediately begin working on the t
|
|
|
27
27
|
|
|
28
28
|
## Progress Tracking - CRITICAL
|
|
29
29
|
|
|
30
|
-
**Before going idle at the end of each work session, you MUST output structured progress sections.** The plugin parses these to persist your TODOs across iterations so you know exactly where to pick up.
|
|
30
|
+
**Before going idle at the end of each work session, you MUST output structured progress sections AND a status line.** The plugin parses these to persist your TODOs across iterations so you know exactly where to pick up.
|
|
31
31
|
|
|
32
32
|
Use this format in your final message of each iteration:
|
|
33
33
|
|
|
@@ -41,6 +41,8 @@ Use this format in your final message of each iteration:
|
|
|
41
41
|
- [ ] Add JWT authentication middleware
|
|
42
42
|
- [ ] Create registration endpoint
|
|
43
43
|
- [ ] Write integration tests
|
|
44
|
+
|
|
45
|
+
STATUS: IN_PROGRESS
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
**Rules:**
|
|
@@ -48,6 +50,7 @@ Use this format in your final message of each iteration:
|
|
|
48
50
|
- Be specific — each item should be a concrete, actionable step
|
|
49
51
|
- Only list truly completed items under ## Completed
|
|
50
52
|
- Order ## Next Steps by priority — the continuation will tell you to start from the top
|
|
53
|
+
- You MUST include a `STATUS: IN_PROGRESS` or `STATUS: COMPLETE` line on its own line in EVERY response
|
|
51
54
|
- The plugin extracts these sections and writes them into `auto-loop.local.md` for the next iteration
|
|
52
55
|
|
|
53
56
|
## Completion Signal - CRITICAL RULES
|
|
@@ -55,22 +58,36 @@ Use this format in your final message of each iteration:
|
|
|
55
58
|
When you have FULLY completed the task, signal completion by outputting the promise-DONE XML tag on its own line:
|
|
56
59
|
|
|
57
60
|
```
|
|
61
|
+
STATUS: COMPLETE
|
|
62
|
+
|
|
58
63
|
<promise>DONE</promise>
|
|
59
64
|
```
|
|
60
65
|
|
|
61
66
|
**IMPORTANT CONSTRAINTS:**
|
|
62
67
|
|
|
68
|
+
- **If your Next Steps list has ANY unchecked items (`- [ ]`), you MUST NOT output the DONE signal.** The plugin will detect the contradiction and REJECT the completion, forcing another iteration.
|
|
69
|
+
- You MUST output `STATUS: COMPLETE` (on its own line) alongside the DONE signal. If the plugin detects `STATUS: IN_PROGRESS` with a DONE signal, it will reject the completion.
|
|
63
70
|
- ONLY output the completion signal when the task is COMPLETELY and VERIFIABLY finished
|
|
64
71
|
- The completion tag MUST be on its own line (not inline with other text)
|
|
65
72
|
- Do NOT mention or echo the completion tag in explanatory text — only output it as the actual signal
|
|
66
73
|
- Do NOT output false completion signals to escape the loop, even if you think you're stuck
|
|
67
|
-
- If you're blocked,
|
|
74
|
+
- If you're blocked, output `STATUS: IN_PROGRESS` and explain the blocker instead of falsely completing
|
|
68
75
|
|
|
69
76
|
The loop can only be stopped by:
|
|
70
77
|
1. Truthful completion signal
|
|
71
78
|
2. Max iterations reached
|
|
72
79
|
3. User running `/cancel-auto-loop`
|
|
73
80
|
|
|
81
|
+
## Force Mode (--ralph)
|
|
82
|
+
|
|
83
|
+
When started with `--ralph`, the loop ignores ALL completion signals (`<promise>DONE</promise>`, `STATUS: COMPLETE`) and runs for the full iteration count. In force mode:
|
|
84
|
+
- You do NOT need to output `STATUS:` lines or the DONE signal
|
|
85
|
+
- The loop will continue for all iterations regardless
|
|
86
|
+
- Focus on making steady progress each iteration
|
|
87
|
+
- Still output `## Completed` and `## Next Steps` sections so the plugin can track progress
|
|
88
|
+
|
|
89
|
+
Example: `/auto-loop --ralph --max 10 Continue the refactoring task`
|
|
90
|
+
|
|
74
91
|
## Checking Status
|
|
75
92
|
|
|
76
93
|
Check current iteration and progress:
|
|
@@ -19,6 +19,15 @@ Example:
|
|
|
19
19
|
|
|
20
20
|
The AI will work on your task and automatically continue until completion.
|
|
21
21
|
|
|
22
|
+
### `/auto-loop <task> --ralph`
|
|
23
|
+
Force mode: ignore all completion signals and run for the full iteration count. Useful when you got interrupted and want to resume, or when you want the AI to keep iterating without stopping early.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
```
|
|
27
|
+
/auto-loop --ralph Continue the refactoring
|
|
28
|
+
/auto-loop --ralph --max 10 Fix all lint errors
|
|
29
|
+
```
|
|
30
|
+
|
|
22
31
|
### `/cancel-auto-loop`
|
|
23
32
|
Cancel an active Auto Loop before it completes.
|
|
24
33
|
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface LoopState {
|
|
|
16
16
|
iteration: number;
|
|
17
17
|
maxIterations: number;
|
|
18
18
|
debounceMs: number;
|
|
19
|
+
forceLoop?: boolean;
|
|
19
20
|
sessionId?: string;
|
|
20
21
|
prompt?: string;
|
|
21
22
|
completed?: string;
|
|
@@ -31,6 +32,8 @@ const SERVICE_NAME = "auto-loop";
|
|
|
31
32
|
const STATE_FILENAME = "auto-loop.local.md";
|
|
32
33
|
const OPENCODE_CONFIG_DIR = join(homedir(), ".config/opencode");
|
|
33
34
|
const COMPLETION_TAG = /^\s*<promise>\s*DONE\s*<\/promise>\s*$/im;
|
|
35
|
+
const STATUS_COMPLETE_TAG = /^\s*STATUS:\s*COMPLETE\s*$/im;
|
|
36
|
+
const STATUS_IN_PROGRESS_TAG = /^\s*STATUS:\s*IN_PROGRESS\s*$/im;
|
|
34
37
|
const DEFAULT_DEBOUNCE_MS = 2000;
|
|
35
38
|
const DEFAULT_MAX_ITERATIONS = 25;
|
|
36
39
|
|
|
@@ -122,6 +125,7 @@ function parseState(content: string): LoopState {
|
|
|
122
125
|
if (key === "iteration") state.iteration = parseInt(value) || 0;
|
|
123
126
|
if (key === "maxIterations") state.maxIterations = parseInt(value) || DEFAULT_MAX_ITERATIONS;
|
|
124
127
|
if (key === "debounceMs") state.debounceMs = parseInt(value) || DEFAULT_DEBOUNCE_MS;
|
|
128
|
+
if (key === "forceLoop") state.forceLoop = value === "true";
|
|
125
129
|
if (key === "sessionId") state.sessionId = value || undefined;
|
|
126
130
|
}
|
|
127
131
|
|
|
@@ -157,6 +161,7 @@ function serializeState(state: LoopState): string {
|
|
|
157
161
|
`maxIterations: ${state.maxIterations}`,
|
|
158
162
|
`debounceMs: ${state.debounceMs}`,
|
|
159
163
|
];
|
|
164
|
+
if (state.forceLoop) lines.push(`forceLoop: ${state.forceLoop}`);
|
|
160
165
|
if (state.sessionId) lines.push(`sessionId: ${state.sessionId}`);
|
|
161
166
|
lines.push("---");
|
|
162
167
|
if (state.prompt) lines.push("", state.prompt);
|
|
@@ -247,6 +252,65 @@ function checkCompletion(text: string): boolean {
|
|
|
247
252
|
return COMPLETION_TAG.test(stripCodeFences(text));
|
|
248
253
|
}
|
|
249
254
|
|
|
255
|
+
// Extract the STATUS signal presence from text.
|
|
256
|
+
function getStatusSignals(text: string): {
|
|
257
|
+
hasComplete: boolean;
|
|
258
|
+
hasInProgress: boolean;
|
|
259
|
+
} {
|
|
260
|
+
const cleaned = stripCodeFences(text);
|
|
261
|
+
return {
|
|
262
|
+
hasComplete: STATUS_COMPLETE_TAG.test(cleaned),
|
|
263
|
+
hasInProgress: STATUS_IN_PROGRESS_TAG.test(cleaned),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check if the parsed Next Steps section contains unchecked items (- [ ] ...).
|
|
268
|
+
// Returns true if there are incomplete items, meaning the task is NOT done.
|
|
269
|
+
function hasIncompleteSteps(text: string): boolean {
|
|
270
|
+
const nextSteps = extractNextSteps(stripCodeFences(text));
|
|
271
|
+
if (!nextSteps) return false;
|
|
272
|
+
|
|
273
|
+
const uncheckedItems = nextSteps
|
|
274
|
+
.split("\n")
|
|
275
|
+
.filter((line) => /^\s*-\s*\[ \]/.test(line));
|
|
276
|
+
|
|
277
|
+
return uncheckedItems.length > 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Validate whether the DONE signal should be honored.
|
|
281
|
+
// Returns { valid: true } if completion is legitimate,
|
|
282
|
+
// or { valid: false, reason: string } if it should be rejected.
|
|
283
|
+
function validateCompletion(text: string): { valid: boolean; reason?: string } {
|
|
284
|
+
// Check 1: contradictory STATUS signals
|
|
285
|
+
const statusSignals = getStatusSignals(text);
|
|
286
|
+
if (statusSignals.hasComplete && statusSignals.hasInProgress) {
|
|
287
|
+
return {
|
|
288
|
+
valid: false,
|
|
289
|
+
reason: "Both STATUS: COMPLETE and STATUS: IN_PROGRESS are present",
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Check 2: STATUS signal contradicts DONE
|
|
294
|
+
if (statusSignals.hasInProgress) {
|
|
295
|
+
return {
|
|
296
|
+
valid: false,
|
|
297
|
+
reason: "STATUS: IN_PROGRESS contradicts the DONE signal",
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Check 3: Unchecked next steps exist
|
|
302
|
+
if (hasIncompleteSteps(text)) {
|
|
303
|
+
return {
|
|
304
|
+
valid: false,
|
|
305
|
+
reason: "Unchecked next steps (- [ ] ...) found alongside DONE signal",
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Check 4: If STATUS signal is present and is COMPLETE, extra confidence
|
|
310
|
+
// If no STATUS signal at all, still allow (backward compatibility)
|
|
311
|
+
return { valid: true };
|
|
312
|
+
}
|
|
313
|
+
|
|
250
314
|
// Extract next steps / TODOs from assistant message text
|
|
251
315
|
// Looks for common patterns: ## Next Steps, ## TODO, checkbox lists, numbered lists after keywords
|
|
252
316
|
function extractNextSteps(text: string): string | undefined {
|
|
@@ -351,13 +415,24 @@ function buildProgressSection(state: LoopState): string {
|
|
|
351
415
|
// Build the loop context reminder for post-compaction injection
|
|
352
416
|
function buildLoopContextReminder(state: LoopState): string {
|
|
353
417
|
const progress = buildProgressSection(state);
|
|
354
|
-
|
|
418
|
+
const forceLabel = state.forceLoop ? " [FORCE MODE]" : "";
|
|
419
|
+
const rules = state.forceLoop
|
|
420
|
+
? `IMPORTANT RULES:
|
|
421
|
+
- Before going idle, output ## Completed and ## Next Steps sections
|
|
422
|
+
- FORCE MODE is active — the loop will continue for all ${state.maxIterations} iterations regardless of completion signals
|
|
423
|
+
- Focus on making steady progress each iteration`
|
|
424
|
+
: `IMPORTANT RULES:
|
|
425
|
+
- Before going idle, output ## Completed and ## Next Steps sections
|
|
426
|
+
- You MUST include a STATUS line: either \`STATUS: IN_PROGRESS\` or \`STATUS: COMPLETE\` on its own line
|
|
427
|
+
- Do NOT output <promise>DONE</promise> if there are ANY unchecked items (\`- [ ]\`) in your Next Steps — the plugin WILL reject it
|
|
428
|
+
- Only output \`STATUS: COMPLETE\` and the DONE signal when ALL steps are truly finished and Next Steps is empty
|
|
429
|
+
- Do NOT output false completion promises. If blocked, output \`STATUS: IN_PROGRESS\` and explain the blocker.`;
|
|
430
|
+
|
|
431
|
+
return `[AUTO LOOP${forceLabel} ACTIVE — Iteration ${state.iteration}/${state.maxIterations}]
|
|
355
432
|
|
|
356
433
|
Original task: ${state.prompt || "(no task specified)"}
|
|
357
434
|
${progress}
|
|
358
|
-
|
|
359
|
-
Before going idle, list your progress using ## Completed and ## Next Steps sections.
|
|
360
|
-
Do NOT output false completion promises. If blocked, explain the blocker.`;
|
|
435
|
+
${rules}`;
|
|
361
436
|
}
|
|
362
437
|
|
|
363
438
|
// Check if session is currently busy (not idle)
|
|
@@ -440,8 +515,12 @@ export const AutoLoopPlugin: Plugin = async (ctx) => {
|
|
|
440
515
|
.number()
|
|
441
516
|
.optional()
|
|
442
517
|
.describe("Debounce delay between iterations in ms (default: 2000)"),
|
|
518
|
+
forceLoop: tool.schema
|
|
519
|
+
.boolean()
|
|
520
|
+
.optional()
|
|
521
|
+
.describe("Force mode (--ralph): ignore completion signals and run for all iterations"),
|
|
443
522
|
},
|
|
444
|
-
async execute({ task, maxIterations = DEFAULT_MAX_ITERATIONS, debounceMs = DEFAULT_DEBOUNCE_MS }, context) {
|
|
523
|
+
async execute({ task, maxIterations = DEFAULT_MAX_ITERATIONS, debounceMs = DEFAULT_DEBOUNCE_MS, forceLoop = false }, context) {
|
|
445
524
|
if (context.abort.aborted) return "Auto Loop start was cancelled.";
|
|
446
525
|
|
|
447
526
|
const state: LoopState = {
|
|
@@ -449,6 +528,7 @@ export const AutoLoopPlugin: Plugin = async (ctx) => {
|
|
|
449
528
|
iteration: 0,
|
|
450
529
|
maxIterations,
|
|
451
530
|
debounceMs,
|
|
531
|
+
forceLoop: forceLoop ? true : undefined,
|
|
452
532
|
sessionId: context.sessionID,
|
|
453
533
|
prompt: task,
|
|
454
534
|
};
|
|
@@ -457,16 +537,21 @@ export const AutoLoopPlugin: Plugin = async (ctx) => {
|
|
|
457
537
|
continuationInFlight = false;
|
|
458
538
|
lastContinuation = 0;
|
|
459
539
|
|
|
460
|
-
|
|
461
|
-
|
|
540
|
+
const modeLabel = forceLoop ? " [FORCE MODE]" : "";
|
|
541
|
+
log("info", `Loop started${modeLabel} for session ${context.sessionID}`);
|
|
542
|
+
toast(`Auto Loop started${modeLabel} (max ${maxIterations} iterations)`, "success");
|
|
543
|
+
|
|
544
|
+
const forceNote = forceLoop
|
|
545
|
+
? `\n\n**FORCE MODE (--ralph):** Completion signals are IGNORED. The loop will run for all ${maxIterations} iterations regardless. Focus on making progress each iteration — you do NOT need to output STATUS or DONE signals.`
|
|
546
|
+
: "";
|
|
462
547
|
|
|
463
|
-
return `Auto Loop started (max ${maxIterations} iterations)
|
|
548
|
+
return `Auto Loop started (max ${maxIterations} iterations).${forceNote}
|
|
464
549
|
|
|
465
550
|
Task: ${task}
|
|
466
551
|
|
|
467
|
-
**Begin working on the task now.** The loop will auto-continue until you signal completion.
|
|
552
|
+
**Begin working on the task now.** The loop will auto-continue until ${forceLoop ? `all ${maxIterations} iterations are used` : "you signal completion"}.
|
|
468
553
|
|
|
469
|
-
Before going idle each iteration, output structured progress:
|
|
554
|
+
Before going idle each iteration, output structured progress${forceLoop ? "" : " AND a status line"}:
|
|
470
555
|
|
|
471
556
|
\`\`\`
|
|
472
557
|
## Completed
|
|
@@ -474,10 +559,18 @@ Before going idle each iteration, output structured progress:
|
|
|
474
559
|
|
|
475
560
|
## Next Steps
|
|
476
561
|
- [ ] What remains (in priority order)
|
|
562
|
+
${forceLoop ? "" : "\nSTATUS: IN_PROGRESS"}
|
|
477
563
|
\`\`\`
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
564
|
+
${forceLoop ? "" : `
|
|
565
|
+
## Completion Rules — READ CAREFULLY
|
|
566
|
+
|
|
567
|
+
1. **If your Next Steps list has ANY unchecked items (\`- [ ]\`), you MUST NOT output the DONE signal.** The plugin will reject it.
|
|
568
|
+
2. You MUST include a \`STATUS: COMPLETE\` or \`STATUS: IN_PROGRESS\` line on its own line in every response.
|
|
569
|
+
3. Only when ALL steps are done and Next Steps is empty, output:
|
|
570
|
+
- \`STATUS: COMPLETE\` on its own line
|
|
571
|
+
- The promise-DONE XML tag on its own line
|
|
572
|
+
4. If you are blocked or stuck, output \`STATUS: IN_PROGRESS\` and explain the blocker. Do NOT output a false DONE.
|
|
573
|
+
`}
|
|
481
574
|
Use /cancel-auto-loop to stop early.`;
|
|
482
575
|
},
|
|
483
576
|
}),
|
|
@@ -512,6 +605,8 @@ Use /cancel-auto-loop to stop early.`;
|
|
|
512
605
|
|
|
513
606
|
- \`/auto-loop <task>\` - Start an auto-continuation loop (default: 25 iterations)
|
|
514
607
|
- \`/auto-loop <task> --max <n>\` - Start with a custom iteration limit
|
|
608
|
+
- \`/auto-loop <task> --ralph\` - Force mode: ignore completion signals, run all iterations
|
|
609
|
+
- \`/auto-loop <task> --ralph --max <n>\` - Force mode with custom limit
|
|
515
610
|
- \`/cancel-auto-loop\` - Stop an active loop
|
|
516
611
|
- \`/auto-loop-help\` - Show this help
|
|
517
612
|
|
|
@@ -519,6 +614,8 @@ Use /cancel-auto-loop to stop early.`;
|
|
|
519
614
|
|
|
520
615
|
- \`/auto-loop Build a REST API\` — runs up to 25 iterations
|
|
521
616
|
- \`/auto-loop Fix all lint errors --max 10\` — runs up to 10 iterations
|
|
617
|
+
- \`/auto-loop --ralph Continue refactoring\` — force runs all 25 iterations, ignores DONE signals
|
|
618
|
+
- \`/auto-loop --ralph --max 50 Big migration\` — force runs all 50 iterations
|
|
522
619
|
|
|
523
620
|
## How It Works
|
|
524
621
|
|
|
@@ -527,6 +624,13 @@ Use /cancel-auto-loop to stop early.`;
|
|
|
527
624
|
3. Plugin auto-continues if not complete
|
|
528
625
|
4. Loop stops when AI outputs: <promise>DONE</promise>
|
|
529
626
|
|
|
627
|
+
## Force Mode (--ralph)
|
|
628
|
+
|
|
629
|
+
When \`--ralph\` is used, the loop ignores ALL completion signals and runs for the full iteration count. Useful when:
|
|
630
|
+
- You got interrupted and want to continue no matter what
|
|
631
|
+
- You want the AI to keep iterating and improving
|
|
632
|
+
- You don't want the AI to stop early
|
|
633
|
+
|
|
530
634
|
## State File
|
|
531
635
|
|
|
532
636
|
Located at: .opencode/auto-loop.local.md`;
|
|
@@ -559,12 +663,23 @@ Located at: .opencode/auto-loop.local.md`;
|
|
|
559
663
|
const lastText = await getLastAssistantText(client, sessionId, directory, log);
|
|
560
664
|
|
|
561
665
|
// Skip completion check on iteration 0 (first idle after loop start)
|
|
562
|
-
// to avoid false positives from the tool's initial response text
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
666
|
+
// to avoid false positives from the tool's initial response text.
|
|
667
|
+
// Also skip entirely when forceLoop is true — force mode ignores
|
|
668
|
+
// all completion signals and runs until max iterations.
|
|
669
|
+
if (!state.forceLoop && state.iteration > 0 && lastText && checkCompletion(lastText)) {
|
|
670
|
+
// Validate the DONE signal — reject if there are unchecked steps
|
|
671
|
+
// or if the STATUS signal contradicts completion
|
|
672
|
+
const validation = validateCompletion(lastText);
|
|
673
|
+
if (validation.valid) {
|
|
674
|
+
await clearState(directory, log);
|
|
675
|
+
log("info", `Loop completed at iteration ${state.iteration}`);
|
|
676
|
+
toast(`Auto Loop completed after ${state.iteration} iteration(s)`, "success");
|
|
677
|
+
return;
|
|
678
|
+
} else {
|
|
679
|
+
log("warn", `Rejected premature DONE signal: ${validation.reason}`);
|
|
680
|
+
toast(`Auto Loop: DONE rejected — ${validation.reason}`, "warning");
|
|
681
|
+
// Fall through to send another continuation prompt
|
|
682
|
+
}
|
|
568
683
|
}
|
|
569
684
|
|
|
570
685
|
if (state.iteration >= state.maxIterations) {
|
|
@@ -591,15 +706,26 @@ Located at: .opencode/auto-loop.local.md`;
|
|
|
591
706
|
// Build continuation prompt with progress context
|
|
592
707
|
const progressSection = buildProgressSection(newState);
|
|
593
708
|
|
|
594
|
-
const
|
|
709
|
+
const forceLabel = state.forceLoop ? " [FORCE MODE]" : "";
|
|
710
|
+
const importantRules = state.forceLoop
|
|
711
|
+
? `IMPORTANT:
|
|
712
|
+
- Pick up from the next incomplete step below
|
|
713
|
+
- Before going idle, list your progress using ## Completed and ## Next Steps sections
|
|
714
|
+
- FORCE MODE is active — the loop will continue for all ${newState.maxIterations} iterations regardless of completion signals
|
|
715
|
+
- Focus on making steady progress each iteration`
|
|
716
|
+
: `IMPORTANT:
|
|
717
|
+
- Pick up from the next incomplete step below
|
|
718
|
+
- Before going idle, list your progress using ## Completed and ## Next Steps sections
|
|
719
|
+
- You MUST include a STATUS line: either \`STATUS: IN_PROGRESS\` or \`STATUS: COMPLETE\` on its own line
|
|
720
|
+
- Do NOT output <promise>DONE</promise> if there are ANY unchecked items (\`- [ ]\`) in your Next Steps — the plugin WILL reject it
|
|
721
|
+
- Only output \`STATUS: COMPLETE\` and the DONE signal when ALL steps are truly finished and Next Steps is empty
|
|
722
|
+
- Do not stop until the task is truly done`;
|
|
723
|
+
|
|
724
|
+
const continuationPrompt = `[AUTO LOOP${forceLabel} — ITERATION ${newState.iteration}/${newState.maxIterations}]
|
|
595
725
|
|
|
596
726
|
Continue working on the task. Do NOT repeat work that is already done.
|
|
597
727
|
${progressSection}
|
|
598
|
-
|
|
599
|
-
- Pick up from the next incomplete step below
|
|
600
|
-
- When FULLY complete, output: <promise>DONE</promise>
|
|
601
|
-
- Before going idle, list your progress using ## Completed and ## Next Steps sections
|
|
602
|
-
- Do not stop until the task is truly done
|
|
728
|
+
${importantRules}
|
|
603
729
|
|
|
604
730
|
Original task:
|
|
605
731
|
${state.prompt || "(no task specified)"}`;
|