@mobrienv/autoloop 0.7.4 → 0.9.1
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 +24 -9
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/testing/mock-acp-server.d.ts +21 -0
- package/dist/testing/mock-acp-server.js +139 -0
- package/dist/testing/mock-acp-server.js.map +1 -0
- package/dist/testing/mock-backend.js +15 -1
- package/dist/testing/mock-backend.js.map +1 -1
- package/package.json +10 -7
- package/plugins/autoloop/.claude-plugin/plugin.json +3 -3
- package/plugins/autoloop/skills/autoloop/SKILL.md +2 -2
- package/plugins/autoloop/skills/autoloop-acp/SKILL.md +118 -0
package/README.md
CHANGED
|
@@ -59,6 +59,9 @@ If Ralph is the broader experimentation surface, autoloop is the cleaner runtime
|
|
|
59
59
|
- **Designed for agents too** -- autoloop is intentionally shaped as a surface that agents can use directly, not just a human operator CLI
|
|
60
60
|
- **Dynamic chains** -- compose presets into multi-stage pipelines
|
|
61
61
|
- **Persistent memory + profiles** -- loops accumulate learnings across runs and can load repo/user tuning profiles
|
|
62
|
+
- **Cost budgets + stall protection** -- stop runaway loops automatically with `event_loop.max_cost_usd` (journaled token cost) and `event_loop.stall_iterations` (repeated identical outputs)
|
|
63
|
+
- **Runtime budgets** -- bound wall-clock time with `event_loop.max_runtime` (whole loop) and `event_loop.max_iteration_runtime` (per iteration, for iterations that wait days on long-running workflows); both take `"3d"`/`"12h"`-style durations
|
|
64
|
+
- **Finish notifications** -- run any command when a loop ends (`[notify] command = "..."`) with run metadata in env vars and a JSON payload on stdin
|
|
62
65
|
|
|
63
66
|
## What autoloop is
|
|
64
67
|
|
|
@@ -140,23 +143,34 @@ The main commands are:
|
|
|
140
143
|
|
|
141
144
|
```
|
|
142
145
|
autoloop run <preset-name|preset-dir> [prompt...] [flags]
|
|
143
|
-
autoloop
|
|
144
|
-
autoloop
|
|
145
|
-
autoloop loops
|
|
146
|
-
autoloop loops
|
|
146
|
+
autoloop init [--preset <name>] [dir]
|
|
147
|
+
autoloop list [--json]
|
|
148
|
+
autoloop loops [--all] [--json]
|
|
149
|
+
autoloop loops show <run-id> [--json]
|
|
150
|
+
autoloop loops artifacts <run-id> [--json]
|
|
147
151
|
autoloop loops watch <run-id>
|
|
152
|
+
autoloop loops health [--verbose] [--json]
|
|
148
153
|
autoloop inspect <artifact> [selector] [project-dir] [--format <md|terminal|text|json|csv|graph>]
|
|
149
154
|
autoloop dashboard [--port <port>]
|
|
150
|
-
autoloop worktree <list|show|merge|clean> [args]
|
|
151
|
-
autoloop chain
|
|
155
|
+
autoloop worktree <list|show|diff|merge|clean> [args]
|
|
156
|
+
autoloop chain list [project-dir]
|
|
157
|
+
autoloop chain run <name> [project-dir] [prompt...] [--dry-run] [--json]
|
|
152
158
|
autoloop emit <topic> [summary]
|
|
153
|
-
autoloop memory <list|status|find|add|remove> [args]
|
|
159
|
+
autoloop memory <list|status|find|add|remove|compact|prune> [args]
|
|
154
160
|
autoloop task <add|complete|update|remove|list> [args]
|
|
155
161
|
autoloop runs clean [--max-age <days>]
|
|
162
|
+
autoloop stats [project-dir] [--json]
|
|
163
|
+
autoloop doctor [project-dir] [--json]
|
|
164
|
+
autoloop triage [project-dir] [--json]
|
|
156
165
|
autoloop config <show|set|unset|path> [args]
|
|
166
|
+
autoloop capabilities
|
|
167
|
+
autoloop robot-docs
|
|
168
|
+
autoloop --version
|
|
157
169
|
```
|
|
158
170
|
|
|
159
|
-
Use `run` to start work, `loops` and `inspect` to understand what happened, and `dashboard` when you want a local operator surface.
|
|
171
|
+
Use `run` to start work, `loops` and `inspect` to understand what happened, and `dashboard` when you want a local operator surface. `stats` summarizes success rates and cost per preset; `doctor` diagnoses the environment and `.autoloop` state. `triage` is the one-call status command for agents — runs, health, doctor, and recommended next commands in a single (optionally `--json`) invocation. `capabilities` prints the machine-readable CLI contract (commands, JSON surfaces, exit codes) and `robot-docs` prints a paste-ready agent handbook.
|
|
172
|
+
|
|
173
|
+
Errors always go to stderr with a documented exit code (0 success, 1 user-input error, 2 environment error), and mistyped commands, subcommands, and flags get a "Did you mean" correction instead of being misinterpreted.
|
|
160
174
|
|
|
161
175
|
### Flags
|
|
162
176
|
|
|
@@ -164,7 +178,7 @@ Use `run` to start work, `loops` and `inspect` to understand what happened, and
|
|
|
164
178
|
|------|-------------|
|
|
165
179
|
| `-h`, `--help` | Show usage |
|
|
166
180
|
| `-v`, `--verbose` | Debug-level logging |
|
|
167
|
-
| `-b`, `--backend` | Override backend
|
|
181
|
+
| `-b`, `--backend` | Override backend (e.g. `-b claude-sdk`, `-b pi`, `-b kiro`, or a command) |
|
|
168
182
|
| `-p`, `--preset` | Resolve a named or custom preset |
|
|
169
183
|
| `--chain` | Run an inline chain of comma-separated presets |
|
|
170
184
|
| `--profile <spec>` | Activate a profile (`repo:<name>` or `user:<name>`), repeatable |
|
|
@@ -219,6 +233,7 @@ Bundled presets give you opinionated starting points for common workflows like c
|
|
|
219
233
|
| `autospec` | Research, clarify, design, plan, and critique specifications |
|
|
220
234
|
| `automerge` | Merge a completed worktree branch back into its base branch |
|
|
221
235
|
| `autopr` | Turn the current branch into a reviewable pull request |
|
|
236
|
+
| `autowiki` | Ingest a queue of source URLs into an OKF-conformant LLM wiki of cross-linked concept pages |
|
|
222
237
|
|
|
223
238
|
## Creating custom presets
|
|
224
239
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { type Config, defaults as configDefaults, get as configGet, getInt as co
|
|
|
3
3
|
export { emit, run, runParallelBranchCli } from "@mobrienv/autoloop-harness";
|
|
4
4
|
export type { EmitResult } from "@mobrienv/autoloop-harness/emit";
|
|
5
5
|
export type { LoopEvent, LoopEventEmitter, } from "@mobrienv/autoloop-harness/events";
|
|
6
|
-
export type { LoopContext, RunOptions, RunSummary, TriggerSource, Verdict, VerdictKind, } from "@mobrienv/autoloop-harness/types";
|
|
6
|
+
export type { LoopContext, RunOptions, RunSummary, StopReason, TriggerSource, Verdict, VerdictKind, } from "@mobrienv/autoloop-harness/types";
|
|
7
|
+
export { STOP_REASONS } from "@mobrienv/autoloop-harness/types";
|
package/dist/index.js
CHANGED
|
@@ -16,4 +16,5 @@ export { loadProject as loadProjectConfig } from "@mobrienv/autoloop-core/config
|
|
|
16
16
|
// without re-implementing the schema layer.
|
|
17
17
|
export { defaults as configDefaults, get as configGet, getInt as configGetInt, getList as configGetList, parseToml as parseConfigToml, } from "@mobrienv/autoloop-core/config-schema";
|
|
18
18
|
export { emit, run, runParallelBranchCli } from "@mobrienv/autoloop-harness";
|
|
19
|
+
export { STOP_REASONS } from "@mobrienv/autoloop-harness/types";
|
|
19
20
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,EAAE;AACF,SAAS;AACT,8DAA8D;AAC9D,EAAE;AACF,gEAAgE;AAChE,iCAAiC;AACjC,sDAAsD;AACtD,QAAQ;AAER,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAClF,uEAAuE;AACvE,4CAA4C;AAC5C,OAAO,EAEL,QAAQ,IAAI,cAAc,EAC1B,GAAG,IAAI,SAAS,EAChB,MAAM,IAAI,YAAY,EACtB,OAAO,IAAI,aAAa,EAExB,SAAS,IAAI,eAAe,GAC7B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,EAAE;AACF,SAAS;AACT,8DAA8D;AAC9D,EAAE;AACF,gEAAgE;AAChE,iCAAiC;AACjC,sDAAsD;AACtD,QAAQ;AAER,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAClF,uEAAuE;AACvE,4CAA4C;AAC5C,OAAO,EAEL,QAAQ,IAAI,cAAc,EAC1B,GAAG,IAAI,SAAS,EAChB,MAAM,IAAI,YAAY,EACtB,OAAO,IAAI,aAAa,EAExB,SAAS,IAAI,eAAe,GAC7B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAe7E,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic mock ACP stdio server for autoloop integration tests.
|
|
4
|
+
*
|
|
5
|
+
* Speaks newline-delimited JSON-RPC 2.0 on stdin/stdout, implementing the
|
|
6
|
+
* minimal agent surface the autoloop ACP client exercises:
|
|
7
|
+
*
|
|
8
|
+
* initialize → echoes the requested protocol version
|
|
9
|
+
* session/new → returns a fixed session id
|
|
10
|
+
* session/set_mode → records the mode id
|
|
11
|
+
* session/set_model → records the model id
|
|
12
|
+
* session/prompt → requests tool permission from the client, streams an
|
|
13
|
+
* agent_message_chunk that reports the recorded
|
|
14
|
+
* mode/model/permission outcome, then ends the turn
|
|
15
|
+
*
|
|
16
|
+
* Environment knobs:
|
|
17
|
+
* MOCK_ACP_RESPONSE extra response text (defaults to "LOOP_COMPLETE")
|
|
18
|
+
* MOCK_ACP_KIRO_NOISE when "1", emits a provider-private `_kiro.dev/...`
|
|
19
|
+
* notification that ACP clients must filter out
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic mock ACP stdio server for autoloop integration tests.
|
|
4
|
+
*
|
|
5
|
+
* Speaks newline-delimited JSON-RPC 2.0 on stdin/stdout, implementing the
|
|
6
|
+
* minimal agent surface the autoloop ACP client exercises:
|
|
7
|
+
*
|
|
8
|
+
* initialize → echoes the requested protocol version
|
|
9
|
+
* session/new → returns a fixed session id
|
|
10
|
+
* session/set_mode → records the mode id
|
|
11
|
+
* session/set_model → records the model id
|
|
12
|
+
* session/prompt → requests tool permission from the client, streams an
|
|
13
|
+
* agent_message_chunk that reports the recorded
|
|
14
|
+
* mode/model/permission outcome, then ends the turn
|
|
15
|
+
*
|
|
16
|
+
* Environment knobs:
|
|
17
|
+
* MOCK_ACP_RESPONSE extra response text (defaults to "LOOP_COMPLETE")
|
|
18
|
+
* MOCK_ACP_KIRO_NOISE when "1", emits a provider-private `_kiro.dev/...`
|
|
19
|
+
* notification that ACP clients must filter out
|
|
20
|
+
*/
|
|
21
|
+
let modeId = "none";
|
|
22
|
+
let modelId = "none";
|
|
23
|
+
let nextOutboundId = 1000;
|
|
24
|
+
const pendingOutbound = new Map();
|
|
25
|
+
function send(message) {
|
|
26
|
+
process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", ...message })}\n`);
|
|
27
|
+
}
|
|
28
|
+
function respond(id, result) {
|
|
29
|
+
send({ id, result });
|
|
30
|
+
}
|
|
31
|
+
function notify(method, params) {
|
|
32
|
+
send({ method, params });
|
|
33
|
+
}
|
|
34
|
+
function request(method, params) {
|
|
35
|
+
const id = nextOutboundId++;
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
pendingOutbound.set(id, resolve);
|
|
38
|
+
send({ id, method, params });
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function handlePrompt(msg) {
|
|
42
|
+
const params = msg.params;
|
|
43
|
+
const permission = (await request("session/request_permission", {
|
|
44
|
+
sessionId: params.sessionId,
|
|
45
|
+
toolCall: {
|
|
46
|
+
toolCallId: "mock-tool-1",
|
|
47
|
+
title: "mock tool",
|
|
48
|
+
kind: "execute",
|
|
49
|
+
status: "pending",
|
|
50
|
+
},
|
|
51
|
+
options: [
|
|
52
|
+
{ optionId: "allow", name: "Allow always", kind: "allow_always" },
|
|
53
|
+
{ optionId: "reject", name: "Reject", kind: "reject_once" },
|
|
54
|
+
],
|
|
55
|
+
}));
|
|
56
|
+
const outcome = permission?.outcome?.outcome ?? "none";
|
|
57
|
+
const optionId = permission?.outcome?.optionId ?? "none";
|
|
58
|
+
if (process.env.MOCK_ACP_KIRO_NOISE === "1") {
|
|
59
|
+
notify("_kiro.dev/heartbeat", { noise: true });
|
|
60
|
+
}
|
|
61
|
+
const extra = process.env.MOCK_ACP_RESPONSE ?? "LOOP_COMPLETE";
|
|
62
|
+
const text = `mock-acp mode=${modeId} model=${modelId} permission=${outcome}:${optionId}\n${extra}`;
|
|
63
|
+
notify("session/update", {
|
|
64
|
+
sessionId: params.sessionId,
|
|
65
|
+
update: {
|
|
66
|
+
sessionUpdate: "agent_message_chunk",
|
|
67
|
+
content: { type: "text", text },
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
if (msg.id !== undefined)
|
|
71
|
+
respond(msg.id, { stopReason: "end_turn" });
|
|
72
|
+
}
|
|
73
|
+
function handleMessage(msg) {
|
|
74
|
+
// Response to one of our outbound requests (permission flow)
|
|
75
|
+
if (msg.id !== undefined && msg.method === undefined) {
|
|
76
|
+
const resolve = pendingOutbound.get(msg.id);
|
|
77
|
+
if (resolve) {
|
|
78
|
+
pendingOutbound.delete(msg.id);
|
|
79
|
+
resolve(msg.result);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
switch (msg.method) {
|
|
84
|
+
case "initialize": {
|
|
85
|
+
const params = msg.params;
|
|
86
|
+
if (msg.id !== undefined)
|
|
87
|
+
respond(msg.id, {
|
|
88
|
+
protocolVersion: params?.protocolVersion ?? 1,
|
|
89
|
+
agentCapabilities: {},
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
case "session/new":
|
|
94
|
+
if (msg.id !== undefined)
|
|
95
|
+
respond(msg.id, { sessionId: "mock-acp-session" });
|
|
96
|
+
return;
|
|
97
|
+
case "session/set_mode": {
|
|
98
|
+
modeId = String(msg.params?.modeId ?? "none");
|
|
99
|
+
if (msg.id !== undefined)
|
|
100
|
+
respond(msg.id, {});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
case "session/set_model": {
|
|
104
|
+
modelId = String(msg.params?.modelId ?? "none");
|
|
105
|
+
if (msg.id !== undefined)
|
|
106
|
+
respond(msg.id, {});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
case "session/prompt":
|
|
110
|
+
void handlePrompt(msg);
|
|
111
|
+
return;
|
|
112
|
+
default:
|
|
113
|
+
// Unknown request → empty result; notifications (e.g. session/cancel) ignored
|
|
114
|
+
if (msg.id !== undefined)
|
|
115
|
+
respond(msg.id, {});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
let buffer = "";
|
|
120
|
+
process.stdin.setEncoding("utf-8");
|
|
121
|
+
process.stdin.on("data", (chunk) => {
|
|
122
|
+
buffer += chunk;
|
|
123
|
+
const lines = buffer.split("\n");
|
|
124
|
+
buffer = lines.pop() ?? "";
|
|
125
|
+
for (const line of lines) {
|
|
126
|
+
const trimmed = line.trim();
|
|
127
|
+
if (!trimmed)
|
|
128
|
+
continue;
|
|
129
|
+
try {
|
|
130
|
+
handleMessage(JSON.parse(trimmed));
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
/* skip malformed input */
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
process.stdin.on("end", () => process.exit(0));
|
|
138
|
+
export {};
|
|
139
|
+
//# sourceMappingURL=mock-acp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-acp-server.js","sourceRoot":"","sources":["../../src/testing/mock-acp-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* or accepts a fixture path as the first positional argument.
|
|
10
10
|
*/
|
|
11
11
|
import { execFileSync } from "node:child_process";
|
|
12
|
-
import { readFileSync } from "node:fs";
|
|
12
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
14
|
function fixturePathFromArgs() {
|
|
15
15
|
return process.argv[2] ?? "";
|
|
@@ -29,6 +29,8 @@ function loadFixture() {
|
|
|
29
29
|
delay_ms: parsed.delay_ms ?? 0,
|
|
30
30
|
emit_event: parsed.emit_event,
|
|
31
31
|
emit_payload: parsed.emit_payload,
|
|
32
|
+
usage: parsed.usage,
|
|
33
|
+
trap_usr1: parsed.trap_usr1 ?? false,
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
catch (err) {
|
|
@@ -56,6 +58,12 @@ function sleep(ms) {
|
|
|
56
58
|
}
|
|
57
59
|
async function main() {
|
|
58
60
|
const fixture = loadFixture();
|
|
61
|
+
if (fixture.trap_usr1) {
|
|
62
|
+
process.on("SIGUSR1", () => {
|
|
63
|
+
process.stdout.write("mock-backend: interrupted by SIGUSR1\n");
|
|
64
|
+
process.exit(130);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
59
67
|
if (fixture.delay_ms > 0) {
|
|
60
68
|
await sleep(fixture.delay_ms);
|
|
61
69
|
}
|
|
@@ -65,6 +73,12 @@ async function main() {
|
|
|
65
73
|
if (fixture.output) {
|
|
66
74
|
process.stdout.write(fixture.output);
|
|
67
75
|
}
|
|
76
|
+
if (fixture.usage) {
|
|
77
|
+
const usageFile = process.env.AUTOLOOP_USAGE_FILE;
|
|
78
|
+
if (usageFile) {
|
|
79
|
+
writeFileSync(usageFile, JSON.stringify(fixture.usage));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
68
82
|
process.exitCode = fixture.exit_code;
|
|
69
83
|
}
|
|
70
84
|
main().catch((err) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-backend.js","sourceRoot":"","sources":["../../src/testing/mock-backend.ts"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"mock-backend.js","sourceRoot":"","sources":["../../src/testing/mock-backend.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobrienv/autoloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Simpler, opinionated loop harness for inspectable long-running agent workflows",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,10 @@
|
|
|
32
32
|
"packages/harness",
|
|
33
33
|
"packages/dashboard",
|
|
34
34
|
"packages/kanban",
|
|
35
|
-
"packages/cli"
|
|
35
|
+
"packages/cli",
|
|
36
|
+
"packages/issue-sync-core",
|
|
37
|
+
"packages/gh-sync",
|
|
38
|
+
"packages/linear-sync"
|
|
36
39
|
],
|
|
37
40
|
"repository": {
|
|
38
41
|
"type": "git",
|
|
@@ -62,7 +65,7 @@
|
|
|
62
65
|
"format": "biome format src/ test/",
|
|
63
66
|
"format:fix": "biome format --write src/ test/",
|
|
64
67
|
"test": "node --experimental-vm-modules node_modules/.bin/vitest run",
|
|
65
|
-
"test:coverage": "node --experimental-vm-modules node_modules/.bin/vitest run --coverage",
|
|
68
|
+
"test:coverage": "node --experimental-vm-modules node_modules/.bin/vitest run --no-file-parallelism --coverage",
|
|
66
69
|
"test:watch": "node --experimental-vm-modules node_modules/.bin/vitest",
|
|
67
70
|
"check": "biome check src/ test/ && tsc --noEmit && npm run test:coverage",
|
|
68
71
|
"release": "node bin/release",
|
|
@@ -90,9 +93,9 @@
|
|
|
90
93
|
"vitest": "^3.0.0"
|
|
91
94
|
},
|
|
92
95
|
"dependencies": {
|
|
93
|
-
"@mobrienv/autoloop-cli": "0.
|
|
94
|
-
"@mobrienv/autoloop-core": "0.
|
|
95
|
-
"@mobrienv/autoloop-harness": "0.
|
|
96
|
-
"@mobrienv/autoloop-presets": "0.
|
|
96
|
+
"@mobrienv/autoloop-cli": "0.9.1",
|
|
97
|
+
"@mobrienv/autoloop-core": "0.9.1",
|
|
98
|
+
"@mobrienv/autoloop-harness": "0.9.1",
|
|
99
|
+
"@mobrienv/autoloop-presets": "0.9.1"
|
|
97
100
|
}
|
|
98
101
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autoloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Run and operate autoloops — start runs, monitor progress, inject guidance, manage worktrees, inspect artifacts, and clean up.",
|
|
5
|
-
"homepage": "https://github.com/mikeyobrien/autoloop
|
|
6
|
-
"repository": "https://github.com/mikeyobrien/autoloop
|
|
5
|
+
"homepage": "https://github.com/mikeyobrien/autoloop",
|
|
6
|
+
"repository": "https://github.com/mikeyobrien/autoloop",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"keywords": ["autoloop", "ai-agents", "autonomous", "coding"]
|
|
9
9
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: autoloop
|
|
3
3
|
description: Run and operate autoloops — start runs, monitor progress, inject guidance, manage worktrees, inspect artifacts, and clean up. Use when the user asks to run an autoloop, check loop status, merge/clean worktrees, or operate on runs.
|
|
4
|
-
argument-hint: [run|status|guide|merge|clean|inspect] [args...]
|
|
4
|
+
argument-hint: "[run|status|guide|merge|clean|inspect] [args...]"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Autoloop Operations
|
|
@@ -32,7 +32,7 @@ autoloop run --chain autocode,autoqa "objective" --worktree
|
|
|
32
32
|
|
|
33
33
|
**When to use `--automerge`:** When the user wants hands-off operation. Appends an automerge step that squash-merges on completion.
|
|
34
34
|
|
|
35
|
-
**Backend override:** Use `-b claude
|
|
35
|
+
**Backend override:** Use `-b claude`, `-b pi`, `-b kiro`, `-b claude-agent-acp`, or `-b acp:<provider>:<command>` to switch backends.
|
|
36
36
|
|
|
37
37
|
Use `run_in_background: true` on the Bash tool when starting runs so the user isn't blocked.
|
|
38
38
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autoloop-acp
|
|
3
|
+
description: Drive autoloop over the Agent Client Protocol (ACP) — connect an external editor, harness, or agent to `autoloop acp` over stdio, send prompts/slash commands that map to autoloop verbs, and stream loop runs as tool calls. Use when wiring up or operating an ACP connection to autoloop from another system.
|
|
4
|
+
argument-hint: "[connect|run|<slash-command>] [args...]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Autoloop over ACP
|
|
8
|
+
|
|
9
|
+
`autoloop acp` presents an **Agent Client Protocol** (ACP) interface over stdio. The external system (an editor, harness, or another agent) is the ACP **client**; autoloop is the ACP **agent**. This skill is for setting up that connection and driving autoloop through it. Parse `$ARGUMENTS` to determine what the user needs; if ambiguous, ask.
|
|
10
|
+
|
|
11
|
+
> Direction matters. This skill is about **other systems driving autoloop** (autoloop is the agent). The inverse — autoloop *consuming* an external ACP agent as a backend — is the separate `-b kiro` option and is **not** what this covers.
|
|
12
|
+
|
|
13
|
+
## Launching the agent
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Start the ACP agent on stdio (the client spawns this as a subprocess)
|
|
17
|
+
autoloop acp
|
|
18
|
+
|
|
19
|
+
# Surface debug logs as agent thoughts
|
|
20
|
+
autoloop acp --verbose # or -v
|
|
21
|
+
|
|
22
|
+
# Usage
|
|
23
|
+
autoloop acp --help # or -h
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Transport rules (important):**
|
|
27
|
+
- Frames are **NDJSON over stdio**. **Stdout is the protocol channel** — nothing else may write to it. All logs go to **stderr**.
|
|
28
|
+
- The client therefore launches `autoloop acp` as a subprocess and speaks ACP on stdin/stdout. Don't pipe other output into stdout.
|
|
29
|
+
|
|
30
|
+
**Working directory (REQUIRED):** per session. The ACP client **must** set it via the `session/new` `cwd` field, as the absolute path of the target project directory. autoloop anchors **all run state** (journal, registry, `runs/`) on `cwd`. If `cwd` is missing or blank, `session/new` is **rejected** with a JSON-RPC `invalid params` error — autoloop does **not** silently fall back to its own launch directory, because doing so would misroute the entire run into the wrong project. `--project-dir` / `AUTOLOOP_PROJECT_DIR` only affect dashboard control; they are not a fallback for the session working directory.
|
|
31
|
+
|
|
32
|
+
### Representative client config
|
|
33
|
+
|
|
34
|
+
ACP clients differ, but they spawn the agent as a command + args. A generic entry looks like:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"command": "autoloop",
|
|
39
|
+
"args": ["acp"]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Add `"--verbose"` to `args` if needed. The client **must** send a valid `cwd` on `session/new` (see Working directory above). No authentication step is required — `authenticate` is a no-op because autoloop runs locally under the caller's own credentials.
|
|
44
|
+
|
|
45
|
+
## What the client sees
|
|
46
|
+
|
|
47
|
+
On `initialize`, autoloop reports:
|
|
48
|
+
- `agentInfo`: `{ name: "autoloop", version: "0.1.0" }`
|
|
49
|
+
- `agentCapabilities`: `{ loadSession: false }` — sessions are not resumable; one session lives for the life of the connection and can launch many runs and quick commands over successive prompt turns.
|
|
50
|
+
|
|
51
|
+
On `session/new`, autoloop **requires** a valid `cwd` (rejecting the session otherwise) and advertises its verbs as **slash commands** (`available_commands_update`), each with a description and an input hint.
|
|
52
|
+
|
|
53
|
+
## Prompt behaviour
|
|
54
|
+
|
|
55
|
+
Each prompt turn's text is dispatched as an autoloop command line:
|
|
56
|
+
|
|
57
|
+
- **Bare objective** (no recognised verb) → runs the default preset: `run autocode "<your prompt>"`. So sending `build the login page` starts an `autocode` loop on that objective.
|
|
58
|
+
- **Explicit verb** or **`/slash` command** always overrides the default (e.g. `run autofix "..."`, `/loops`, `inspect scratchpad`).
|
|
59
|
+
- A single outer **XML-ish wrapper** is stripped before parsing — `<user_message>`, `<user>`, `<message>`, `<query>`, or `<prompt>`. This makes model-driven clients that wrap prompts work transparently. Unrecognised tags are left untouched.
|
|
60
|
+
- Tokenising honours single and double quotes, so quoted objectives with spaces survive: `run autocode "Fix the login bug"`.
|
|
61
|
+
- An empty prompt returns help text.
|
|
62
|
+
|
|
63
|
+
## Command surface
|
|
64
|
+
|
|
65
|
+
All 14 verbs work both as bare prompt lines and as `/slash` commands. They run in one of three modes:
|
|
66
|
+
|
|
67
|
+
### Stream mode — long-running loops, streamed as ACP tool calls
|
|
68
|
+
|
|
69
|
+
| Verb | Hint | Description |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `run` | `<preset> [objective] [flags]` | Start a loop with a preset (e.g. `run autocode "Fix bug"`) |
|
|
72
|
+
| `chain` | `<list\|run> [args]` | Run or list chains of presets |
|
|
73
|
+
|
|
74
|
+
These execute a loop and bridge its events onto ACP `session/update` notifications as tool calls. The turn ends with a summary message.
|
|
75
|
+
|
|
76
|
+
### Capture mode — quick commands, returns captured text
|
|
77
|
+
|
|
78
|
+
| Verb | Hint | Description |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `loops` | `[--all\|show <id>\|artifacts <id>\|health]` | List active/recent runs or show/health a run |
|
|
81
|
+
| `inspect` | `<artifact> [selector] [--format <fmt>]` | Inspect a run artifact (journal, scratchpad, metrics, ...) |
|
|
82
|
+
| `guide` | `[--run <id>] <message>` | Inject operator guidance into the next iteration of a run |
|
|
83
|
+
| `list` | | List available presets |
|
|
84
|
+
| `memory` | `<list\|status\|find\|add\|remove> [args]` | Manage persistent loop memory |
|
|
85
|
+
| `task` | `<add\|complete\|update\|remove\|list> [args]` | Manage loop tasks |
|
|
86
|
+
| `worktree` | `<list\|show\|merge\|clean> [args]` | Manage git worktrees for isolated runs |
|
|
87
|
+
| `runs` | `clean [--max-age <days>]` | Maintain run directories |
|
|
88
|
+
| `config` | `<show\|set\|unset\|path> [args]` | Show or edit autoloop configuration |
|
|
89
|
+
| `control` | `<show\|capabilities\|interrupt\|guide> <id>` | Inspect or control a live run |
|
|
90
|
+
| `emit` | `<topic> [summary]` | Emit a coordination topic event into a run |
|
|
91
|
+
|
|
92
|
+
These run synchronously with stdout/stderr captured and return the text as a single agent message.
|
|
93
|
+
|
|
94
|
+
### Control mode — dashboard lifecycle
|
|
95
|
+
|
|
96
|
+
| Verb | Hint | Description |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `dashboard` | `[start\|stop\|status] [--port <port>] [--host <host>]` | Start/stop the local dashboard and return its URL |
|
|
99
|
+
|
|
100
|
+
Handled directly by the agent. On start, the dashboard **URL is returned on its own line** so clients can linkify it.
|
|
101
|
+
|
|
102
|
+
## Streaming & cancellation
|
|
103
|
+
|
|
104
|
+
- **Stream verbs** (`run`, `chain`) emit live `session/update` notifications as the loop runs, then a final summary with a stop reason.
|
|
105
|
+
- **Capture verbs** return their full captured output in one message.
|
|
106
|
+
- Cancel an in-flight turn with the ACP **`session/cancel`** notification — it aborts the active turn (and the running loop) for that session.
|
|
107
|
+
|
|
108
|
+
## Quick reference
|
|
109
|
+
|
|
110
|
+
| The client wants to... | Send this prompt |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Start a feature loop | `run autocode "..."` (or just the bare objective) |
|
|
113
|
+
| Fix a bug | `run autofix "..."` |
|
|
114
|
+
| List/inspect runs | `loops` / `loops show <id>` / `inspect scratchpad` |
|
|
115
|
+
| Steer a running loop | `guide "..."` |
|
|
116
|
+
| List presets | `list` |
|
|
117
|
+
| Open the dashboard | `dashboard start` (URL returned on its own line) |
|
|
118
|
+
| Cancel the current turn | ACP `session/cancel` notification |
|