@dpeek/codeless 0.1.1 → 0.1.3
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 +79 -33
- package/extension/implementer-reporting.js +78 -10
- package/extension/planner.js +135 -15
- package/package.json +2 -1
- package/prompts/change.md +40 -0
- package/prompts/commit.md +37 -0
- package/prompts/implement.md +17 -0
- package/prompts/review.md +20 -0
- package/spec/workflow.md +215 -149
- package/src/attempt.ts +4 -1
- package/src/cli.ts +254 -71
- package/src/metrics.ts +90 -4
package/README.md
CHANGED
|
@@ -12,8 +12,11 @@ consumer application code. Its tests use independent temporary repositories.
|
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
|
-
Codeless requires Bun, Git, Herdr, and Pi.
|
|
16
|
-
|
|
15
|
+
Codeless requires Bun, Git, Herdr, and Pi. Planner activation requires Herdr's
|
|
16
|
+
official Pi lifecycle integration; install it with `herdr integration install pi`
|
|
17
|
+
and restart existing Pi processes after updating it. The managed launch and
|
|
18
|
+
session-replacement contract is verified against Herdr 0.8.2 and Pi 0.85.1.
|
|
19
|
+
The scoped package is configured for public npm access. Once published, install it with either package manager:
|
|
17
20
|
|
|
18
21
|
```sh
|
|
19
22
|
bun add --global @dpeek/codeless
|
|
@@ -91,20 +94,30 @@ The configured prompt directory provides `change.md`, `implement.md`,
|
|
|
91
94
|
|
|
92
95
|
Path arguments are JSON-quoted. Each direction is `<directions>/<slug>.md`.
|
|
93
96
|
The project owns prompt contents, approval rules, review criteria, and the
|
|
94
|
-
transition from review to commit.
|
|
95
|
-
|
|
97
|
+
transition from review to commit. `codeless init` creates missing copies of the
|
|
98
|
+
package's four generic starters, but never replaces existing prompt bytes.
|
|
99
|
+
Review and commit those local prompt edits in the invoking checkout, then bring
|
|
100
|
+
that commit onto the configured integration branch before creating streams.
|
|
101
|
+
Required prompts and directions must exist for stream creation and opening.
|
|
96
102
|
|
|
97
103
|
The package-owned planner extension activates every planner session. Before its
|
|
98
|
-
first project prompt, it requires the exact `<slug>-planner` Pi name
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
first project prompt, it requires the exact `<slug>-planner` Pi name and verifies
|
|
105
|
+
the `<slug>_planner` Herdr identity, managed interactive readiness, foreground
|
|
106
|
+
worktree, and matching native session reference from Herdr's Pi lifecycle
|
|
107
|
+
integration. It confirms `approve_stream_change`,
|
|
108
|
+
`dispatch_stream_implementer`, `rework_stream_implementer`, `finish_stream_implementer`, and `next_stream_change` are active. Missing or
|
|
101
109
|
incompatible activation, identity mismatch, or inactive tools stops before
|
|
102
|
-
`/change
|
|
110
|
+
`/change`. During session replacement, activation allows a brief bounded wait for
|
|
111
|
+
an otherwise-valid Herdr identity to publish the current native Pi reference;
|
|
112
|
+
it never waits on a wrong name, process, lifecycle source, or worktree. Global
|
|
113
|
+
installation of Codeless's extension is unnecessary.
|
|
103
114
|
The approval tool has no arguments. Its extension derives the active
|
|
104
115
|
`<slug>-planner` Pi session and passes it to the backing CLI, which requires it
|
|
105
|
-
to match the worktree and branch.
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
to match the clean stream worktree and branch. That stream commit is the
|
|
117
|
+
proposal's base; approval does not compare it with a concurrently advancing
|
|
118
|
+
integration branch. The CLI validates the proposal, exclusively creates the next
|
|
119
|
+
monotonic `changes/NNN.md`, and records one hash-backed journal approval. Landing
|
|
120
|
+
later owns locked synchronization and any required rebase onto integration. Exact retries reconcile a missing
|
|
108
121
|
file or journal step; conflicting state stops. Approval returns the numbered path
|
|
109
122
|
but does not dispatch it. Dispatch uses the package executable, inherits the
|
|
110
123
|
planner's worktree, and queues review only on success. Pi loads the extension
|
|
@@ -121,7 +134,7 @@ sending `/change` after resources reload. The previous conversation is not copie
|
|
|
121
134
|
journal and project files carry context.
|
|
122
135
|
|
|
123
136
|
This uses Pi's `newSession({ setup, withSession })` command API, verified with
|
|
124
|
-
Pi 0.
|
|
137
|
+
Pi 0.85.1. Only the replacement context activates the selection and sends the
|
|
125
138
|
new prompt. Configuration changes take effect at the next role-session boundary,
|
|
126
139
|
not during an active planner or implementer. Review and remediation therefore
|
|
127
140
|
continue with their existing session's selection. Duplicate requests
|
|
@@ -157,8 +170,15 @@ planners or worktree shells are running.
|
|
|
157
170
|
metrics/<slug>/NNN.json # dispatch/landing times and deduplicated implementer attempts
|
|
158
171
|
```
|
|
159
172
|
|
|
160
|
-
Run `codeless init` once after configuring a project. It creates
|
|
161
|
-
|
|
173
|
+
Run `codeless init` once after configuring a project. It creates missing generic
|
|
174
|
+
`change.md`, `implement.md`, `review.md`, and `commit.md` starters in the
|
|
175
|
+
configured in-project prompt directory, reporting each as created or preserved.
|
|
176
|
+
Existing prompt files are never replaced; review and commit generated files from
|
|
177
|
+
the editable invoking checkout, then bring that commit onto the configured
|
|
178
|
+
integration branch before creating streams. Init refuses to generate missing
|
|
179
|
+
prompts when invoked from the dedicated integration checkout.
|
|
180
|
+
|
|
181
|
+
It also creates the shared state layout and dedicated integration worktree at
|
|
162
182
|
`<workspace>/worktree/<integration-branch>` without requiring Herdr. For the
|
|
163
183
|
default workspace it adds only `/.codeless/state/` to the primary checkout's
|
|
164
184
|
`.gitignore`; an absolute workspace override does not modify repository ignores.
|
|
@@ -172,36 +192,56 @@ inspect its targets before using it.
|
|
|
172
192
|
|
|
173
193
|
## Commands
|
|
174
194
|
|
|
175
|
-
Run creation
|
|
176
|
-
|
|
195
|
+
Run creation and opening from a Herdr-managed operator shell outside the target
|
|
196
|
+
planner/implementer panes. Herdr requires an available interactive shell in the
|
|
197
|
+
target pane before it can start Pi. Landing needs no Herdr session.
|
|
177
198
|
|
|
178
199
|
```sh
|
|
179
200
|
codeless init
|
|
180
201
|
codeless create <slug>
|
|
181
202
|
codeless open <slug>
|
|
182
|
-
codeless planner <slug>
|
|
183
203
|
codeless approve <planner-session>
|
|
184
204
|
codeless dispatch <numbered-change-file>
|
|
205
|
+
codeless rework <numbered-change-file> <feedback>
|
|
206
|
+
codeless finish <numbered-change-file>
|
|
185
207
|
codeless land <slug>
|
|
186
208
|
codeless next <numbered-change-file> <landed-commit>
|
|
187
209
|
codeless metrics
|
|
188
210
|
```
|
|
189
211
|
|
|
190
212
|
Slugs are lowercase kebab-case, at most 24 characters. `init` validates the
|
|
191
|
-
existing configuration and integration branch,
|
|
192
|
-
|
|
193
|
-
|
|
213
|
+
existing configuration and integration branch, validates all configured prompt
|
|
214
|
+
destinations before mutation, reports created or preserved starters plus the
|
|
215
|
+
branch, primary checkout, workspace, and integration worktree, then creates the
|
|
216
|
+
shared state directories and canonical integration worktree when absent. All other
|
|
194
217
|
commands validate their prerequisites and never bootstrap this setup. `create` starts
|
|
195
218
|
`stream/<slug>` from the integration branch and creates its local documents;
|
|
196
|
-
it refuses existing streams. `open`
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
219
|
+
it refuses existing streams. `open` reuses the existing stream workspace and
|
|
220
|
+
one- or two-pane layout. It focuses an existing managed planner without installing
|
|
221
|
+
dependencies, sending another prompt, or replacing its conversation. To restart,
|
|
222
|
+
exit Pi deliberately, leave both stream panes at their shell prompts in the
|
|
223
|
+
stream worktree, then run `codeless open <slug>` from another Herdr shell.
|
|
224
|
+
|
|
225
|
+
When a planner must be started, creation/opening validates the available shells,
|
|
226
|
+
runs the configured install command and role preflight, adds a right-hand shell
|
|
227
|
+
only when absent, and uses `herdr agent start` for named, readiness-checked Pi
|
|
228
|
+
startup. It verifies the result before sending activation. An occupied or
|
|
229
|
+
mismatched pane, unmanaged agent, ambiguous layout, or failed startup stops;
|
|
230
|
+
Codeless never takes over an existing agent. Pi's display name is separate from
|
|
231
|
+
Herdr's managed agent name. Activation verifies names and native session binding;
|
|
232
|
+
it never renames an unmanaged process. There is no direct `planner` command.
|
|
233
|
+
|
|
200
234
|
Dispatch validates the implementer selection before touching the planner's
|
|
201
235
|
right-hand pane, starts a fresh ephemeral implementer with Codeless's reporting
|
|
202
|
-
extension and its explicit Pi extension flag, and waits for completion.
|
|
203
|
-
|
|
204
|
-
|
|
236
|
+
extension and its explicit Pi extension flag, and waits for completion. Rework verifies
|
|
237
|
+
that same change's idle implementer and worktree, invokes one package-owned Pi command
|
|
238
|
+
that verifies its immutable stream/change scope, arms reporting, and submits one feedback
|
|
239
|
+
turn with the same one-hour limit, records a `rework`
|
|
240
|
+
attempt, and returns it before review is queued again. Finish verifies that identity,
|
|
241
|
+
gracefully exits it, and waits for the right pane's stream-worktree shell. Neither operation
|
|
242
|
+
replaces the agent, changes its selection, retries, or continues after a mismatch, timeout,
|
|
243
|
+
blocked agent, or ambiguous pane. Its JSON result is a normalized attempt report, which the
|
|
244
|
+
planner tool exposes before queueing review. It includes the actual settled model/thinking selection,
|
|
205
245
|
terminal text and outcome, full-session Pi usage and available Pi cost estimate,
|
|
206
246
|
timestamps, and tool/error counts; prompts, source, thinking, credentials, and
|
|
207
247
|
transcripts are not retained.
|
|
@@ -216,11 +256,17 @@ integration fast-forward, Codeless records the landed time and commit on that
|
|
|
216
256
|
change's canonical record, creating a landed record without elapsed time when
|
|
217
257
|
dispatch collection was unavailable; collection warnings never alter dispatch or
|
|
218
258
|
landing.
|
|
219
|
-
`codeless metrics` prints every recorded stream and a project total. Its
|
|
220
|
-
|
|
259
|
+
`codeless metrics` prints every recorded stream and a project total. Its first
|
|
260
|
+
table reports dispatch-to-land wall-clock time; among landed changes, records
|
|
221
261
|
without a measured duration are explicitly unavailable. Dispatched-but-unlanded
|
|
222
|
-
changes remain a separate count.
|
|
223
|
-
|
|
262
|
+
changes remain a separate count. Its second table reports implementer attempts:
|
|
263
|
+
distinct changes with rework, initial and rework turns, incomplete collection,
|
|
264
|
+
exact stored terminal outcomes, and tool errors. Usage and cost coverage count
|
|
265
|
+
measured attempts separately from unavailable collection; token totals include
|
|
266
|
+
only measured usage, and cost totals remain grouped by recorded currency without
|
|
267
|
+
conversion. These counts do not establish implementation quality or review
|
|
268
|
+
success. Metrics are prospective local observations, not journal state or a
|
|
269
|
+
recovery mechanism.
|
|
224
270
|
|
|
225
271
|
Landing requires clean stream and integration worktrees and exactly one stream
|
|
226
272
|
commit outside their merge base. It acquires `.land-lock` atomically, recording
|
|
@@ -248,8 +294,8 @@ control Pi itself. Preparation can be repeated safely after inspecting a failure
|
|
|
248
294
|
|
|
249
295
|
If validation or session replacement fails, the planner stops for operator
|
|
250
296
|
attention. Landing is already complete, and any preparation fast-forward remains
|
|
251
|
-
applied. There is no background retry;
|
|
252
|
-
journal and Git state.
|
|
297
|
+
applied. There is no background retry; exit Pi and reopen the stream from another
|
|
298
|
+
Herdr shell to recover from the journal and Git state.
|
|
253
299
|
|
|
254
300
|
## Package development
|
|
255
301
|
|
|
@@ -74,23 +74,89 @@ function write(path, value) {
|
|
|
74
74
|
export default function implementerReportingExtension(pi) {
|
|
75
75
|
pi.registerFlag("codeless-attempt", { type: "string" });
|
|
76
76
|
let configuration;
|
|
77
|
+
let startupScope;
|
|
78
|
+
let rearm;
|
|
77
79
|
let startedAt;
|
|
80
|
+
let entryOffset = 0;
|
|
78
81
|
let toolCalls = 0;
|
|
79
82
|
let errorCount = 0;
|
|
83
|
+
|
|
84
|
+
function configure(value, offset = 0) {
|
|
85
|
+
if (
|
|
86
|
+
typeof value?.path !== "string" ||
|
|
87
|
+
typeof value?.id !== "string" ||
|
|
88
|
+
typeof value?.stream !== "string" ||
|
|
89
|
+
typeof value?.change !== "string" ||
|
|
90
|
+
!["initial", "rework"].includes(value?.kind)
|
|
91
|
+
)
|
|
92
|
+
throw new Error("Codeless attempt configuration is invalid");
|
|
93
|
+
return { value, offset };
|
|
94
|
+
}
|
|
80
95
|
pi.on("session_start", () => {
|
|
81
96
|
try {
|
|
82
97
|
const value = JSON.parse(pi.getFlag("codeless-attempt") ?? "");
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
typeof value?.id === "string" &&
|
|
86
|
-
typeof value?.stream === "string" &&
|
|
87
|
-
typeof value?.change === "string"
|
|
88
|
-
)
|
|
89
|
-
configuration = value;
|
|
98
|
+
configuration = configure(value).value;
|
|
99
|
+
startupScope = { stream: configuration.stream, change: configuration.change };
|
|
90
100
|
} catch {}
|
|
91
101
|
});
|
|
102
|
+
function requireScope(value) {
|
|
103
|
+
if (
|
|
104
|
+
startupScope === undefined ||
|
|
105
|
+
value?.stream !== startupScope.stream ||
|
|
106
|
+
value?.change !== startupScope.change
|
|
107
|
+
)
|
|
108
|
+
throw new Error("Codeless stream/change scope does not match this implementer session");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pi.registerCommand("codeless-rework", {
|
|
112
|
+
description: "Submit one scope-verified Codeless implementer rework turn",
|
|
113
|
+
handler: async (args, ctx) => {
|
|
114
|
+
let value;
|
|
115
|
+
try {
|
|
116
|
+
value = JSON.parse(args);
|
|
117
|
+
} catch {
|
|
118
|
+
throw new Error("Codeless rework requires one JSON-quoted request");
|
|
119
|
+
}
|
|
120
|
+
requireScope(value);
|
|
121
|
+
if (typeof value?.feedback !== "string" || value.feedback.trim().length === 0)
|
|
122
|
+
throw new Error("Codeless rework requires concise non-empty feedback");
|
|
123
|
+
const next = configure(value, ctx.sessionManager.getEntries().length);
|
|
124
|
+
if (next.value.kind !== "rework") throw new Error("Codeless rework attempt must be rework");
|
|
125
|
+
configuration = undefined;
|
|
126
|
+
rearm = next;
|
|
127
|
+
try {
|
|
128
|
+
pi.sendUserMessage(`Review feedback: ${value.feedback.trim()}`, { deliverAs: "followUp" });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
rearm = undefined;
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
pi.registerCommand("codeless-finish", {
|
|
137
|
+
description: "Gracefully finish a scope-verified Codeless implementer",
|
|
138
|
+
handler: async (args, ctx) => {
|
|
139
|
+
let value;
|
|
140
|
+
try {
|
|
141
|
+
value = JSON.parse(args);
|
|
142
|
+
} catch {
|
|
143
|
+
throw new Error("Codeless finish requires one JSON-quoted scope");
|
|
144
|
+
}
|
|
145
|
+
requireScope(value);
|
|
146
|
+
await ctx.shutdown();
|
|
147
|
+
},
|
|
148
|
+
});
|
|
92
149
|
pi.on("agent_start", () => {
|
|
93
|
-
|
|
150
|
+
if (rearm !== undefined) {
|
|
151
|
+
configuration = rearm.value;
|
|
152
|
+
entryOffset = rearm.offset;
|
|
153
|
+
rearm = undefined;
|
|
154
|
+
startedAt = new Date().toISOString();
|
|
155
|
+
toolCalls = 0;
|
|
156
|
+
errorCount = 0;
|
|
157
|
+
} else {
|
|
158
|
+
startedAt ??= new Date().toISOString();
|
|
159
|
+
}
|
|
94
160
|
});
|
|
95
161
|
pi.on("tool_execution_end", (event) => {
|
|
96
162
|
toolCalls += 1;
|
|
@@ -100,9 +166,10 @@ export default function implementerReportingExtension(pi) {
|
|
|
100
166
|
if (configuration === undefined) return;
|
|
101
167
|
const report = configuration;
|
|
102
168
|
configuration = undefined;
|
|
103
|
-
const
|
|
169
|
+
const entries = ctx.sessionManager.getEntries().slice(entryOffset);
|
|
170
|
+
const settledMessages = messages(entries);
|
|
104
171
|
const final = [...settledMessages].reverse().find((message) => message.role === "assistant");
|
|
105
|
-
const totals = sessionUsage(
|
|
172
|
+
const totals = sessionUsage(entries);
|
|
106
173
|
const model = final?.responseModel ?? final?.model ?? ctx.model?.id;
|
|
107
174
|
const provider = final?.provider ?? ctx.model?.provider;
|
|
108
175
|
const selection =
|
|
@@ -123,6 +190,7 @@ export default function implementerReportingExtension(pi) {
|
|
|
123
190
|
stream: report.stream,
|
|
124
191
|
change: report.change,
|
|
125
192
|
role: "implementer",
|
|
193
|
+
kind: report.kind,
|
|
126
194
|
startedAt: startedAt ?? new Date().toISOString(),
|
|
127
195
|
endedAt: new Date().toISOString(),
|
|
128
196
|
...(selection === undefined ? {} : { selection }),
|
package/extension/planner.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { resolve } from "node:path";
|
|
2
3
|
import { validAttempt } from "../src/attempt.ts";
|
|
3
4
|
|
|
4
5
|
const codeless = fileURLToPath(new URL("../bin/codeless", import.meta.url));
|
|
5
6
|
const thinkingLevels = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
7
|
+
const nativeSessionAttempts = 20;
|
|
8
|
+
const nativeSessionRetryDelayMs = 50;
|
|
6
9
|
const requiredTools = [
|
|
7
10
|
"approve_stream_change",
|
|
8
11
|
"dispatch_stream_implementer",
|
|
12
|
+
"rework_stream_implementer",
|
|
13
|
+
"finish_stream_implementer",
|
|
9
14
|
"next_stream_change",
|
|
10
15
|
];
|
|
11
16
|
|
|
@@ -91,24 +96,47 @@ export default function plannerExtension(pi) {
|
|
|
91
96
|
throw new Error("Herdr returned an invalid planner identity response");
|
|
92
97
|
}
|
|
93
98
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
if (renamed.code !== 0) {
|
|
99
|
+
const attempts = activation ? nativeSessionAttempts : 1;
|
|
100
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
101
|
+
const agent = await plannerIdentity();
|
|
102
|
+
if (agent?.name !== expectedPlanner) {
|
|
100
103
|
throw new Error(
|
|
101
|
-
|
|
102
|
-
renamed.stdout.trim() ||
|
|
103
|
-
"Codeless could not establish Herdr planner identity",
|
|
104
|
+
`Codeless planner identity is ${agent?.name ?? "missing"}, expected ${expectedPlanner}; exit this agent and run codeless open ${match[1]} from another Herdr shell`,
|
|
104
105
|
);
|
|
105
106
|
}
|
|
106
|
-
agent
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
if (agent.agent !== "pi" || agent.interactive_ready !== true) {
|
|
108
|
+
throw new Error("Codeless requires a Herdr-managed Pi planner started by codeless open");
|
|
109
|
+
}
|
|
110
|
+
if (
|
|
111
|
+
typeof agent.foreground_cwd !== "string" ||
|
|
112
|
+
resolve(agent.foreground_cwd) !== resolve(ctx.cwd)
|
|
113
|
+
) {
|
|
114
|
+
throw new Error("Codeless planner worktree does not match Herdr's foreground cwd");
|
|
115
|
+
}
|
|
116
|
+
const session = agent.agent_session;
|
|
117
|
+
const expectedSession =
|
|
118
|
+
session?.kind === "path"
|
|
119
|
+
? ctx.sessionManager.getSessionFile()
|
|
120
|
+
: session?.kind === "id"
|
|
121
|
+
? ctx.sessionManager.getSessionId()
|
|
122
|
+
: undefined;
|
|
123
|
+
if (
|
|
124
|
+
agent.screen_detection_skipped !== true ||
|
|
125
|
+
session?.source !== "herdr:pi" ||
|
|
126
|
+
session.agent !== "pi" ||
|
|
127
|
+
!expectedSession
|
|
128
|
+
) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
"Codeless planner native session does not match Herdr's Pi lifecycle integration",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
if (session.value === expectedSession) break;
|
|
134
|
+
if (attempt === attempts - 1) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
"Codeless planner native session does not match Herdr's Pi lifecycle integration",
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
await new Promise((resolveDelay) => setTimeout(resolveDelay, nativeSessionRetryDelayMs));
|
|
112
140
|
}
|
|
113
141
|
const activeTools = ctx.getSystemPromptOptions().selectedTools ?? [];
|
|
114
142
|
const missing = requiredTools.filter((tool) => !activeTools.includes(tool));
|
|
@@ -290,6 +318,98 @@ export default function plannerExtension(pi) {
|
|
|
290
318
|
},
|
|
291
319
|
});
|
|
292
320
|
|
|
321
|
+
pi.registerTool({
|
|
322
|
+
name: "rework_stream_implementer",
|
|
323
|
+
label: "Remediate stream implementation",
|
|
324
|
+
description:
|
|
325
|
+
"Reuse the idle implementer for this approved change, submit one actionable feedback turn, collect its rework attempt, and queue review again.",
|
|
326
|
+
promptSnippet: "Send one concise remediation request to the existing stream implementer",
|
|
327
|
+
promptGuidelines: [
|
|
328
|
+
"Call rework_stream_implementer only when review finds actionable defects in the approved change. Use its approved changePath and concise feedback; never reproduce Herdr commands.",
|
|
329
|
+
"The tool queues review only after a settled rework attempt. Stop on any error and do not retry automatically.",
|
|
330
|
+
],
|
|
331
|
+
parameters: {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {
|
|
334
|
+
changePath: {
|
|
335
|
+
type: "string",
|
|
336
|
+
description: "Absolute path to the approved changes/NNN.md file",
|
|
337
|
+
},
|
|
338
|
+
feedback: { type: "string", description: "Concise actionable review feedback" },
|
|
339
|
+
},
|
|
340
|
+
required: ["changePath", "feedback"],
|
|
341
|
+
additionalProperties: false,
|
|
342
|
+
},
|
|
343
|
+
async execute(_toolCallId, params, signal) {
|
|
344
|
+
const changePath = params.changePath.replace(/^@/, "");
|
|
345
|
+
const execution = await pi.exec("bun", [codeless, "rework", changePath, params.feedback], {
|
|
346
|
+
signal,
|
|
347
|
+
timeout: 3_700_000,
|
|
348
|
+
});
|
|
349
|
+
const output = [execution.stdout.trim(), execution.stderr.trim()]
|
|
350
|
+
.filter(Boolean)
|
|
351
|
+
.join("\n");
|
|
352
|
+
if (execution.code !== 0)
|
|
353
|
+
throw new Error(output || `codeless rework failed with exit code ${execution.code}`);
|
|
354
|
+
let attempt;
|
|
355
|
+
try {
|
|
356
|
+
attempt = JSON.parse(execution.stdout);
|
|
357
|
+
} catch {
|
|
358
|
+
throw new Error("Codeless returned an invalid rework attempt");
|
|
359
|
+
}
|
|
360
|
+
if (!validAttempt(attempt, attempt?.stream, attempt?.change) || attempt.kind !== "rework")
|
|
361
|
+
throw new Error("Codeless returned an invalid rework attempt");
|
|
362
|
+
pi.sendUserMessage(`/review ${JSON.stringify(changePath)}`, {
|
|
363
|
+
deliverAs: "steer",
|
|
364
|
+
expandPromptTemplates: true,
|
|
365
|
+
});
|
|
366
|
+
return {
|
|
367
|
+
content: [{ type: "text", text: attempt.text || "Implementer rework settled." }],
|
|
368
|
+
details: { changePath, attempt },
|
|
369
|
+
};
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
pi.registerTool({
|
|
374
|
+
name: "finish_stream_implementer",
|
|
375
|
+
label: "Finish stream implementer",
|
|
376
|
+
description:
|
|
377
|
+
"Gracefully exit the verified idle implementer after review approval and confirm its right-hand pane returned to the stream shell.",
|
|
378
|
+
promptSnippet: "Finish the approved stream implementer before commit and landing",
|
|
379
|
+
promptGuidelines: [
|
|
380
|
+
"Call finish_stream_implementer exactly once after recording review approval and before following commit-and-land instructions.",
|
|
381
|
+
"Stop on failure; do not use Herdr commands or continue to commit and land.",
|
|
382
|
+
],
|
|
383
|
+
parameters: {
|
|
384
|
+
type: "object",
|
|
385
|
+
properties: {
|
|
386
|
+
changePath: {
|
|
387
|
+
type: "string",
|
|
388
|
+
description: "Absolute path to the approved changes/NNN.md file",
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
required: ["changePath"],
|
|
392
|
+
additionalProperties: false,
|
|
393
|
+
},
|
|
394
|
+
async execute(_toolCallId, params, signal) {
|
|
395
|
+
const changePath = params.changePath.replace(/^@/, "");
|
|
396
|
+
const execution = await pi.exec("bun", [codeless, "finish", changePath], {
|
|
397
|
+
signal,
|
|
398
|
+
timeout: 35_000,
|
|
399
|
+
});
|
|
400
|
+
const output = [execution.stdout.trim(), execution.stderr.trim()]
|
|
401
|
+
.filter(Boolean)
|
|
402
|
+
.join("\n");
|
|
403
|
+
if (execution.code !== 0)
|
|
404
|
+
throw new Error(output || `codeless finish failed with exit code ${execution.code}`);
|
|
405
|
+
return {
|
|
406
|
+
content: [
|
|
407
|
+
{ type: "text", text: "Implementer exited and its pane returned to the stream shell." },
|
|
408
|
+
],
|
|
409
|
+
};
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
|
|
293
413
|
pi.registerTool({
|
|
294
414
|
name: "dispatch_stream_implementer",
|
|
295
415
|
label: "Dispatch stream implementer",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dpeek/codeless",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "An attended planner and implementer workflow for parallel capability development",
|
|
5
5
|
"homepage": "https://github.com/dpeek/codeless#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"bin",
|
|
19
19
|
"extension",
|
|
20
|
+
"prompts",
|
|
20
21
|
"src",
|
|
21
22
|
"README.md",
|
|
22
23
|
"spec"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Propose and coordinate the next small change for a stream
|
|
3
|
+
argument-hint: "<stream-directory> <direction-file>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the planner for the stream at `$1`. The current working directory is the stream's repository worktree.
|
|
7
|
+
|
|
8
|
+
Read the repository guidance, `.codeless/config.json`, `$1/planner.md`, every numbered Markdown file in `$1/changes/`, `$2`, its related contracts, and the current implementation relevant to the stream. Resolve the integration branch from that configuration.
|
|
9
|
+
|
|
10
|
+
Before proposing, inspect the branch, recent commits, and worktree. If changes are not explained as an active approved change in `planner.md`, stop and show the operator the evidence. Never discard work automatically.
|
|
11
|
+
|
|
12
|
+
If the latest numbered change is approved but uncommitted, resume it. If it is committed but unlanded on the configured integration branch, resume review or landing. Otherwise require a clean worktree with no commits outside the configured integration branch, fast-forward to that branch, and reread the direction and affected contracts. Stop on divergence. Once planning begins, keep that stream commit as the proposal's base; do not resynchronize merely because integration advances while the proposal awaits approval. Locked landing owns the later rebase.
|
|
13
|
+
|
|
14
|
+
Propose exactly one small, complete change and write it to `$1/change.md`:
|
|
15
|
+
|
|
16
|
+
```markdown
|
|
17
|
+
# <short outcome>
|
|
18
|
+
|
|
19
|
+
## Why
|
|
20
|
+
|
|
21
|
+
## Change
|
|
22
|
+
|
|
23
|
+
## Acceptance
|
|
24
|
+
|
|
25
|
+
## Decisions
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Under `Decisions`, record only choices that matter now; write `None` when there are none.
|
|
29
|
+
|
|
30
|
+
Present the proposal and wait for the operator:
|
|
31
|
+
|
|
32
|
+
- `go`: call `approve_stream_change` exactly once. It validates and promotes the proposal, records one approval, and returns its number, title, and absolute numbered change path. After it succeeds, call `dispatch_stream_implementer` exactly once with that returned change path.
|
|
33
|
+
- `no`: append the rejected proposal and reason to `planner.md`; do not allocate a numbered change.
|
|
34
|
+
- other feedback: revise `change.md`, recheck the relevant state, and present it again.
|
|
35
|
+
|
|
36
|
+
Approval is separate from dispatch. Do not edit approval state by hand or reproduce the dispatch tool's worktree, pane, or agent operations. If dispatch returns an error, stop and show the operator the exact error; do not retry automatically.
|
|
37
|
+
|
|
38
|
+
After the implementer settles, follow the queued `/review <absolute-numbered-change-path>` instruction. Do not finish after merely reporting that implementation settled. Keep this implementer alive for every remediation round belonging to the same numbered change.
|
|
39
|
+
|
|
40
|
+
The implementation updates code, focused tests, owning contracts, and the direction; remove satisfied todo intentions. If no worthwhile work remains, say so.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Commit one reviewed stream change
|
|
3
|
+
argument-hint: "<numbered-change-file>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Commit the reviewed implementation of `$1` on the current stream branch.
|
|
7
|
+
|
|
8
|
+
Before committing or landing:
|
|
9
|
+
|
|
10
|
+
- confirm `planner.md` records review approval for this change;
|
|
11
|
+
- inspect the complete diff for unrelated edits;
|
|
12
|
+
- confirm every acceptance criterion is met;
|
|
13
|
+
- run the relevant checks if their passing result is not current;
|
|
14
|
+
- determine whether this reviewed change already has its one commit because a previous landing attempt stopped.
|
|
15
|
+
|
|
16
|
+
If the change is not committed, create exactly one commit. Use an imperative subject that describes the resulting behavior. Add a short body only for a non-obvious design decision. If the reviewed change already has its one unlanded commit, do not create another.
|
|
17
|
+
|
|
18
|
+
Read `.codeless/config.json` to resolve the configured integration branch. Derive `<slug>` from the numbered change path and land it with:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
codeless land <slug>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Replace the placeholder with the literal stream slug and do not include angle brackets. This command owns the shared integration lock, rebases the one stream commit when the configured integration branch advanced, runs the configured checks, and fast-forwards the dedicated integration checkout. It does not modify other branches or checkouts.
|
|
25
|
+
|
|
26
|
+
If landing succeeds, append the full commit hash and subject plus a `landed on <configured-integration-branch>` note to the stream's `planner.md`. Include any decisions the next planner needs before resetting the session.
|
|
27
|
+
|
|
28
|
+
Then call `next_stream_change` exactly once with `changePath` set to the absolute path of `$1` and `landedCommit` set to that full hash. Finish this turn after requesting the handoff. The tool validates the completed landing, fast-forwards the stream to the current configured integration branch, and starts a fresh planner session with the project's `/change` prompt. Do not propose the next change in this session or send `/new` through shell keystrokes.
|
|
29
|
+
|
|
30
|
+
The next planner proposes one change and waits for the operator's `go`; this handoff does not approve further implementation. If no worthwhile work remains, it reports that and stops. If handoff validation fails or the reset is cancelled, stop and report the state; do not retry automatically. Landing remains complete even if the handoff fails.
|
|
31
|
+
|
|
32
|
+
There are two expected landing stops:
|
|
33
|
+
|
|
34
|
+
- If another stream owns the integration slot, leave this stream committed where it is, report the owner, and wait. Do not poll, queue, or retry automatically.
|
|
35
|
+
- If this stream owns the slot and the rebase conflicts, keep the slot. Resolve the conflicts in this worktree so both the current configured integration branch and the approved change are preserved, stage the resolutions, and continue the rebase with `GIT_EDITOR=true git rebase --continue`. Repeat until the rebase completes, run the relevant checks, then run `codeless land <slug>` again to finish. The slot prevents another automated landing from moving the integration branch while you resolve it.
|
|
36
|
+
|
|
37
|
+
For any other failure while this stream owns the slot, report the exact state and wait for the operator. The slot remains held for deliberate recovery; never remove the workspace's landing lock automatically or on guesswork.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Implement one approved stream change
|
|
3
|
+
argument-hint: "<numbered-change-file>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the implementer. Implement only the approved change in `$1` in the current worktree.
|
|
7
|
+
|
|
8
|
+
Read the numbered change, repository guidance, and the smallest relevant part of the codebase. Preserve unrelated work.
|
|
9
|
+
|
|
10
|
+
- Do not edit the planner journal, current proposal, or numbered changes beside `$1`.
|
|
11
|
+
- Do not commit, rebase, merge, switch branches, or reset files.
|
|
12
|
+
- Update the owning contracts and todo with the implementation.
|
|
13
|
+
- Add or update focused tests for changed behavior.
|
|
14
|
+
- Run the smallest useful checks.
|
|
15
|
+
- If the change cannot be completed safely, stop and explain the blocker with evidence.
|
|
16
|
+
|
|
17
|
+
When finished, report what changed, checks run and their results, and assumptions or remaining concerns.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Review the worktree against one approved stream change
|
|
3
|
+
argument-hint: "<numbered-change-file>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the planner reviewing the implementation of `$1`.
|
|
7
|
+
|
|
8
|
+
Read the approved change and repository guidance. Inspect the complete worktree diff and relevant surrounding code. Review for correctness and regressions, unmet acceptance criteria, missing or weak tests, accidental scope growth, and consistency with the repository's domain model and style.
|
|
9
|
+
|
|
10
|
+
Run focused checks when the implementation report is insufficient evidence.
|
|
11
|
+
|
|
12
|
+
If remediation is required, call `rework_stream_implementer` once with `$1` as `changePath` and a concise actionable feedback list. The tool reuses the verified idle implementer, waits for its one feedback turn, returns its rework attempt, and queues review again. Do not reproduce agent operations or retry automatically.
|
|
13
|
+
|
|
14
|
+
If operator judgment is required, stop and ask one concrete question. If the change is ready:
|
|
15
|
+
|
|
16
|
+
1. append a compact approval and verification summary to the stream's `planner.md`;
|
|
17
|
+
2. call `finish_stream_implementer` once with `$1` as `changePath`; it gracefully exits the verified implementer and confirms its pane returned to the stream shell;
|
|
18
|
+
3. only after that tool succeeds, read `.codeless/config.json`, resolve its `prompts` directory, and immediately follow that directory's `commit.md` instructions for `$1`.
|
|
19
|
+
|
|
20
|
+
Do not finish the implementer before approval because remediation for this numbered change retains its context. If the finish tool fails, stop and report its error.
|