opencode-auto-loop 0.1.0 → 0.1.2
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 +5 -24
- package/package.json +1 -1
- package/skills/auto-loop/SKILL.md +14 -25
- package/src/index.ts +30 -11
package/commands/auto-loop.md
CHANGED
|
@@ -4,27 +4,12 @@ description: Start Auto Loop - auto-continues until task completion
|
|
|
4
4
|
|
|
5
5
|
# Auto Loop
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Invoke the `auto-loop` tool with the following arguments:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **task**: $ARGUMENTS
|
|
10
|
+
- **maxIterations**: 100
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
mkdir -p .opencode && cat > .opencode/auto-loop.local.md << 'EOF'
|
|
15
|
-
---
|
|
16
|
-
active: true
|
|
17
|
-
iteration: 0
|
|
18
|
-
maxIterations: 100
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
$ARGUMENTS
|
|
22
|
-
EOF
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Task
|
|
26
|
-
|
|
27
|
-
Now begin working on the task: **$ARGUMENTS**
|
|
12
|
+
After the tool confirms the loop is active, **immediately begin working on the task**. Do not just acknowledge — start doing the work right away.
|
|
28
13
|
|
|
29
14
|
## Progress Tracking
|
|
30
15
|
|
|
@@ -38,15 +23,11 @@ Before going idle, you MUST output structured progress so the plugin knows where
|
|
|
38
23
|
- [ ] What needs to be done next (in priority order)
|
|
39
24
|
```
|
|
40
25
|
|
|
41
|
-
The plugin extracts these into the state file for the next iteration's continuation prompt.
|
|
42
|
-
|
|
43
26
|
## Completion
|
|
44
27
|
|
|
45
|
-
When the task is FULLY completed, signal completion by outputting:
|
|
28
|
+
When the task is FULLY completed, signal completion by outputting the promise-DONE XML tag on its own line:
|
|
46
29
|
|
|
47
|
-
```
|
|
48
30
|
<promise>DONE</promise>
|
|
49
|
-
```
|
|
50
31
|
|
|
51
32
|
**IMPORTANT:** ONLY output this when the task is COMPLETELY and VERIFIABLY finished. Do NOT output false promises to escape the loop.
|
|
52
33
|
|
package/package.json
CHANGED
|
@@ -11,30 +11,19 @@ Start an iterative development loop that automatically continues until the task
|
|
|
11
11
|
|
|
12
12
|
The Auto Loop creates a continuous feedback cycle for completing complex tasks:
|
|
13
13
|
|
|
14
|
-
1. You
|
|
15
|
-
2.
|
|
16
|
-
3.
|
|
17
|
-
4.
|
|
14
|
+
1. You invoke the `auto-loop` tool, which creates the state file and starts the loop
|
|
15
|
+
2. You work on the task until you go idle
|
|
16
|
+
3. The plugin detects the idle state and checks for completion
|
|
17
|
+
4. If not complete, it extracts your progress and prompts you to continue
|
|
18
|
+
5. This repeats until you output the completion signal or max iterations reached
|
|
18
19
|
|
|
19
20
|
Your previous work remains accessible through files, git history, and the state file's progress sections.
|
|
20
21
|
|
|
21
22
|
## Starting the Loop
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
**Always use the `auto-loop` tool** to start the loop. Do NOT create the state file manually. The tool handles state file creation, session tracking, and initialization.
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
mkdir -p .opencode && cat > .opencode/auto-loop.local.md << 'EOF'
|
|
27
|
-
---
|
|
28
|
-
active: true
|
|
29
|
-
iteration: 0
|
|
30
|
-
maxIterations: 100
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
[The user's task prompt goes here]
|
|
34
|
-
EOF
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Then inform the user and begin working on the task.
|
|
26
|
+
After the tool confirms the loop is active, **immediately begin working on the task**. Do not just acknowledge — start doing the work.
|
|
38
27
|
|
|
39
28
|
## Progress Tracking - CRITICAL
|
|
40
29
|
|
|
@@ -61,9 +50,9 @@ Use this format in your final message of each iteration:
|
|
|
61
50
|
- Order ## Next Steps by priority — the continuation will tell you to start from the top
|
|
62
51
|
- The plugin extracts these sections and writes them into `auto-loop.local.md` for the next iteration
|
|
63
52
|
|
|
64
|
-
## Completion
|
|
53
|
+
## Completion Signal - CRITICAL RULES
|
|
65
54
|
|
|
66
|
-
When you have FULLY completed the task, signal completion by outputting:
|
|
55
|
+
When you have FULLY completed the task, signal completion by outputting the promise-DONE XML tag on its own line:
|
|
67
56
|
|
|
68
57
|
```
|
|
69
58
|
<promise>DONE</promise>
|
|
@@ -71,14 +60,14 @@ When you have FULLY completed the task, signal completion by outputting:
|
|
|
71
60
|
|
|
72
61
|
**IMPORTANT CONSTRAINTS:**
|
|
73
62
|
|
|
74
|
-
- ONLY output
|
|
75
|
-
- The
|
|
76
|
-
- Do NOT
|
|
77
|
-
- Do NOT
|
|
63
|
+
- ONLY output the completion signal when the task is COMPLETELY and VERIFIABLY finished
|
|
64
|
+
- The completion tag MUST be on its own line (not inline with other text)
|
|
65
|
+
- Do NOT mention or echo the completion tag in explanatory text — only output it as the actual signal
|
|
66
|
+
- Do NOT output false completion signals to escape the loop, even if you think you're stuck
|
|
78
67
|
- If you're blocked, explain the blocker and request help instead of falsely completing
|
|
79
68
|
|
|
80
69
|
The loop can only be stopped by:
|
|
81
|
-
1. Truthful completion
|
|
70
|
+
1. Truthful completion signal
|
|
82
71
|
2. Max iterations reached
|
|
83
72
|
3. User running `/cancel-auto-loop`
|
|
84
73
|
|
package/src/index.ts
CHANGED
|
@@ -30,7 +30,7 @@ type OpencodeClient = PluginInput["client"];
|
|
|
30
30
|
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
|
-
const COMPLETION_TAG =
|
|
33
|
+
const COMPLETION_TAG = /^\s*<promise>\s*DONE\s*<\/promise>\s*$/im;
|
|
34
34
|
const DEBOUNCE_MS = 2000;
|
|
35
35
|
|
|
36
36
|
// Get plugin root directory (ESM only — package is "type": "module")
|
|
@@ -52,11 +52,13 @@ function copyIfChanged(src: string, dest: string): void {
|
|
|
52
52
|
cpSync(src, dest, { recursive: true });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
// Auto-copy skills and commands to opencode config, updating if content changed
|
|
56
|
-
|
|
55
|
+
// Auto-copy skills and commands to opencode config, updating if content changed.
|
|
56
|
+
// Returns true if any new files were copied (first install), false otherwise.
|
|
57
|
+
function setupSkillsAndCommands(log: LogFn): boolean {
|
|
57
58
|
const pluginRoot = getPluginRoot();
|
|
58
59
|
const skillsDir = join(OPENCODE_CONFIG_DIR, "skill");
|
|
59
60
|
const commandsDir = join(OPENCODE_CONFIG_DIR, "command");
|
|
61
|
+
let newFilesCopied = false;
|
|
60
62
|
|
|
61
63
|
// Copy skills
|
|
62
64
|
const pluginSkillsDir = join(pluginRoot, "skills");
|
|
@@ -65,8 +67,10 @@ function setupSkillsAndCommands(log: LogFn): void {
|
|
|
65
67
|
for (const skill of skills) {
|
|
66
68
|
const srcFile = join(pluginSkillsDir, skill, "SKILL.md");
|
|
67
69
|
const destFile = join(skillsDir, skill, "SKILL.md");
|
|
70
|
+
const isNew = !existsSync(destFile);
|
|
68
71
|
try {
|
|
69
72
|
copyIfChanged(srcFile, destFile);
|
|
73
|
+
if (isNew && existsSync(destFile)) newFilesCopied = true;
|
|
70
74
|
} catch (err) {
|
|
71
75
|
log("warn", `Failed to copy skill '${skill}': ${err}`);
|
|
72
76
|
}
|
|
@@ -78,13 +82,18 @@ function setupSkillsAndCommands(log: LogFn): void {
|
|
|
78
82
|
if (existsSync(pluginCommandsDir)) {
|
|
79
83
|
const commands = ["auto-loop.md", "cancel-auto-loop.md", "auto-loop-help.md"];
|
|
80
84
|
for (const cmd of commands) {
|
|
85
|
+
const destCmd = join(commandsDir, cmd);
|
|
86
|
+
const isNew = !existsSync(destCmd);
|
|
81
87
|
try {
|
|
82
|
-
copyIfChanged(join(pluginCommandsDir, cmd),
|
|
88
|
+
copyIfChanged(join(pluginCommandsDir, cmd), destCmd);
|
|
89
|
+
if (isNew && existsSync(destCmd)) newFilesCopied = true;
|
|
83
90
|
} catch (err) {
|
|
84
91
|
log("warn", `Failed to copy command '${cmd}': ${err}`);
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
94
|
}
|
|
95
|
+
|
|
96
|
+
return newFilesCopied;
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
// Get state file path (project-relative)
|
|
@@ -182,9 +191,11 @@ function clearState(directory: string, log: LogFn): void {
|
|
|
182
191
|
}
|
|
183
192
|
}
|
|
184
193
|
|
|
185
|
-
// Strip markdown code fences before checking for completion tag
|
|
194
|
+
// Strip markdown code fences and inline code before checking for completion tag
|
|
186
195
|
function stripCodeFences(text: string): string {
|
|
187
|
-
return text
|
|
196
|
+
return text
|
|
197
|
+
.replace(/```[\s\S]*?```/g, "") // triple-backtick blocks
|
|
198
|
+
.replace(/`[^`]+`/g, ""); // inline backtick code
|
|
188
199
|
}
|
|
189
200
|
|
|
190
201
|
// Extract text from the last assistant message in a session
|
|
@@ -366,8 +377,12 @@ export const AutoLoopPlugin: Plugin = async (ctx) => {
|
|
|
366
377
|
}
|
|
367
378
|
};
|
|
368
379
|
|
|
369
|
-
// Auto-setup skills and commands
|
|
370
|
-
setupSkillsAndCommands(log);
|
|
380
|
+
// Auto-setup skills and commands — notify on first install
|
|
381
|
+
const isFirstInstall = setupSkillsAndCommands(log);
|
|
382
|
+
if (isFirstInstall) {
|
|
383
|
+
toast("Auto Loop installed — restart opencode to enable /auto-loop commands", "warning");
|
|
384
|
+
log("info", "First install detected — commands copied, restart needed for slash commands");
|
|
385
|
+
}
|
|
371
386
|
|
|
372
387
|
// Debounce tracking for idle events
|
|
373
388
|
let lastContinuation = 0;
|
|
@@ -403,7 +418,9 @@ export const AutoLoopPlugin: Plugin = async (ctx) => {
|
|
|
403
418
|
|
|
404
419
|
Task: ${task}
|
|
405
420
|
|
|
406
|
-
|
|
421
|
+
**Begin working on the task now.** The loop will auto-continue until you signal completion.
|
|
422
|
+
|
|
423
|
+
Before going idle each iteration, output structured progress:
|
|
407
424
|
|
|
408
425
|
\`\`\`
|
|
409
426
|
## Completed
|
|
@@ -413,7 +430,7 @@ I will auto-continue until the task is complete. Before going idle each iteratio
|
|
|
413
430
|
- [ ] What remains (in priority order)
|
|
414
431
|
\`\`\`
|
|
415
432
|
|
|
416
|
-
When
|
|
433
|
+
When the task is FULLY and VERIFIABLY complete, output the completion signal on its own line (the promise-DONE XML tag). Do NOT mention or echo the completion tag until you are truly done.
|
|
417
434
|
|
|
418
435
|
Use /cancel-auto-loop to stop early.`;
|
|
419
436
|
},
|
|
@@ -480,7 +497,9 @@ Located at: .opencode/auto-loop.local.md`;
|
|
|
480
497
|
// Fetch last assistant message (used for completion check + progress extraction)
|
|
481
498
|
const lastText = await getLastAssistantText(client, sessionId, directory, log);
|
|
482
499
|
|
|
483
|
-
|
|
500
|
+
// Skip completion check on iteration 0 (first idle after loop start)
|
|
501
|
+
// to avoid false positives from the tool's initial response text
|
|
502
|
+
if (state.iteration > 0 && lastText && checkCompletion(lastText)) {
|
|
484
503
|
clearState(directory, log);
|
|
485
504
|
log("info", `Loop completed at iteration ${state.iteration}`);
|
|
486
505
|
toast(`Auto Loop completed after ${state.iteration} iteration(s)`, "success");
|