opencode-auto-loop 0.1.3 → 0.1.5
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 +2 -2
- package/commands/auto-loop.md +13 -3
- package/package.json +1 -1
- package/skills/auto-loop/SKILL.md +9 -2
- package/skills/auto-loop-help/SKILL.md +1 -1
- package/src/index.ts +94 -10
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ The AI will work on your task and automatically continue until completion.
|
|
|
43
43
|
1. `/auto-loop` creates a state file at `.opencode/auto-loop.local.md`
|
|
44
44
|
2. When the AI goes idle, the plugin checks if `<promise>DONE</promise>` was output
|
|
45
45
|
3. If not found, it extracts progress (## Completed / ## Next Steps) and injects a continuation prompt
|
|
46
|
-
4. Loop continues until DONE is found or max iterations (
|
|
46
|
+
4. Loop continues until DONE is found or max iterations (25) reached
|
|
47
47
|
5. State file is deleted when complete
|
|
48
48
|
6. Loop context survives session compaction
|
|
49
49
|
|
|
@@ -75,7 +75,7 @@ Format (markdown with YAML frontmatter):
|
|
|
75
75
|
---
|
|
76
76
|
active: true
|
|
77
77
|
iteration: 3
|
|
78
|
-
maxIterations:
|
|
78
|
+
maxIterations: 25
|
|
79
79
|
sessionId: ses_abc123
|
|
80
80
|
---
|
|
81
81
|
|
package/commands/auto-loop.md
CHANGED
|
@@ -23,7 +23,7 @@ After the tool confirms the loop is active, **immediately begin working on the t
|
|
|
23
23
|
|
|
24
24
|
## Progress Tracking
|
|
25
25
|
|
|
26
|
-
Before going idle, you MUST output structured progress so the plugin knows where you left off:
|
|
26
|
+
Before going idle, you MUST output structured progress AND a status line so the plugin knows where you left off:
|
|
27
27
|
|
|
28
28
|
```markdown
|
|
29
29
|
## Completed
|
|
@@ -31,15 +31,25 @@ Before going idle, you MUST output structured progress so the plugin knows where
|
|
|
31
31
|
|
|
32
32
|
## Next Steps
|
|
33
33
|
- [ ] What needs to be done next (in priority order)
|
|
34
|
+
|
|
35
|
+
STATUS: IN_PROGRESS
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
## Completion
|
|
37
39
|
|
|
38
|
-
When the task is FULLY completed
|
|
40
|
+
When the task is FULLY completed with NO remaining next steps, signal completion:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
STATUS: COMPLETE
|
|
39
44
|
|
|
40
45
|
<promise>DONE</promise>
|
|
46
|
+
```
|
|
41
47
|
|
|
42
|
-
**IMPORTANT:**
|
|
48
|
+
**IMPORTANT:**
|
|
49
|
+
- ONLY output `STATUS: COMPLETE` and the DONE signal when the task is COMPLETELY and VERIFIABLY finished.
|
|
50
|
+
- Do NOT output the DONE signal if there are ANY unchecked items (`- [ ]`) in your Next Steps — the plugin WILL reject it.
|
|
51
|
+
- If `STATUS: IN_PROGRESS` is present alongside a DONE signal, the plugin will reject it.
|
|
52
|
+
- Do NOT output false promises to escape the loop.
|
|
43
53
|
|
|
44
54
|
## Cancellation
|
|
45
55
|
|
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,16 +58,20 @@ 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
|
package/src/index.ts
CHANGED
|
@@ -31,6 +31,8 @@ const SERVICE_NAME = "auto-loop";
|
|
|
31
31
|
const STATE_FILENAME = "auto-loop.local.md";
|
|
32
32
|
const OPENCODE_CONFIG_DIR = join(homedir(), ".config/opencode");
|
|
33
33
|
const COMPLETION_TAG = /^\s*<promise>\s*DONE\s*<\/promise>\s*$/im;
|
|
34
|
+
const STATUS_COMPLETE_TAG = /^\s*STATUS:\s*COMPLETE\s*$/im;
|
|
35
|
+
const STATUS_IN_PROGRESS_TAG = /^\s*STATUS:\s*IN_PROGRESS\s*$/im;
|
|
34
36
|
const DEFAULT_DEBOUNCE_MS = 2000;
|
|
35
37
|
const DEFAULT_MAX_ITERATIONS = 25;
|
|
36
38
|
|
|
@@ -247,6 +249,65 @@ function checkCompletion(text: string): boolean {
|
|
|
247
249
|
return COMPLETION_TAG.test(stripCodeFences(text));
|
|
248
250
|
}
|
|
249
251
|
|
|
252
|
+
// Extract the STATUS signal presence from text.
|
|
253
|
+
function getStatusSignals(text: string): {
|
|
254
|
+
hasComplete: boolean;
|
|
255
|
+
hasInProgress: boolean;
|
|
256
|
+
} {
|
|
257
|
+
const cleaned = stripCodeFences(text);
|
|
258
|
+
return {
|
|
259
|
+
hasComplete: STATUS_COMPLETE_TAG.test(cleaned),
|
|
260
|
+
hasInProgress: STATUS_IN_PROGRESS_TAG.test(cleaned),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check if the parsed Next Steps section contains unchecked items (- [ ] ...).
|
|
265
|
+
// Returns true if there are incomplete items, meaning the task is NOT done.
|
|
266
|
+
function hasIncompleteSteps(text: string): boolean {
|
|
267
|
+
const nextSteps = extractNextSteps(stripCodeFences(text));
|
|
268
|
+
if (!nextSteps) return false;
|
|
269
|
+
|
|
270
|
+
const uncheckedItems = nextSteps
|
|
271
|
+
.split("\n")
|
|
272
|
+
.filter((line) => /^\s*-\s*\[ \]/.test(line));
|
|
273
|
+
|
|
274
|
+
return uncheckedItems.length > 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Validate whether the DONE signal should be honored.
|
|
278
|
+
// Returns { valid: true } if completion is legitimate,
|
|
279
|
+
// or { valid: false, reason: string } if it should be rejected.
|
|
280
|
+
function validateCompletion(text: string): { valid: boolean; reason?: string } {
|
|
281
|
+
// Check 1: contradictory STATUS signals
|
|
282
|
+
const statusSignals = getStatusSignals(text);
|
|
283
|
+
if (statusSignals.hasComplete && statusSignals.hasInProgress) {
|
|
284
|
+
return {
|
|
285
|
+
valid: false,
|
|
286
|
+
reason: "Both STATUS: COMPLETE and STATUS: IN_PROGRESS are present",
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Check 2: STATUS signal contradicts DONE
|
|
291
|
+
if (statusSignals.hasInProgress) {
|
|
292
|
+
return {
|
|
293
|
+
valid: false,
|
|
294
|
+
reason: "STATUS: IN_PROGRESS contradicts the DONE signal",
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Check 3: Unchecked next steps exist
|
|
299
|
+
if (hasIncompleteSteps(text)) {
|
|
300
|
+
return {
|
|
301
|
+
valid: false,
|
|
302
|
+
reason: "Unchecked next steps (- [ ] ...) found alongside DONE signal",
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Check 4: If STATUS signal is present and is COMPLETE, extra confidence
|
|
307
|
+
// If no STATUS signal at all, still allow (backward compatibility)
|
|
308
|
+
return { valid: true };
|
|
309
|
+
}
|
|
310
|
+
|
|
250
311
|
// Extract next steps / TODOs from assistant message text
|
|
251
312
|
// Looks for common patterns: ## Next Steps, ## TODO, checkbox lists, numbered lists after keywords
|
|
252
313
|
function extractNextSteps(text: string): string | undefined {
|
|
@@ -355,9 +416,12 @@ function buildLoopContextReminder(state: LoopState): string {
|
|
|
355
416
|
|
|
356
417
|
Original task: ${state.prompt || "(no task specified)"}
|
|
357
418
|
${progress}
|
|
358
|
-
|
|
359
|
-
Before going idle,
|
|
360
|
-
|
|
419
|
+
IMPORTANT RULES:
|
|
420
|
+
- Before going idle, output ## Completed and ## Next Steps sections
|
|
421
|
+
- You MUST include a STATUS line: either \`STATUS: IN_PROGRESS\` or \`STATUS: COMPLETE\` on its own line
|
|
422
|
+
- Do NOT output <promise>DONE</promise> if there are ANY unchecked items (\`- [ ]\`) in your Next Steps — the plugin WILL reject it
|
|
423
|
+
- Only output \`STATUS: COMPLETE\` and the DONE signal when ALL steps are truly finished and Next Steps is empty
|
|
424
|
+
- Do NOT output false completion promises. If blocked, output \`STATUS: IN_PROGRESS\` and explain the blocker.`;
|
|
361
425
|
}
|
|
362
426
|
|
|
363
427
|
// Check if session is currently busy (not idle)
|
|
@@ -466,7 +530,7 @@ Task: ${task}
|
|
|
466
530
|
|
|
467
531
|
**Begin working on the task now.** The loop will auto-continue until you signal completion.
|
|
468
532
|
|
|
469
|
-
Before going idle each iteration, output structured progress:
|
|
533
|
+
Before going idle each iteration, output structured progress AND a status line:
|
|
470
534
|
|
|
471
535
|
\`\`\`
|
|
472
536
|
## Completed
|
|
@@ -474,9 +538,18 @@ Before going idle each iteration, output structured progress:
|
|
|
474
538
|
|
|
475
539
|
## Next Steps
|
|
476
540
|
- [ ] What remains (in priority order)
|
|
541
|
+
|
|
542
|
+
STATUS: IN_PROGRESS
|
|
477
543
|
\`\`\`
|
|
478
544
|
|
|
479
|
-
|
|
545
|
+
## Completion Rules — READ CAREFULLY
|
|
546
|
+
|
|
547
|
+
1. **If your Next Steps list has ANY unchecked items (\`- [ ]\`), you MUST NOT output the DONE signal.** The plugin will reject it.
|
|
548
|
+
2. You MUST include a \`STATUS: COMPLETE\` or \`STATUS: IN_PROGRESS\` line on its own line in every response.
|
|
549
|
+
3. Only when ALL steps are done and Next Steps is empty, output:
|
|
550
|
+
- \`STATUS: COMPLETE\` on its own line
|
|
551
|
+
- The promise-DONE XML tag on its own line
|
|
552
|
+
4. If you are blocked or stuck, output \`STATUS: IN_PROGRESS\` and explain the blocker. Do NOT output a false DONE.
|
|
480
553
|
|
|
481
554
|
Use /cancel-auto-loop to stop early.`;
|
|
482
555
|
},
|
|
@@ -561,10 +634,19 @@ Located at: .opencode/auto-loop.local.md`;
|
|
|
561
634
|
// Skip completion check on iteration 0 (first idle after loop start)
|
|
562
635
|
// to avoid false positives from the tool's initial response text
|
|
563
636
|
if (state.iteration > 0 && lastText && checkCompletion(lastText)) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
637
|
+
// Validate the DONE signal — reject if there are unchecked steps
|
|
638
|
+
// or if the STATUS signal contradicts completion
|
|
639
|
+
const validation = validateCompletion(lastText);
|
|
640
|
+
if (validation.valid) {
|
|
641
|
+
await clearState(directory, log);
|
|
642
|
+
log("info", `Loop completed at iteration ${state.iteration}`);
|
|
643
|
+
toast(`Auto Loop completed after ${state.iteration} iteration(s)`, "success");
|
|
644
|
+
return;
|
|
645
|
+
} else {
|
|
646
|
+
log("warn", `Rejected premature DONE signal: ${validation.reason}`);
|
|
647
|
+
toast(`Auto Loop: DONE rejected — ${validation.reason}`, "warning");
|
|
648
|
+
// Fall through to send another continuation prompt
|
|
649
|
+
}
|
|
568
650
|
}
|
|
569
651
|
|
|
570
652
|
if (state.iteration >= state.maxIterations) {
|
|
@@ -597,8 +679,10 @@ Continue working on the task. Do NOT repeat work that is already done.
|
|
|
597
679
|
${progressSection}
|
|
598
680
|
IMPORTANT:
|
|
599
681
|
- Pick up from the next incomplete step below
|
|
600
|
-
- When FULLY complete, output: <promise>DONE</promise>
|
|
601
682
|
- Before going idle, list your progress using ## Completed and ## Next Steps sections
|
|
683
|
+
- You MUST include a STATUS line: either \`STATUS: IN_PROGRESS\` or \`STATUS: COMPLETE\` on its own line
|
|
684
|
+
- Do NOT output <promise>DONE</promise> if there are ANY unchecked items (\`- [ ]\`) in your Next Steps — the plugin WILL reject it
|
|
685
|
+
- Only output \`STATUS: COMPLETE\` and the DONE signal when ALL steps are truly finished and Next Steps is empty
|
|
602
686
|
- Do not stop until the task is truly done
|
|
603
687
|
|
|
604
688
|
Original task:
|