@wular/coder 0.1.24 → 0.1.26
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 +30 -26
- package/dist/cli.js +3 -3
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/handle/SKILL.md +4 -0
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/skills/handle/SKILL.md +4 -0
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor/skills/handle/SKILL.md +4 -0
package/README.md
CHANGED
|
@@ -21,52 +21,56 @@ Plugs into Claude Code or Codex to enable the harness with Coder instances.
|
|
|
21
21
|
|
|
22
22
|
## Get started
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
**1. Install a host plugin.**
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Pick the host you use (or both):
|
|
27
27
|
|
|
28
|
-
>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
> Always use Coder for all implementation and system exploring tasks.
|
|
33
|
-
|
|
34
|
-
The work happens in the engine, and your session gets the result. Codex is the primary engine when available, with a Claude subagent as fallback.
|
|
35
|
-
|
|
36
|
-
### Claude Code
|
|
28
|
+
<table>
|
|
29
|
+
<tr><th>Claude Code</th><th>Codex</th></tr>
|
|
30
|
+
<tr valign="top">
|
|
31
|
+
<td>
|
|
37
32
|
|
|
38
33
|
```
|
|
39
34
|
/plugin marketplace add muzam1l/coder
|
|
40
|
-
|
|
41
35
|
/plugin install coder@coder-plugins
|
|
42
|
-
|
|
43
36
|
/reload-plugins
|
|
44
|
-
|
|
45
37
|
/coder:setup
|
|
46
38
|
```
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
npm install -g @wular/coder
|
|
52
|
-
coder host-setup --claude
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Codex
|
|
40
|
+
</td>
|
|
41
|
+
<td>
|
|
56
42
|
|
|
57
43
|
```
|
|
58
44
|
codex plugin marketplace add muzam1l/coder
|
|
59
45
|
codex plugin add coder@coder-plugins
|
|
60
46
|
```
|
|
61
47
|
|
|
62
|
-
|
|
48
|
+
</td>
|
|
49
|
+
</tr>
|
|
50
|
+
</table>
|
|
63
51
|
|
|
64
|
-
|
|
52
|
+
Or do it from the shell instead of slash commands - this installs the same plugin:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
65
55
|
npm install -g @wular/coder
|
|
66
|
-
coder host-setup --codex
|
|
56
|
+
coder host-setup --claude # or --codex
|
|
67
57
|
```
|
|
68
58
|
|
|
69
|
-
|
|
59
|
+
**2. Install at least one engine CLI**, logged in: [codex](https://github.com/openai/codex) (`npm install -g @openai/codex && codex login`) or [Claude Code](https://github.com/anthropics/claude-code) (`npm install -g @anthropic-ai/claude-code && claude auth login`). Run `coder host-setup` any time to check which engines are ready.
|
|
60
|
+
|
|
61
|
+
**3. Ask your host to use it:**
|
|
62
|
+
|
|
63
|
+
> Use Coder to explain the directory structure of the workspace.
|
|
64
|
+
|
|
65
|
+
Or make it the default in AGENTS.md/CLAUDE.md:
|
|
66
|
+
|
|
67
|
+
> Always use Coder for all implementation and system exploring tasks.
|
|
68
|
+
|
|
69
|
+
The work happens in the engine, and your session gets the result. Codex is the primary engine when available, with a Claude subagent as fallback.
|
|
70
|
+
|
|
71
|
+
_Recommended setup: Claude Code as host (fable model preferably), Codex as engine - best for performance and cost distribution._
|
|
72
|
+
|
|
73
|
+
Note: with Codex as host, use "Ask for approval"/"Full Access", not "Approve for me" - the latter disables invoking CLI agents.
|
|
70
74
|
|
|
71
75
|
## Staying up to date
|
|
72
76
|
|
package/dist/cli.js
CHANGED
|
@@ -119,7 +119,7 @@ ${b.dim(`[coder] detached — task still running: coder task result ${k}`)}
|
|
|
119
119
|
`)}f.exit(h.status==="completed"?0:1)}if(i.json){A({taskId:k,status:l.status,startupCheck:l.threadId?"passed":"pending",commands:{result:`coder task result ${k}`,steer:`coder task steer ${k} "<follow-up>"`,stop:`coder task stop ${k}`,watch:`coder task stream ${k}`}});return}f.stdout.write(`${b.dim("[coder]")} task ${b.cyan(k)} started in the background (${l.status}).
|
|
120
120
|
`),f.stdout.write(`${b.dim(H)}
|
|
121
121
|
`)}async function FD(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd"]}),u=x(i),n=$[0],v=Gr(u,n);if(!v)M(`Worker: task ${n} not found.`);a(u,n,{status:"running",pid:f.pid});try{if(v.simulateApproval){let g=await s_(C(u,n),{onEvent:(U)=>zn(u,n,U)});if(zn(u,n,{message:`simulated approval: ${g}`}),g==="decline"){a(u,n,{status:"cancelled",completedAt:new Date().toISOString(),error:"simulated approval denied"});return}}await _2(v)(u,v,{echo:!1})}catch(g){let U=g instanceof Error?g.message:String(g);a(u,n,{status:"failed",error:U}),zn(u,n,{kind:"error",message:U}),f.exit(1)}}import TD from"node:fs";import k2 from"node:path";import mv from"node:process";async function q_(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd"],booleanOptions:["json","wait"]});rr($,1,"task result");let u=x(i),n=nr(u,$[0]);if(i.wait){if(kn.includes(n.status))mv.stderr.write(`${b.dim(`[coder] waiting for task ${n.id} to finish...`)}
|
|
122
|
-
`);let K=await U$(u,n);if(n=K.job,K.reason==="approval")$$(n.id,K.approval,i.json)}let v=Pn(C(u,n.id)).filter((K)=>!K.response),g=k2.join(C(u,n.id),"result.json"),U=TD.existsSync(g)?JSON.parse(TD.readFileSync(g,"utf8")):null,_=kn.includes(n.status),I=_?Sr(u,n.id,1)[0]:void 0,D=_?ei(u,n):n.updatedAt,k=r$(D),O=_&&v.length===0&&k>n$,t=()=>{if(i.wait)mv.exit(n.status==="completed"?0:1)};if(i.json)return A({taskId:n.id,name:n.name??null,status:n.status,agent:n.agent,..._?{idleMs:k,lastActivityAt:D??null,stalled:O}:{},pendingApprovals:v.map((K)=>({id:K.id,summary:K.summary})),result:U}),t();let P=b,J=[`${P.dim("task")} ${P.cyan(n.id)}`,...n.name?[`${P.dim("name")} ${n.name}`]:[],`${P.dim("status")} ${i$(n.status)}`,`${P.dim("agent")} ${n.agent??"-"}`,...U?.tokens?[`${P.dim("tokens")} ${On(U.tokens,U.model??n.model)}`]:[]];if(v.length){J.push("",P.dim("pending approvals:"));for(let K of v)J.push(` ${P.cyan(K.id)} ${K.summary}`)}if(_&&I){let K=I.message??I.kind??"";J.push(`${P.dim("last")} ${P.dim(`${K} (${vi(k)} ago)`)}`)}if(J.push(""),U)J.push(U.finalMessage||"(no final message)");else if(_)J.push(P.dim(O?`Result pending, but no progress for ${vi(k)} — the task may be stalled.`:"Result pending — task is still running."));else J.push(P.dim("(no result)"));if(_&&!i.wait){let K=[`Wait for it: coder task result ${n.id} --wait`,`Follow live: coder task stream ${n.id}`];if(O)K.push(`Check the transcript: coder task stream ${n.id} --tail all`);J.push("",er(K,P))}if(mv.stdout.write(`${J.join(`
|
|
122
|
+
`);let K=await U$(u,n);if(n=K.job,K.reason==="approval")$$(n.id,K.approval,i.json)}let v=Pn(C(u,n.id)).filter((K)=>!K.response),g=k2.join(C(u,n.id),"result.json"),U=TD.existsSync(g)?JSON.parse(TD.readFileSync(g,"utf8")):null,_=kn.includes(n.status),I=_?Sr(u,n.id,1)[0]:void 0,D=_?ei(u,n):n.updatedAt,k=r$(D),O=_&&v.length===0&&k>n$,t=()=>{if(i.wait)mv.exit(n.status==="completed"?0:1)};if(i.json)return A({taskId:n.id,name:n.name??null,status:n.status,agent:n.agent,model:n.model??null,..._?{idleMs:k,lastActivityAt:D??null,stalled:O}:{},pendingApprovals:v.map((K)=>({id:K.id,summary:K.summary})),result:U}),t();let P=b,J=[`${P.dim("task")} ${P.cyan(n.id)}`,...n.name?[`${P.dim("name")} ${n.name}`]:[],`${P.dim("status")} ${i$(n.status)}`,`${P.dim("agent")} ${(n.agent??"-")+(n.model?`/${n.model}`:"")}`,...U?.tokens?[`${P.dim("tokens")} ${On(U.tokens,U.model??n.model)}`]:[]];if(v.length){J.push("",P.dim("pending approvals:"));for(let K of v)J.push(` ${P.cyan(K.id)} ${K.summary}`)}if(_&&I){let K=I.message??I.kind??"";J.push(`${P.dim("last")} ${P.dim(`${K} (${vi(k)} ago)`)}`)}if(J.push(""),U)J.push(U.finalMessage||"(no final message)");else if(_)J.push(P.dim(O?`Result pending, but no progress for ${vi(k)} — the task may be stalled.`:"Result pending — task is still running."));else J.push(P.dim("(no result)"));if(_&&!i.wait){let K=[`Wait for it: coder task result ${n.id} --wait`,`Follow live: coder task stream ${n.id}`];if(O)K.push(`Check the transcript: coder task stream ${n.id} --tail all`);J.push("",er(K,P))}if(mv.stdout.write(`${J.join(`
|
|
123
123
|
`)}
|
|
124
124
|
`),U?.touchedFiles?.length)mv.stderr.write(`[coder] touched files: ${U.touchedFiles.join(", ")}
|
|
125
125
|
`);return t()}import oD from"node:fs";import D2 from"node:path";import ri from"node:process";async function fv(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd","tail"],booleanOptions:["json"]});rr($,1,"task stream");let u=x(i),n=nr(u,$[0]),v=new Set(["completed","failed","cancelled"]),g=Number.MAX_SAFE_INTEGER,U=i.tail,_=U===void 0?0:U==="all"?g:Math.max(0,Number.parseInt(String(U),10)||0),I=Math.max(0,Sr(u,n.id,g).length-_),D=()=>{let P=Sr(u,n.id,g);for(let J of P.slice(I))if(i.json)ri.stdout.write(`${JSON.stringify(J)}
|
|
@@ -129,10 +129,10 @@ ${b.dim(`[coder] detached — task still running: coder task result ${k}`)}
|
|
|
129
129
|
${t?.finalMessage||(P?"(no final message)":`(task ${k.status})`)}
|
|
130
130
|
`);let J=t?.tokens?` tokens=${On(t.tokens,t.model??k.model)}`:"";ri.stderr.write(`${b.dim(`[coder] task=${n.id} status=${k.status}${J}`)}
|
|
131
131
|
`)}ri.exit(k.status==="completed"?0:1)}async function V_(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd","model","effort","permissions"],booleanOptions:["background","wait"]}),u=x(i),[n,...v]=$,g=v.join(" ").trim();if(!n||!g)M("Missing task id or follow-up text.",{hint:['Usage: coder task steer <task-id> "<follow-up>" [--wait]',"Help: coder task steer --help"]});let U=nr(u,n);if(!U.threadId)M(`Task ${U.id} has no thread to steer yet (status: ${U.status}).`,{hint:`Wait for it to start: coder task stream ${U.id}`});if(U.status==="running")M(`Task ${U.id} is still running.`,{hint:[`Watch it: coder task stream ${U.id}`,`Or stop first: coder task stop ${U.id}`]});let _=[g,"--resume",U.id,"--cwd",u,...U.agent?["--agent",U.agent]:[],...i.model?["--model",i.model]:["--model",U.model].filter(()=>U.model),...i.effort?["--effort",i.effort]:U.effort?["--effort",U.effort]:[],...i.permissions?["--permissions",i.permissions]:U.permissions?["--permissions",U.permissions]:[],...i.wait?["--wait"]:[]];await sn(_)}import ZD from"node:process";async function Y_(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd"],booleanOptions:["json"]});rr($,1,"task stop");let u=x(i),n=nr(u,$[0]),v=n.agent==="claude"?{detail:"claude worker terminated"}:await wk(u,{threadId:n.threadId,turnId:n.turnId});if(n.pid&&n.status==="running")try{ZD.kill(n.pid,"SIGTERM")}catch{}if(a(u,n.id,{status:"cancelled",completedAt:new Date().toISOString()}),i.json){A({taskId:n.id,status:"cancelled",interrupt:v.detail});return}ZD.stdout.write(`Stopped task ${b.cyan(n.id)} ${b.dim(`(${v.detail})`)}
|
|
132
|
-
`)}import yv from"node:process";async function Q_(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd"],booleanOptions:["json","all","stopped","archived"]});rr($,0,"task list");let u=x(i),n=(k)=>k.archived===!0,v=(k)=>hr.includes(k.status),g=(k)=>kn.includes(k.status),U=Ar(u);if(i.archived)U=U.filter(n);else if(U=U.filter((k)=>!n(k)),i.stopped)U=U.filter(v);else if(!i.all)U=U.filter(g);let _=U.map((k)=>({taskId:k.id,status:k.status,agent:k.agent??null,name:k.name??null,prompt:String(k.prompt??"").slice(0,80),updatedAt:k.updatedAt,archived:k.archived??!1,idleMs:kn.includes(k.status)?r$(ei(u,k)):null}));if(i.json){A(_);return}if(!_.length){let k=i.archived?"archived tasks":i.stopped?"stopped tasks":i.all?"tasks":"running tasks";yv.stdout.write(`No ${k}.
|
|
132
|
+
`)}import yv from"node:process";async function Q_(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd"],booleanOptions:["json","all","stopped","archived"]});rr($,0,"task list");let u=x(i),n=(k)=>k.archived===!0,v=(k)=>hr.includes(k.status),g=(k)=>kn.includes(k.status),U=Ar(u);if(i.archived)U=U.filter(n);else if(U=U.filter((k)=>!n(k)),i.stopped)U=U.filter(v);else if(!i.all)U=U.filter(g);let _=U.map((k)=>({taskId:k.id,status:k.status,agent:k.agent??null,model:k.model??null,name:k.name??null,prompt:String(k.prompt??"").slice(0,80),updatedAt:k.updatedAt,archived:k.archived??!1,idleMs:kn.includes(k.status)?r$(ei(u,k)):null}));if(i.json){A(_);return}if(!_.length){let k=i.archived?"archived tasks":i.stopped?"stopped tasks":i.all?"tasks":"running tasks";yv.stdout.write(`No ${k}.
|
|
133
133
|
`);let O=[];if(!i.archived&&!i.stopped&&!i.all){let t=Ar(u),P=t.filter((K)=>!n(K)&&v(K)).length,J=t.filter(n).length;if(P)O.push(`${P} stopped: coder task list --stopped`);if(J)O.push(`${J} archived: coder task list --archived`)}if(O.length)yv.stdout.write(`
|
|
134
134
|
${er(O,b)}
|
|
135
|
-
`);return}let I=b;for(let k of _){let O=k.archived?` ${I.dim("(archived)")}`:"",t=k.name?k.name:I.dim(k.prompt),P=k.idleMs!==null&&k.idleMs>=e_?` ${(k.idleMs>n$?I.red:I.dim)(`· idle ${vi(k.idleMs)}`)}`:"";yv.stdout.write(`${I.cyan(k.taskId.padEnd(24))} ${i$(k.status,10)} ${I.dim((k.agent??"-").padEnd(
|
|
135
|
+
`);return}let I=b;for(let k of _){let O=k.archived?` ${I.dim("(archived)")}`:"",t=k.name?k.name:I.dim(k.prompt),P=k.idleMs!==null&&k.idleMs>=e_?` ${(k.idleMs>n$?I.red:I.dim)(`· idle ${vi(k.idleMs)}`)}`:"";yv.stdout.write(`${I.cyan(k.taskId.padEnd(24))} ${i$(k.status,10)} ${I.dim(((k.agent??"-")+(k.model?`/${k.model}`:"")).padEnd(14))} ${t}${P}${O}
|
|
136
136
|
`)}let D=["Result: coder task result <task-id>"];if(!i.all&&!i.stopped&&!i.archived)D.push("All tasks: coder task list --all");yv.stdout.write(`
|
|
137
137
|
${er(D,I)}
|
|
138
138
|
`)}import CD from"node:process";import{spawnSync as RD}from"node:child_process";function xD(r){return RD("codex",["archive",r],{encoding:"utf8"}).status===0}function jD(r){return RD("codex",["delete","--force",r],{encoding:"utf8"}).status===0}function mD(r){if(r.agent==="codex"&&r.threadId)xD(r.threadId)}async function b_(r){let{options:i,positionals:$}=R(r,{valueOptions:["cwd"],booleanOptions:["all-stopped","json"]});rr($,1,"task archive");let u=x(i),n=new Date().toISOString();if(i["all-stopped"]){let U=Ar(u).filter((I)=>!I.archived&&hr.includes(I.status));for(let I of U)a(u,I.id,{archived:!0,archivedAt:n}),mD(I);let _=U.map((I)=>I.id);if(i.json){A({archived:_,count:_.length});return}CD.stdout.write(_.length?`Archived ${_.length} stopped task${_.length===1?"":"s"}.
|
package/package.json
CHANGED
|
@@ -39,6 +39,10 @@ If `coder` is not on PATH, install it yourself without asking (`npm install -g @
|
|
|
39
39
|
|
|
40
40
|
When no coder engine can start (missing binaries, auth, quota across the whole chain), the runtime exits 3 with a JSON payload (`fallback.action: "run-native-subagent"`). Mention the failure to the user (including any limit-reset time), then run the task with your own subagent facility: spawn one `general-purpose` subagent via the Agent tool whose prompt is the payload's `task` verbatim, prefixed with: "NEVER run git write operations (commit, checkout, stash, reset, push, etc.); leave changes uncommitted." If the payload's `permissions` is not `auto`, append it: `read-only` means investigate and report without modifying anything; `workspace-write` means never touch anything outside the workspace. Relay its output when it completes.
|
|
41
41
|
|
|
42
|
+
## Supervision loop
|
|
43
|
+
|
|
44
|
+
With coders running in the background, don't fire-and-forget: schedule recurring check-ins (the `/loop` skill or ScheduleWakeup; interval by the work's pace, 10m default). Each tick: `coder task list`, fetch finished results, dispatch newly unblocked coders, steer stuck ones, stop off-track ones, surface pending approvals. Stop the loop when all tasks are done and relayed.
|
|
45
|
+
|
|
42
46
|
## Controlling tasks
|
|
43
47
|
|
|
44
48
|
- Continue prior work ("keep going", "apply the top fix"): `coder task steer <task-id> "<follow-up>"`.
|
|
@@ -33,6 +33,10 @@ When you run setup, also check codex is new enough for the default GPT-5.6 model
|
|
|
33
33
|
- Permissions default to auto mode. Pass `--permissions read-only` when the task is read-only; `--permissions workspace-write` to forbid any escalation beyond the project.
|
|
34
34
|
- A background dispatch prints a task id plus its result/steer/stop commands. Relay them and fetch the result (above) rather than polling.
|
|
35
35
|
|
|
36
|
+
## Supervision loop
|
|
37
|
+
|
|
38
|
+
With coders running in the background, don't fire-and-forget: run a recurring monitoring loop in this task (interval by the work's pace, 10m default). Each tick: `coder task list`, fetch finished results, dispatch newly unblocked coders, steer stuck ones, stop off-track ones, surface pending approvals. Stop the loop when all tasks are done and relayed.
|
|
39
|
+
|
|
36
40
|
## Controlling tasks
|
|
37
41
|
|
|
38
42
|
- Continue prior work ("keep going", "apply the top fix"): `coder task steer <task-id> "<follow-up>"`.
|
|
@@ -32,6 +32,10 @@ When you run setup, also check codex is new enough for the default GPT-5.6 model
|
|
|
32
32
|
- Permissions default to auto mode. Pass `--permissions read-only` when the task is read-only; `--permissions workspace-write` to forbid any escalation beyond the project.
|
|
33
33
|
- A background dispatch prints a task id plus its result/steer/stop commands. Relay them and fetch the result (above) rather than polling.
|
|
34
34
|
|
|
35
|
+
## Supervision loop
|
|
36
|
+
|
|
37
|
+
With coders running in the background, don't fire-and-forget: run a recurring monitoring loop in this session (interval by the work's pace, 10m default). Each tick: `coder task list`, fetch finished results, dispatch newly unblocked coders, steer stuck ones, stop off-track ones, surface pending approvals. Stop the loop when all tasks are done and relayed.
|
|
38
|
+
|
|
35
39
|
## Controlling tasks
|
|
36
40
|
|
|
37
41
|
- Continue prior work ("keep going", "apply the top fix"): `coder task steer <task-id> "<follow-up>"`.
|