buildline 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/features.js +2 -1
- package/src/install.js +1 -0
- package/templates/AGENTS.block.md +3 -1
- package/templates/agents/buildline-design-reviewer.md +1 -1
- package/templates/agents/buildline-mapper.md +1 -1
- package/templates/agents/buildline-reviewer.md +1 -1
- package/templates/agents/buildline-spec-critic.md +1 -1
- package/templates/agents/buildline-verifier.md +1 -1
- package/templates/bin/hook.mjs +17 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buildline",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Install an AI software factory into any project. A spec-driven pipeline for Claude Code (and any AGENTS.md-compatible agent), for developers, designers and product managers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/features.js
CHANGED
|
@@ -214,7 +214,8 @@ export function readActivity(root, limit = 200) {
|
|
|
214
214
|
* Claude Code), a feature whose files changed in the last two minutes counts as worked on.
|
|
215
215
|
*/
|
|
216
216
|
export function liveState(feature, sessions, at = Date.now()) {
|
|
217
|
-
|
|
217
|
+
// A finished feature has nothing live, even if an old session is still tied to it.
|
|
218
|
+
const mine = feature.status === 'done' ? [] : sessions.filter((s) => s.feature === feature.id);
|
|
218
219
|
const working = mine.filter((s) => s.state === 'working');
|
|
219
220
|
const recent = at - new Date(feature.updated || 0) < 2 * 60e3;
|
|
220
221
|
const waiting = mine.find((s) => s.state === 'waiting');
|
package/src/install.js
CHANGED
|
@@ -125,6 +125,7 @@ export function buildEntries(root, config) {
|
|
|
125
125
|
const subagentStation = Object.fromEntries(Object.entries(STATION_SUBAGENTS).map(([st, agent]) => [`${agent}.md`, st]));
|
|
126
126
|
for (const f of fs.readdirSync(path.join(templatesDir, 'agents'))) {
|
|
127
127
|
const m = subagentStation[f] ? modelFor(config, subagentStation[f]) : null;
|
|
128
|
+
// Subagents follow the session's model (`model: inherit`) unless buildline.json picks one for their station.
|
|
128
129
|
const content = read(`agents/${f}`);
|
|
129
130
|
add(`.claude/agents/${f}`, m?.cli === 'claude' && m.model ? content.replace(/^model: .*$/m, `model: ${m.model}`) : content);
|
|
130
131
|
}
|
|
@@ -47,4 +47,6 @@ A task only gets `"passes": true` after the gates pass **and** its own verificat
|
|
|
47
47
|
7. **Ask, don't guess,** when the spec is ambiguous about user-facing behaviour.
|
|
48
48
|
8. **Record state with the feature script.** `node .buildline/bin/feature.mjs move|task|log|block|approve …` stamps real times, keeps the history the team watches live, and refuses moves that skip a required approval. Never edit `feature.json` by hand.
|
|
49
49
|
9. **Mark it blocked before you ask.** When you need an answer from a person, run `node .buildline/bin/feature.mjs block <id> "<the question>"`, then ask. When they answer, run `node .buildline/bin/feature.mjs unblock <id>` and carry on.
|
|
50
|
-
10. **Respect model routing.** `models` in `buildline.json`
|
|
50
|
+
10. **Respect model routing.** `models` in `buildline.json` says who runs each station.
|
|
51
|
+
- **Another CLI** (e.g. `"review": "codex"`): hand it off with `npx buildline step <station> <id> --headless` and summarise the result instead of doing it yourself.
|
|
52
|
+
- **Another Claude model** (e.g. `"spec": "fable"`): the station's slash command switches model by itself when the person types it. You can't switch models, and opening the station yourself keeps yours. So when the next station runs on a different model than you, end with "Next: type `/buildline-<station> <id>`, it runs on <model>." Don't ask the person to switch models first; if they ask you to run it on that model, tell them to type that command. For stations that don't need the person (build, verify, review, and in the autopilot loop), you may instead run `npx buildline step <station> <id> --headless`, which runs it on its model, and summarise the result.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: buildline-design-reviewer
|
|
3
3
|
description: Reviews UX design notes, prototypes or implemented UI (via screenshots) against the project's design system, the spec's acceptance scenarios and accessibility basics. Use at the design and review stations for any user-facing change.
|
|
4
4
|
tools: Read, Grep, Glob, Bash
|
|
5
|
-
model:
|
|
5
|
+
model: inherit
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
You are a senior product designer reviewing a change for consistency, clarity and accessibility.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: buildline-mapper
|
|
3
3
|
description: Maps an existing codebase (product purpose, architecture, conventions, design system, commands, hazards) into a compact summary for the factory's context files. Use during onboarding of an existing project or when the context files are stale.
|
|
4
4
|
tools: Read, Grep, Glob, Bash
|
|
5
|
-
model:
|
|
5
|
+
model: inherit
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
You are a staff engineer joining a new codebase. Produce the summary you'd want on your first day: dense, factual, with file paths as evidence. Read, don't modify anything.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: buildline-reviewer
|
|
3
3
|
description: Reviews a factory feature's diff against its spec and the project's principles for spec fit, correctness, security, maintainability and test quality. Use at the review station.
|
|
4
4
|
tools: Read, Grep, Glob, Bash
|
|
5
|
-
model:
|
|
5
|
+
model: inherit
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
You are a senior engineer doing a pre-merge review. Your goal is to find the problems that matter, not to show thoroughness.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: buildline-spec-critic
|
|
3
3
|
description: Critiques a factory feature spec (spec.md) for untestable requirements, missing unhappy paths, contradictions and hidden scope. Use after writing or changing a spec, before design or planning.
|
|
4
4
|
tools: Read, Grep, Glob
|
|
5
|
-
model:
|
|
5
|
+
model: inherit
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
You are a sharp, constructive product reviewer. You review one `spec.md` from `.buildline/features/<id>-<slug>/`. Also read `.buildline/context/product.md` and `.buildline/principles.md` for context.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: buildline-verifier
|
|
3
3
|
description: Independently verifies a factory feature against its acceptance scenarios by running gates, the app, tests and browser checks, then reports PASS/FAIL with evidence. Use at the verify station; never the same context that built the feature.
|
|
4
4
|
tools: Read, Grep, Glob, Bash, Write
|
|
5
|
-
model:
|
|
5
|
+
model: inherit
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
You are an independent QA engineer. You did not build this feature and you don't trust claims you haven't observed yourself.
|
package/templates/bin/hook.mjs
CHANGED
|
@@ -113,6 +113,17 @@ function promptDetail(prompt) {
|
|
|
113
113
|
return done ? oneLine(done[1], 90) : oneLine(prompt, 60);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/** A feature.mjs command that changes a feature's state, e.g. "feature.mjs task 3 T1 pass". */
|
|
117
|
+
function actedOn(command) {
|
|
118
|
+
const m = /feature\.mjs\s+(?:move|task|log|block|unblock|approve)\s+(\d{1,4})\b/.exec(String(command || ''));
|
|
119
|
+
return m ? m[1].padStart(3, '0') : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isDone(id) {
|
|
123
|
+
const dir = fs.existsSync(featuresDir) ? fs.readdirSync(featuresDir).find((d) => d.startsWith(`${id}-`)) : null;
|
|
124
|
+
return dir ? readJson(path.join(featuresDir, dir, 'feature.json'))?.status === 'done' : false;
|
|
125
|
+
}
|
|
126
|
+
|
|
116
127
|
function detail(input) {
|
|
117
128
|
const ti = input.tool_input || {};
|
|
118
129
|
if (ti.file_path) return path.basename(ti.file_path);
|
|
@@ -189,12 +200,15 @@ function heartbeat(input, event) {
|
|
|
189
200
|
const prompt = input.prompt || '';
|
|
190
201
|
const station = /\/buildline-([a-z]+)/.exec(prompt)?.[1];
|
|
191
202
|
const ti = input.tool_input || {};
|
|
192
|
-
// A session stays on the feature it was started for; reading another feature's files doesn't move it
|
|
193
|
-
|
|
203
|
+
// A session stays on the feature it was started for; reading another feature's files doesn't move it,
|
|
204
|
+
// recording progress on one does. It lets go once its feature is done or the conversation is cleared,
|
|
205
|
+
// so a reopened session doesn't keep flagging a finished feature.
|
|
206
|
+
const bound = prev.feature && !isDone(prev.feature) && !(event === 'start' && input.source === 'clear') ? prev.feature : null;
|
|
207
|
+
const feature = featureRef(prompt) || actedOn(ti.command) || bound || featureRef(ti.file_path) || featureRef(ti.command) || featureRef(ti.prompt) || null;
|
|
194
208
|
const next = {
|
|
195
209
|
...prev,
|
|
196
210
|
feature,
|
|
197
|
-
station: station || prev.station || null,
|
|
211
|
+
station: station || (feature === prev.feature ? prev.station : null) || null,
|
|
198
212
|
model: input.model?.id || input.model || prev.model || null,
|
|
199
213
|
...nextState(prev, input, event),
|
|
200
214
|
lastAt: at,
|