@dpeek/codeless 0.1.0

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 ADDED
@@ -0,0 +1,242 @@
1
+ # Codeless
2
+
3
+ Codeless is an attended workflow for parallel capability development. Each stream has one
4
+ branch/worktree, one planner, an implementer shell, and at most one approved
5
+ change. Work within a stream is sequential: propose, approve, implement, review,
6
+ commit, land.
7
+
8
+ This project owns the executable, Pi extension, Git/Herdr mechanics, and tests.
9
+ Project instructions and prompts belong to the consuming project. The package
10
+ has its own dependencies, TypeScript configuration, and checks; it imports no
11
+ consumer application code. Its tests use independent temporary repositories.
12
+
13
+ ## Installation
14
+
15
+ Codeless requires Bun, Git, Herdr, and Pi. The scoped package is configured for
16
+ public npm access. Once published, install it with either package manager:
17
+
18
+ ```sh
19
+ bun add --global @dpeek/codeless
20
+ # or
21
+ npm install --global @dpeek/codeless
22
+ ```
23
+
24
+ The package installs the `codeless` executable:
25
+
26
+ ```sh
27
+ codeless --help
28
+ ```
29
+
30
+ The implemented workflow contract is in [spec/workflow.md](./spec/workflow.md).
31
+ Proposed and missing behavior is kept in [todo/workflow.md](./todo/workflow.md).
32
+
33
+ ## Project configuration
34
+
35
+ Run `codeless` inside the intended Git checkout. The runner discovers the project
36
+ from the current working directory, including when invoked from a subdirectory
37
+ or through a linked executable. Its installation location does not select the
38
+ project. Help also works outside a repository.
39
+
40
+ Each project commits `.codeless/config.json`:
41
+
42
+ ```json
43
+ {
44
+ "integrationBranch": "main",
45
+ "directions": "todo",
46
+ "prompts": ".codeless/prompts",
47
+ "install": ["bun", "install", "--frozen-lockfile"],
48
+ "check": ["bun", "run", "check"],
49
+ "planner": {
50
+ "provider": "openai-codex",
51
+ "model": "gpt-5.6-sol",
52
+ "thinking": "high"
53
+ },
54
+ "implementer": {
55
+ "provider": "openai-codex",
56
+ "model": "gpt-5.6-terra",
57
+ "thinking": "medium"
58
+ }
59
+ }
60
+ ```
61
+
62
+ All fields are required. Directory paths are relative to the checkout and must
63
+ stay within it. Commands are nonempty argument arrays, executed in the target
64
+ worktree without shell interpolation. Each role requires an exact Pi provider
65
+ and model ID plus one of `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or
66
+ `max`. Configuration is trusted project code.
67
+
68
+ Before a role session starts, Codeless launches a short-lived, promptless Pi RPC
69
+ process in the target worktree. It requires the exact provider/model in Pi's
70
+ available-model response, selects it, requires the configured level in that
71
+ model's available-thinking response, and verifies the resulting state. Failure
72
+ identifies the role and requested selection; Codeless does not start agent work
73
+ with Pi's fallback model or a clamped thinking level. Successful launch output
74
+ shows the effective role selection, and the Pi process receives the same exact
75
+ model and level.
76
+
77
+ The invoking checkout selects the integration branch. Keep that setting aligned
78
+ across participating worktrees. The runner discovers its checkout from Git's
79
+ worktree list. Directions, prompts, and install commands come from the target
80
+ worktree; landing reads the check command after rebasing. Creating a stream
81
+ first verifies its direction and prompts in the integration checkout.
82
+
83
+ ## Project prompt contract
84
+
85
+ The configured prompt directory provides `change.md`, `implement.md`,
86
+ `review.md`, and `commit.md`, using Pi prompt-template syntax. The runner supplies:
87
+
88
+ - `/change <stream-directory> <direction-file>` to the planner;
89
+ - `/implement <numbered-change-file>` to the implementer;
90
+ - `/review <numbered-change-file>` to the planner after successful dispatch.
91
+
92
+ Path arguments are JSON-quoted. Each direction is `<directions>/<slug>.md`.
93
+ The project owns prompt contents, approval rules, review criteria, and the
94
+ transition from review to commit. Required prompts and directions must exist;
95
+ the runner does not generate or copy project instructions.
96
+
97
+ The package-owned planner extension activates every planner session. Before its
98
+ first project prompt, it requires the exact `<slug>-planner` Pi name, establishes
99
+ and verifies the `<slug>_planner` Herdr identity, and confirms `approve_stream_change`,
100
+ `dispatch_stream_implementer`, and `next_stream_change` are active. Missing or
101
+ incompatible activation, identity mismatch, or inactive tools stops before
102
+ `/change`; global Pi extension installation is unnecessary.
103
+ The approval tool has no arguments. Its extension derives the active
104
+ `<slug>-planner` Pi session and passes it to the backing CLI, which requires it
105
+ to match the worktree and branch. The CLI validates the clean current-integration
106
+ baseline and proposal, exclusively creates the next monotonic `changes/NNN.md`,
107
+ and records one hash-backed journal approval. Exact retries reconcile a missing
108
+ file or journal step; conflicting state stops. Approval returns the numbered path
109
+ but does not dispatch it. Dispatch uses the package executable, inherits the
110
+ planner's worktree, and queues review only on success. Pi loads the extension
111
+ explicitly at planner launch.
112
+
113
+ After landing and recording the full landed commit hash in `planner.md`, the
114
+ project's commit prompt calls `next_stream_change` with the completed
115
+ `changePath` and `landedCommit`. The tool requests `/streams-next`, an extension
116
+ command that waits for the current turn to settle, prepares the next loop, and
117
+ replaces the Pi session in the same pane. It preserves the planner name, applies
118
+ the planner selection read and validated after the stream fast-forwards to the
119
+ captured integration commit, then activates and verifies the replacement before
120
+ sending `/change` after resources reload. The previous conversation is not copied; the
121
+ journal and project files carry context.
122
+
123
+ This uses Pi's `newSession({ setup, withSession })` command API, verified with
124
+ Pi 0.84.3. Only the replacement context activates the selection and sends the
125
+ new prompt. Configuration changes take effect at the next role-session boundary,
126
+ not during an active planner or implementer. Review and remediation therefore
127
+ continue with their existing session's selection. Duplicate requests
128
+ while a handoff is pending are rejected. Ordinary session startup, waiting for
129
+ approval, failed validation, and cancelled replacement do not schedule another
130
+ loop. The project prompt still owns approval and stopping when no work remains.
131
+
132
+ ## Shared local state
133
+
134
+ Worktrees, proposals, numbered changes, and journals live in one shared local
135
+ workspace. By default it is `.codeless/state/` in the primary checkout. That
136
+ directory is Git-ignored while `.codeless/config.json`, prompts, and guidance
137
+ remain tracked. Every linked worktree resolves the same primary-checkout state.
138
+
139
+ To put the state elsewhere, configure an absolute path once from any checkout:
140
+
141
+ ```sh
142
+ git config --local codeless.workspaceRoot /absolute/path/to/workspace
143
+ ```
144
+
145
+ This local Git setting is shared by all worktrees. Keep the path stable while
146
+ planners or worktree shells are running.
147
+
148
+ ```text
149
+ <primary-checkout>/.codeless/state/
150
+ stream/<slug>/
151
+ planner.md # decisions and outcomes
152
+ change.md # editable current proposal
153
+ changes/NNN.md # approved proposals
154
+ worktree/<slug>/ # stream/<slug> branch
155
+ worktree/main/ # example integration checkout location
156
+ .land-lock/ # shared landing owner and recorded integration commit
157
+ metrics/<slug>/NNN.json # first dispatch, landing time, and landed commit
158
+ ```
159
+
160
+ Normal `git clean -fd` preserves ignored state. `git clean -fdx` removes ignored
161
+ files and can therefore destroy local Codeless journals, metrics, and worktrees;
162
+ inspect its targets before using it.
163
+
164
+ ## Commands
165
+
166
+ Run creation, opening, and planner launch from a Herdr-managed shell. Landing
167
+ needs no Herdr session.
168
+
169
+ ```sh
170
+ codeless create <slug>
171
+ codeless open <slug>
172
+ codeless planner <slug>
173
+ codeless approve <planner-session>
174
+ codeless dispatch <numbered-change-file>
175
+ codeless land <slug>
176
+ codeless next <numbered-change-file> <landed-commit>
177
+ codeless metrics
178
+ ```
179
+
180
+ Slugs are lowercase kebab-case, at most 24 characters. `create` starts
181
+ `stream/<slug>` from the integration branch and creates its local documents;
182
+ it refuses existing streams. `open` resumes a stream. Both run the configured
183
+ install command, then validate and open a planner beside an idle shell. `planner`
184
+ starts Pi in an existing stream's lone shell after the same role preflight. Its
185
+ activation establishes the same identity as creation and reopening.
186
+ Dispatch validates the implementer selection before touching the planner's
187
+ right-hand pane, starts a fresh ephemeral implementer, and waits for completion.
188
+
189
+ The first valid dispatch creates one atomic local JSON metric record for its
190
+ stream and numbered change. Retries preserve its original dispatch time. After a
191
+ successful integration fast-forward, Codeless records the landed time and commit
192
+ on that change's canonical record, creating a landed record without elapsed time
193
+ when dispatch collection was unavailable; collection warnings never alter
194
+ dispatch or landing.
195
+ `codeless metrics` prints every recorded stream and a project total. Its elapsed
196
+ columns are dispatch-to-land wall-clock time; among landed changes, records
197
+ without a measured duration are explicitly unavailable. Dispatched-but-unlanded
198
+ changes remain a separate count. Metrics are prospective local observations, not
199
+ journal state or a recovery mechanism.
200
+
201
+ Landing requires clean stream and integration worktrees and exactly one stream
202
+ commit outside their merge base. It acquires `.land-lock` atomically, recording
203
+ the owner and integration commit. It rebases if necessary, runs the configured
204
+ check command, requires the checked worktree to remain clean, and fast-forwards
205
+ the integration checkout. Success releases the lock. Other branches and
206
+ checkouts are untouched; no push is performed.
207
+
208
+ Another lock owner causes a stop, without queuing or polling. Rebase conflicts
209
+ or failed checks retain ownership. Resolve the existing failure, then rerun
210
+ `land`; it verifies the recorded integration commit has not changed. To abandon
211
+ a landing, inspect the owner/base and Git state before manually removing the
212
+ lock. There is no automatic stale-lock removal or retry.
213
+
214
+ `next` is the session handoff's preparation command. It requires the stream's
215
+ own clean worktree and latest numbered change, a full commit hash present in its
216
+ journal, that commit in the stream's history, and all stream commits included
217
+ in the configured integration branch. A landing lock owned by this stream or
218
+ with an unknown owner stops preparation; another stream's lock does not block
219
+ planning. It fast-forwards to a captured integration commit, verifies project
220
+ prompts, direction, and planner selection, and returns JSON containing
221
+ `sessionName`, `prompt`, and the validated `selection`. It does not modify
222
+ journals, allocate a change, approve implementation, or
223
+ control Pi itself. Preparation can be repeated safely after inspecting a failure.
224
+
225
+ If validation or session replacement fails, the planner stops for operator
226
+ attention. Landing is already complete, and any preparation fast-forward remains
227
+ applied. There is no background retry; restarting the planner recovers from the
228
+ journal and Git state.
229
+
230
+ ## Package development
231
+
232
+ From this package directory, run `bun run check` for formatting, lint, types,
233
+ and tests, or `bun run test` for tests alone. The integration tests use real Git
234
+ worktrees and mock Herdr/Pi commands; they never launch actual agents.
235
+
236
+ `npm publish` runs the full check through `prepublishOnly` and publishes
237
+ `@dpeek/codeless` with public access.
238
+
239
+ Keep source, tests, executable, extension, and dependencies inside this project.
240
+ Keep project policies and real prompts outside it. Add automation only for
241
+ concrete needs; this package has no supervisor, project registry, queue, or
242
+ automatic recovery service.
package/bin/codeless ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { runCodeless } from "../src/cli.ts";
4
+
5
+ try {
6
+ await runCodeless(process.argv.slice(2));
7
+ } catch (error) {
8
+ console.error(`codeless: ${error instanceof Error ? error.message : String(error)}`);
9
+ process.exitCode = 1;
10
+ }
@@ -0,0 +1,336 @@
1
+ import { fileURLToPath } from "node:url";
2
+
3
+ const codeless = fileURLToPath(new URL("../bin/codeless", import.meta.url));
4
+ const thinkingLevels = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
5
+ const requiredTools = [
6
+ "approve_stream_change",
7
+ "dispatch_stream_implementer",
8
+ "next_stream_change",
9
+ ];
10
+
11
+ function selection(value) {
12
+ if (
13
+ typeof value !== "object" ||
14
+ value === null ||
15
+ Array.isArray(value) ||
16
+ typeof value.provider !== "string" ||
17
+ value.provider.length === 0 ||
18
+ typeof value.model !== "string" ||
19
+ value.model.length === 0 ||
20
+ !thinkingLevels.has(value.thinking)
21
+ ) {
22
+ throw new Error("Codeless returned an invalid planner selection");
23
+ }
24
+ return { provider: value.provider, model: value.model, thinking: value.thinking };
25
+ }
26
+
27
+ export default function plannerExtension(pi) {
28
+ let registered = false;
29
+ let pending;
30
+
31
+ pi.registerCommand("streams-activate", {
32
+ description: "Verify this planner session before starting its project prompt",
33
+ handler: async (args, ctx) => {
34
+ let prompt;
35
+ try {
36
+ prompt = JSON.parse(args);
37
+ } catch {
38
+ throw new Error("Codeless activation requires one JSON-quoted project prompt");
39
+ }
40
+ if (typeof prompt !== "string" || !prompt.startsWith("/change ")) {
41
+ throw new Error("Codeless activation requires a /change project prompt");
42
+ }
43
+ const activation = ctx.sessionManager
44
+ .getEntries()
45
+ .findLast(
46
+ (entry) =>
47
+ entry.type === "custom" &&
48
+ entry.customType === "streams-role-selection" &&
49
+ entry.data?.role === "planner",
50
+ );
51
+ if (activation) {
52
+ const requested = selection(activation.data.selection);
53
+ const reference = `${requested.provider}/${requested.model}`;
54
+ const model = ctx.modelRegistry.find(requested.provider, requested.model);
55
+ if (!model) throw new Error(`Planner requested ${reference}, but Pi could not find it`);
56
+ if (!(await pi.setModel(model))) {
57
+ throw new Error(`Planner requested ${reference}, but Pi authentication is unavailable`);
58
+ }
59
+ pi.setThinkingLevel(requested.thinking);
60
+ if (
61
+ ctx.model?.provider !== requested.provider ||
62
+ ctx.model.id !== requested.model ||
63
+ pi.getThinkingLevel() !== requested.thinking
64
+ ) {
65
+ const effective = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "no model";
66
+ throw new Error(
67
+ `Planner requested ${reference} at thinking level ${requested.thinking}, but Pi applied ${effective} at ${pi.getThinkingLevel()}`,
68
+ );
69
+ }
70
+ }
71
+ const sessionName = pi.getSessionName();
72
+ const match = /^([a-z][a-z0-9-]{0,23})-planner$/.exec(sessionName ?? "");
73
+ if (!match)
74
+ throw new Error("Codeless activation requires an exact <slug>-planner Pi session name");
75
+ const expectedPlanner = `${match[1].replaceAll("-", "_")}_planner`;
76
+ const pane = process.env.HERDR_PANE_ID;
77
+ if (!pane) throw new Error("Codeless activation requires a Herdr-managed planner pane");
78
+ const plannerIdentity = async () => {
79
+ const identity = await pi.exec("herdr", ["agent", "get", pane], { timeout: 30_000 });
80
+ if (identity.code !== 0) {
81
+ throw new Error(
82
+ identity.stderr.trim() ||
83
+ identity.stdout.trim() ||
84
+ "Codeless could not verify Herdr planner identity",
85
+ );
86
+ }
87
+ try {
88
+ return JSON.parse(identity.stdout).result?.agent;
89
+ } catch {
90
+ throw new Error("Herdr returned an invalid planner identity response");
91
+ }
92
+ };
93
+ let agent = await plannerIdentity();
94
+ if (agent?.name === "pi") {
95
+ const renamed = await pi.exec("herdr", ["agent", "rename", pane, expectedPlanner], {
96
+ timeout: 30_000,
97
+ });
98
+ if (renamed.code !== 0) {
99
+ throw new Error(
100
+ renamed.stderr.trim() ||
101
+ renamed.stdout.trim() ||
102
+ "Codeless could not establish Herdr planner identity",
103
+ );
104
+ }
105
+ agent = await plannerIdentity();
106
+ }
107
+ if (agent?.name !== expectedPlanner) {
108
+ throw new Error(
109
+ `Codeless planner identity is ${agent?.name ?? "missing"}, expected ${expectedPlanner}`,
110
+ );
111
+ }
112
+ const activeTools = ctx.getSystemPromptOptions().selectedTools ?? [];
113
+ const missing = requiredTools.filter((tool) => !activeTools.includes(tool));
114
+ if (missing.length > 0) {
115
+ throw new Error(
116
+ `Codeless planner activation is missing required tools: ${missing.join(", ")}`,
117
+ );
118
+ }
119
+ ctx.ui.notify(`Planner activated: ${sessionName} / ${expectedPlanner}`, "info");
120
+ pi.sendUserMessage(prompt, { expandPromptTemplates: true });
121
+ },
122
+ });
123
+
124
+ pi.registerCommand("streams-next", {
125
+ description: "Finish a requested stream handoff and start a fresh planner session",
126
+ handler: async (_args, ctx) => {
127
+ if (!pending || pending.running) throw new Error("No pending Codeless handoff");
128
+ const request = pending;
129
+ request.running = true;
130
+ try {
131
+ await ctx.waitForIdle();
132
+ const execution = await pi.exec(
133
+ "bun",
134
+ [codeless, "next", request.changePath, request.landedCommit],
135
+ {
136
+ timeout: 30_000,
137
+ },
138
+ );
139
+ if (execution.code !== 0) {
140
+ throw new Error(
141
+ execution.stderr.trim() ||
142
+ execution.stdout.trim() ||
143
+ "Codeless handoff validation failed",
144
+ );
145
+ }
146
+ const { sessionName, prompt, selection: requestedValue } = JSON.parse(execution.stdout);
147
+ if (
148
+ sessionName !== pi.getSessionName() ||
149
+ typeof prompt !== "string" ||
150
+ !prompt.startsWith("/change ")
151
+ ) {
152
+ throw new Error("Codeless returned an invalid planner handoff");
153
+ }
154
+ const requested = selection(requestedValue);
155
+ const result = await ctx.newSession({
156
+ setup: async (sm) => {
157
+ sm.appendSessionInfo(sessionName);
158
+ sm.appendCustomEntry("streams-role-selection", {
159
+ role: "planner",
160
+ selection: requested,
161
+ });
162
+ },
163
+ withSession: async (replacement) => {
164
+ await replacement.sendUserMessage(`/streams-activate ${JSON.stringify(prompt)}`, {
165
+ expandPromptTemplates: true,
166
+ });
167
+ },
168
+ });
169
+ if (result.cancelled)
170
+ throw new Error("Codeless handoff cancelled; the current session was retained");
171
+ } finally {
172
+ pending = undefined;
173
+ }
174
+ },
175
+ });
176
+
177
+ pi.on("session_start", () => {
178
+ if (registered || !pi.getSessionName()?.endsWith("-planner")) return;
179
+ registered = true;
180
+
181
+ pi.registerTool({
182
+ name: "next_stream_change",
183
+ label: "Start next stream change",
184
+ description:
185
+ "After a successful landing and journal update, start a fresh planner session to propose the next change.",
186
+ promptSnippet: "Start the next planning loop after recording a successful landing",
187
+ promptGuidelines: [
188
+ "Call next_stream_change exactly once after landing the latest approved change and recording its full commit hash in the planner journal.",
189
+ "Finish this turn after requesting the handoff. The replacement planner proposes the next change and waits for approval. Stop on failure or cancellation; do not retry automatically.",
190
+ ],
191
+ parameters: {
192
+ type: "object",
193
+ properties: {
194
+ changePath: {
195
+ type: "string",
196
+ description: "Absolute path to the completed changes/NNN.md file",
197
+ },
198
+ landedCommit: {
199
+ type: "string",
200
+ description: "Full landed commit hash recorded in planner.md",
201
+ },
202
+ },
203
+ required: ["changePath", "landedCommit"],
204
+ additionalProperties: false,
205
+ },
206
+ async execute(_toolCallId, params) {
207
+ if (pending) throw new Error("A Codeless handoff is already pending");
208
+ pending = {
209
+ changePath: params.changePath.replace(/^@/, ""),
210
+ landedCommit: params.landedCommit,
211
+ running: false,
212
+ };
213
+ try {
214
+ pi.sendUserMessage("/streams-next", {
215
+ deliverAs: "followUp",
216
+ expandPromptTemplates: true,
217
+ });
218
+ } catch (error) {
219
+ pending = undefined;
220
+ throw error;
221
+ }
222
+ return {
223
+ content: [
224
+ {
225
+ type: "text",
226
+ text: "Requested the next planning loop. Finish this turn so the handoff can validate the landing and start a fresh session.",
227
+ },
228
+ ],
229
+ };
230
+ },
231
+ });
232
+
233
+ pi.registerTool({
234
+ name: "approve_stream_change",
235
+ label: "Approve stream change",
236
+ description:
237
+ "Promote this planner's current proposal exactly once into an immutable numbered change and record its approval before dispatch.",
238
+ promptSnippet: "Approve the current stream proposal after the operator says go",
239
+ promptGuidelines: [
240
+ "After an operator says go, call approve_stream_change exactly once. It allocates and records the approval but does not dispatch implementation.",
241
+ "Use the returned changePath for one explicit dispatch_stream_implementer call. Keep rejection and revision conversational.",
242
+ ],
243
+ parameters: {
244
+ type: "object",
245
+ properties: {},
246
+ additionalProperties: false,
247
+ },
248
+ async execute(_toolCallId, _params, signal) {
249
+ const sessionName = pi.getSessionName();
250
+ const session = /^([a-z][a-z0-9-]{0,23})-planner$/.exec(sessionName ?? "");
251
+ if (!session) throw new Error("Approval requires an active <slug>-planner Pi session");
252
+ const execution = await pi.exec("bun", [codeless, "approve", sessionName], {
253
+ signal,
254
+ timeout: 30_000,
255
+ });
256
+ const output = [execution.stdout.trim(), execution.stderr.trim()]
257
+ .filter(Boolean)
258
+ .join("\n");
259
+ if (execution.code !== 0) {
260
+ throw new Error(output || `codeless approve failed with exit code ${execution.code}`);
261
+ }
262
+ let approval;
263
+ try {
264
+ approval = JSON.parse(execution.stdout);
265
+ } catch {
266
+ throw new Error("Codeless returned an invalid approval");
267
+ }
268
+ if (
269
+ typeof approval !== "object" ||
270
+ approval === null ||
271
+ typeof approval.number !== "string" ||
272
+ !/^\d{3}$/.test(approval.number) ||
273
+ typeof approval.changePath !== "string" ||
274
+ !approval.changePath.startsWith("/") ||
275
+ typeof approval.title !== "string" ||
276
+ !approval.title
277
+ ) {
278
+ throw new Error("Codeless returned an invalid approval");
279
+ }
280
+ return {
281
+ content: [
282
+ {
283
+ type: "text",
284
+ text: `Approved ${approval.number}: ${approval.title}. Dispatch requires a separate explicit call.`,
285
+ },
286
+ ],
287
+ details: approval,
288
+ };
289
+ },
290
+ });
291
+
292
+ pi.registerTool({
293
+ name: "dispatch_stream_implementer",
294
+ label: "Dispatch stream implementer",
295
+ description:
296
+ "Create or reuse this stream planner's right-hand Herdr pane, start a fresh Pi implementer in the correct worktree, submit one approved numbered change, wait for it to settle, and queue this planner's review.",
297
+ promptSnippet: "Dispatch one approved stream change to a fresh Pi implementer",
298
+ promptGuidelines: [
299
+ "Use dispatch_stream_implementer exactly once after the operator approves and the numbered stream change file is created; never reproduce its Herdr pane or agent commands with bash.",
300
+ "After successful dispatch, the tool queues the expanded review prompt for the planner; follow that queued instruction instead of ending the workflow after reporting implementation completion.",
301
+ ],
302
+ parameters: {
303
+ type: "object",
304
+ properties: {
305
+ changePath: {
306
+ type: "string",
307
+ description: "Absolute path to an approved changes/NNN.md stream change file",
308
+ },
309
+ },
310
+ required: ["changePath"],
311
+ additionalProperties: false,
312
+ },
313
+ async execute(_toolCallId, params, signal) {
314
+ const changePath = params.changePath.replace(/^@/, "");
315
+ const execution = await pi.exec("bun", [codeless, "dispatch", changePath], {
316
+ signal,
317
+ timeout: 3_700_000,
318
+ });
319
+ const output = [execution.stdout.trim(), execution.stderr.trim()]
320
+ .filter(Boolean)
321
+ .join("\n");
322
+ if (execution.code !== 0) {
323
+ throw new Error(output || `codeless dispatch failed with exit code ${execution.code}`);
324
+ }
325
+ pi.sendUserMessage(`/review ${JSON.stringify(changePath)}`, {
326
+ deliverAs: "steer",
327
+ expandPromptTemplates: true,
328
+ });
329
+ return {
330
+ content: [{ type: "text", text: output || "Implementer settled." }],
331
+ details: { changePath },
332
+ };
333
+ },
334
+ });
335
+ });
336
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@dpeek/codeless",
3
+ "version": "0.1.0",
4
+ "description": "An attended planner and implementer workflow for parallel capability development",
5
+ "homepage": "https://github.com/dpeek/codeless#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/dpeek/codeless/issues"
8
+ },
9
+ "license": "UNLICENSED",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/dpeek/codeless.git"
13
+ },
14
+ "bin": {
15
+ "codeless": "bin/codeless"
16
+ },
17
+ "files": [
18
+ "bin",
19
+ "extension",
20
+ "src",
21
+ "README.md",
22
+ "spec"
23
+ ],
24
+ "type": "module",
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "scripts": {
29
+ "check": "oxfmt --config .oxfmtrc.json --write . && oxlint --config .oxlintrc.json --fix --type-aware --type-check . && bun run test",
30
+ "prepublishOnly": "bun run check",
31
+ "test": "bun test ./test --dots"
32
+ },
33
+ "dependencies": {
34
+ "effect": "4.0.0-rc.112"
35
+ },
36
+ "devDependencies": {
37
+ "@types/bun": "1.4.0",
38
+ "oxfmt": "0.64.0",
39
+ "oxlint": "1.79.0",
40
+ "oxlint-tsgolint": "7.0.2001",
41
+ "typescript": "^7"
42
+ },
43
+ "engines": {
44
+ "bun": ">=1.4.0"
45
+ },
46
+ "packageManager": "bun@1.4.0"
47
+ }