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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buildline",
3
- "version": "0.8.0",
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
- const mine = sessions.filter((s) => s.feature === feature.id);
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` can send a station to another tool (e.g. `"review": "codex"`). If a station is routed to a different CLI than the one you are, hand it off with `npx buildline step <station> <id> --headless` and summarise the result instead of doing it yourself.
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: sonnet
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: sonnet
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: opus
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: sonnet
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: sonnet
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.
@@ -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
- const feature = featureRef(prompt) || prev.feature || featureRef(ti.file_path) || featureRef(ti.command) || featureRef(ti.prompt) || null;
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,