@boopkit/cli 0.8.1 → 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/README.md +2 -2
- package/dist/capability-versions.json +10 -10
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +12 -5
- package/dist/commands/loop.d.ts +5 -1
- package/dist/commands/loop.js +37 -18
- package/dist/lib/execute.js +11 -1
- package/dist/lib/stale-scripts.js +54 -14
- package/docs/loop.md +2 -0
- package/docs/skills.md +8 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -147,7 +147,7 @@ Adding a capability installs its skills into your project's `skills/` directory.
|
|
|
147
147
|
**Development capabilities (skills only — no runtime code)**
|
|
148
148
|
|
|
149
149
|
- **`coding`** — boop-coding-beads-flow, boop-coding-development, boop-coding-commit-changes, boop-coding-review-changes, boop-coding-debugging
|
|
150
|
-
- **`product-design`** — boop-product-design-engineering-design, boop-product-design-feature-documentation, boop-product-design-feature-requirements, boop-product-design-reference-integration, boop-product-design-knowledge-backfill, boop-product-design-thought-partnership, boop-writing-technical-prose, boop-writing-documents, boop-writing-semantic-line-breaks
|
|
150
|
+
- **`product-design`** — boop-product-design-engineering-design, boop-product-design-feature-documentation, boop-product-design-feature-requirements, boop-product-design-reference-integration, boop-product-design-knowledge-backfill, boop-product-design-docs-sweep-workflow, boop-product-design-reference-restructure-workflow, boop-product-design-thought-partnership, boop-writing-technical-prose, boop-writing-documents, boop-writing-semantic-line-breaks
|
|
151
151
|
- **`project-mgmt`** — boop-project-mgmt-beads-planning, boop-project-mgmt-engineering-planning, boop-project-mgmt-issue-sweep-workflow, boop-project-mgmt-stray-disposition
|
|
152
152
|
- **`skills`** — boop-skill-authoring, boop-skills-discovery, boop-skills-agent-surface-design, boop-get-smart, boop-so-what, boop-skills-hyperlinking, boop-skills-edit-triage
|
|
153
153
|
- **`testing`** — boop-testing-tdd-light, boop-testing-testing
|
|
@@ -164,7 +164,7 @@ Adding a capability installs its skills into your project's `skills/` directory.
|
|
|
164
164
|
- **`email`** — boop-email-email-design
|
|
165
165
|
- **`services`** — boop-services-controllers, boop-services-services
|
|
166
166
|
- **`users-and-accounts`** — boop-users-user-data-modeling
|
|
167
|
-
- **`web`** — boop-web-web-ui-design, boop-web-testing, boop-web-website-dev-workflow, boop-web-design-handoff-workflow
|
|
167
|
+
- **`web`** — boop-web-web-ui-design, boop-web-css, boop-web-testing, boop-web-website-dev-workflow, boop-web-design-handoff-workflow, boop-web-design-handoff-bundle
|
|
168
168
|
|
|
169
169
|
_One-line description of each skill: see the bundled `docs/skills.md`._
|
|
170
170
|
<!-- SKILLS-INDEX:END -->
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"@boopkit/cap-api": "0.4.5",
|
|
3
3
|
"@boopkit/cap-background-jobs": "0.5.1",
|
|
4
4
|
"@boopkit/cap-cloud-storage": "0.5.0",
|
|
5
|
-
"@boopkit/cap-coding": "0.4.
|
|
5
|
+
"@boopkit/cap-coding": "0.4.9",
|
|
6
6
|
"@boopkit/cap-database": "0.4.6",
|
|
7
7
|
"@boopkit/cap-email": "0.4.4",
|
|
8
8
|
"@boopkit/cap-hello": "0.4.1",
|
|
9
9
|
"@boopkit/cap-noticeboard": "0.4.1",
|
|
10
10
|
"@boopkit/cap-oauth-client": "0.1.1",
|
|
11
|
-
"@boopkit/cap-oauth-server": "0.3.
|
|
12
|
-
"@boopkit/cap-product-design": "0.6.
|
|
13
|
-
"@boopkit/cap-project-mgmt": "0.4.
|
|
11
|
+
"@boopkit/cap-oauth-server": "0.3.3",
|
|
12
|
+
"@boopkit/cap-product-design": "0.6.13",
|
|
13
|
+
"@boopkit/cap-project-mgmt": "0.4.15",
|
|
14
14
|
"@boopkit/cap-services": "0.4.3",
|
|
15
|
-
"@boopkit/cap-skills": "0.5.
|
|
16
|
-
"@boopkit/cap-testing": "0.5.
|
|
15
|
+
"@boopkit/cap-skills": "0.5.8",
|
|
16
|
+
"@boopkit/cap-testing": "0.5.10",
|
|
17
17
|
"@boopkit/cap-typescript": "0.1.0",
|
|
18
|
-
"@boopkit/cap-users": "0.9.
|
|
19
|
-
"@boopkit/cap-ux": "0.2.
|
|
20
|
-
"@boopkit/cap-web": "0.5.
|
|
21
|
-
"@boopkit/cap-workflows": "0.2.
|
|
18
|
+
"@boopkit/cap-users": "0.9.1",
|
|
19
|
+
"@boopkit/cap-ux": "0.2.5",
|
|
20
|
+
"@boopkit/cap-web": "0.5.8",
|
|
21
|
+
"@boopkit/cap-workflows": "0.2.4",
|
|
22
22
|
"@boopkit/logger": "0.4.0"
|
|
23
23
|
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -17,5 +17,10 @@ export interface InitDeps {
|
|
|
17
17
|
* pins @beads/bd as a devDependency, then whatever is on PATH.
|
|
18
18
|
*/
|
|
19
19
|
export declare function findBd(projectDir: string, searchPath?: string): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* The `bd init` arguments that give a project a beads database in Dolt server
|
|
22
|
+
* mode, which boop loop requires. bd derives its database name from the prefix.
|
|
23
|
+
*/
|
|
24
|
+
export declare function bdInitArgs(projectName: string): string[];
|
|
20
25
|
export declare function runInit(name: string, options: InitOptions, deps?: InitDeps): void;
|
|
21
26
|
export {};
|
package/dist/commands/init.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.findBd = findBd;
|
|
37
|
+
exports.bdInitArgs = bdInitArgs;
|
|
37
38
|
exports.runInit = runInit;
|
|
38
39
|
const fs = __importStar(require("fs"));
|
|
39
40
|
const path = __importStar(require("path"));
|
|
@@ -106,6 +107,13 @@ function commitScaffold(targetDir, name, run) {
|
|
|
106
107
|
console.error(`Warning: Unable to commit the scaffold: ${err.message}`);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* The `bd init` arguments that give a project a beads database in Dolt server
|
|
112
|
+
* mode, which boop loop requires. bd derives its database name from the prefix.
|
|
113
|
+
*/
|
|
114
|
+
function bdInitArgs(projectName) {
|
|
115
|
+
return ['init', '--non-interactive', '--server', '-p', projectName];
|
|
116
|
+
}
|
|
109
117
|
/** Commit the scaffold, then run `bd init` in the new project; report and return false rather than throw. */
|
|
110
118
|
function initBeads(targetDir, name, args, deps) {
|
|
111
119
|
const findBdFn = deps.findBdFn ?? findBd;
|
|
@@ -490,18 +498,17 @@ function runInit(name, options, deps = {}) {
|
|
|
490
498
|
// they exist but creates them when they don't, and the templates never
|
|
491
499
|
// overwrite an existing file. Server mode, because boop loop refuses a
|
|
492
500
|
// project whose beads runs embedded. Git is touched only here, just before
|
|
493
|
-
// bd init, because that is when a repository gets created anyway.
|
|
494
|
-
|
|
495
|
-
const bdInitArgs = ['init', '--non-interactive', '--server', '-p', projectName];
|
|
501
|
+
// bd init, because that is when a repository gets created anyway.
|
|
502
|
+
const initArgs = bdInitArgs(projectName);
|
|
496
503
|
const beadsPending = installedCapabilities.includes('project-mgmt') &&
|
|
497
|
-
!(shouldInitBeads && initBeads(targetDir, projectName,
|
|
504
|
+
!(shouldInitBeads && initBeads(targetDir, projectName, initArgs, deps));
|
|
498
505
|
console.log(`\nNext steps:`);
|
|
499
506
|
console.log(` cd ${name}`);
|
|
500
507
|
if (!shouldInstall) {
|
|
501
508
|
console.log(' npm install');
|
|
502
509
|
}
|
|
503
510
|
if (beadsPending) {
|
|
504
|
-
console.log(` bd ${
|
|
511
|
+
console.log(` bd ${initArgs.join(' ')}`);
|
|
505
512
|
}
|
|
506
513
|
console.log(' boop status ← check what needs configuration');
|
|
507
514
|
if (kernel) {
|
package/dist/commands/loop.d.ts
CHANGED
|
@@ -66,7 +66,10 @@ export interface WorktreeContext {
|
|
|
66
66
|
/**
|
|
67
67
|
* Hard requirement: beads must run in Dolt server mode so concurrent worktrees
|
|
68
68
|
* share one issue DB over TCP. Reads `.beads/metadata.json` and throws an
|
|
69
|
-
* actionable error on embedded/missing — call before any `bd` work.
|
|
69
|
+
* actionable error on embedded/missing — call before any `bd` work. A project
|
|
70
|
+
* with no `.beads/` at all (one started with `boop add`) is told the `bd init`
|
|
71
|
+
* command `boop init` would have run. The messages name no skill file, because
|
|
72
|
+
* an installed project may not have the one boop's own repository does.
|
|
70
73
|
*/
|
|
71
74
|
export declare function assertDoltServerMode(projectDir: string, deps?: {
|
|
72
75
|
existsSyncFn?: (p: string) => boolean;
|
|
@@ -325,6 +328,7 @@ export declare function splitStreamLines(carry: string, chunk: string): {
|
|
|
325
328
|
lines: string[];
|
|
326
329
|
rest: string;
|
|
327
330
|
};
|
|
331
|
+
export declare function sessionEnv(model: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
328
332
|
export declare function runLoop(options: LoopOptions, deps?: {
|
|
329
333
|
/** Runs one Claude session for an issue. Tests substitute this; the planning phase is not affected. */
|
|
330
334
|
runClaudeFn?: RunClaudeFn;
|
package/dist/commands/loop.js
CHANGED
|
@@ -57,12 +57,14 @@ exports.buildPrompt = buildPrompt;
|
|
|
57
57
|
exports.shouldRotateSession = shouldRotateSession;
|
|
58
58
|
exports.sessionDidWork = sessionDidWork;
|
|
59
59
|
exports.splitStreamLines = splitStreamLines;
|
|
60
|
+
exports.sessionEnv = sessionEnv;
|
|
60
61
|
exports.runLoop = runLoop;
|
|
61
62
|
const child_process_1 = require("child_process");
|
|
62
63
|
const fs = __importStar(require("fs"));
|
|
63
64
|
const path = __importStar(require("path"));
|
|
64
65
|
const crypto_1 = require("crypto");
|
|
65
66
|
const string_decoder_1 = require("string_decoder");
|
|
67
|
+
const init_1 = require("./init");
|
|
66
68
|
/**
|
|
67
69
|
* Output cap for every `bd … --json` read the loop makes.
|
|
68
70
|
*
|
|
@@ -131,13 +133,22 @@ function readReadyQueue(execSyncFn) {
|
|
|
131
133
|
/**
|
|
132
134
|
* Hard requirement: beads must run in Dolt server mode so concurrent worktrees
|
|
133
135
|
* share one issue DB over TCP. Reads `.beads/metadata.json` and throws an
|
|
134
|
-
* actionable error on embedded/missing — call before any `bd` work.
|
|
136
|
+
* actionable error on embedded/missing — call before any `bd` work. A project
|
|
137
|
+
* with no `.beads/` at all (one started with `boop add`) is told the `bd init`
|
|
138
|
+
* command `boop init` would have run. The messages name no skill file, because
|
|
139
|
+
* an installed project may not have the one boop's own repository does.
|
|
135
140
|
*/
|
|
136
141
|
function assertDoltServerMode(projectDir, deps) {
|
|
137
142
|
const existsSyncFn = deps?.existsSyncFn || fs.existsSync;
|
|
138
143
|
const readFileFn = deps?.readFileFn || ((p, enc) => fs.readFileSync(p, enc));
|
|
139
|
-
const
|
|
140
|
-
const
|
|
144
|
+
const beadsDir = path.join(projectDir, '.beads');
|
|
145
|
+
const metaPath = path.join(beadsDir, 'metadata.json');
|
|
146
|
+
if (!existsSyncFn(beadsDir)) {
|
|
147
|
+
const initCommand = `bd ${(0, init_1.bdInitArgs)(path.basename(path.resolve(projectDir))).join(' ')}`;
|
|
148
|
+
throw new Error(`beads is not set up in ${projectDir}: there is no .beads folder. ` +
|
|
149
|
+
`The loop tracks its work in beads, running in Dolt server mode. To set it up, run this from the project folder:\n ${initCommand}`);
|
|
150
|
+
}
|
|
151
|
+
const fix = 'Switch this project\'s beads to Dolt server mode before running the loop.';
|
|
141
152
|
if (!existsSyncFn(metaPath)) {
|
|
142
153
|
throw new Error(`beads metadata not found at ${metaPath}. The loop requires Dolt server mode for concurrent worktrees. ${fix}`);
|
|
143
154
|
}
|
|
@@ -500,8 +511,10 @@ async function planWork(opts) {
|
|
|
500
511
|
const prompt = buildPlanningPrompt(workContent, flowContent, opts.prefix);
|
|
501
512
|
const sessionId = (0, crypto_1.randomUUID)();
|
|
502
513
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
|
|
503
|
-
|
|
504
|
-
const
|
|
514
|
+
// A spec-driven run usually has no prefix, so fall back to the spec's base name.
|
|
515
|
+
const runName = opts.prefix || path.basename(opts.workPath, path.extname(opts.workPath));
|
|
516
|
+
const logFile = path.join(opts.logDir, `_planning_${runName}_${timestamp}.log`);
|
|
517
|
+
const readableLogFile = path.join(opts.logDir, `_planning_${runName}_${timestamp}.readable.log`);
|
|
505
518
|
// Write readable log header
|
|
506
519
|
fs.writeFileSync(readableLogFile, `=== Planning Session ===\nSession: ${sessionId}\nStarted: ${new Date().toISOString()}\n\n`);
|
|
507
520
|
console.log(`\x1b[32m[bd-loop]\x1b[0m 📋 Starting planning phase...`);
|
|
@@ -1075,6 +1088,24 @@ function splitStreamLines(carry, chunk) {
|
|
|
1075
1088
|
const rest = pieces.pop() ?? '';
|
|
1076
1089
|
return { lines: pieces.filter(Boolean), rest };
|
|
1077
1090
|
}
|
|
1091
|
+
// Propagate the chosen model beyond this session via the environment.
|
|
1092
|
+
// `--model` (in args) sets this session; ANTHROPIC_MODEL additionally
|
|
1093
|
+
// (a) reaches a nested `boop loop` — child processes inherit the env — so it
|
|
1094
|
+
// doesn't reset to the default, and (b) is honored by any sub-agent whose
|
|
1095
|
+
// definition opts into `model: inherit`. We deliberately do NOT set
|
|
1096
|
+
// CLAUDE_CODE_SUBAGENT_MODEL: sub-agents keep their own declared model, so a
|
|
1097
|
+
// deliberately-cheap agent (e.g. Explore on Haiku) stays cheap. The invoking
|
|
1098
|
+
// agent can still override a sub-agent's model per-invocation when it needs
|
|
1099
|
+
// to. See resolveModel().
|
|
1100
|
+
//
|
|
1101
|
+
// CLAUDE_CODE_ENTRYPOINT marks the session as machine-driven, so its transcript
|
|
1102
|
+
// is not mistaken for one the operator typed into. CLAUDE_CODE_DISABLE_AUTO_MEMORY
|
|
1103
|
+
// keeps the operator's auto-memory index, written for their interactive session,
|
|
1104
|
+
// out of a coding session (honoured by Claude Code 2.1.296).
|
|
1105
|
+
function sessionEnv(model, env = process.env) {
|
|
1106
|
+
const machineDriven = { ...env, CLAUDE_CODE_ENTRYPOINT: 'boop-loop', CLAUDE_CODE_DISABLE_AUTO_MEMORY: '1' };
|
|
1107
|
+
return model ? { ...machineDriven, ANTHROPIC_MODEL: model } : machineDriven;
|
|
1108
|
+
}
|
|
1078
1109
|
function runClaude(prompt, opts, logFile, readableLogFile) {
|
|
1079
1110
|
return new Promise((resolve) => {
|
|
1080
1111
|
const args = [
|
|
@@ -1105,19 +1136,7 @@ function runClaude(prompt, opts, logFile, readableLogFile) {
|
|
|
1105
1136
|
const readableStream = readableLogFile
|
|
1106
1137
|
? fs.createWriteStream(readableLogFile, { flags: 'a' })
|
|
1107
1138
|
: null;
|
|
1108
|
-
|
|
1109
|
-
// `--model` (in args) sets this session; ANTHROPIC_MODEL additionally
|
|
1110
|
-
// (a) reaches a nested `boop loop` — child processes inherit the env — so it
|
|
1111
|
-
// doesn't reset to the default, and (b) is honored by any sub-agent whose
|
|
1112
|
-
// definition opts into `model: inherit`. We deliberately do NOT set
|
|
1113
|
-
// CLAUDE_CODE_SUBAGENT_MODEL: sub-agents keep their own declared model, so a
|
|
1114
|
-
// deliberately-cheap agent (e.g. Explore on Haiku) stays cheap. The invoking
|
|
1115
|
-
// agent can still override a sub-agent's model per-invocation when it needs
|
|
1116
|
-
// to. See resolveModel().
|
|
1117
|
-
const childEnv = opts.model
|
|
1118
|
-
? { ...process.env, ANTHROPIC_MODEL: opts.model }
|
|
1119
|
-
: process.env;
|
|
1120
|
-
const child = (0, child_process_1.spawn)(claudeBin, args, { cwd: opts.cwd, stdio: ['pipe', 'pipe', 'pipe'], env: childEnv });
|
|
1139
|
+
const child = (0, child_process_1.spawn)(claudeBin, args, { cwd: opts.cwd, stdio: ['pipe', 'pipe', 'pipe'], env: sessionEnv(opts.model) });
|
|
1121
1140
|
let limitHit = false;
|
|
1122
1141
|
// Track context-window pressure from outside the agent (spec: the loop reads
|
|
1123
1142
|
// the stream it already parses; the agent is never told). Peak occupancy over
|
package/dist/lib/execute.js
CHANGED
|
@@ -186,7 +186,17 @@ function executeTask(task, manifest) {
|
|
|
186
186
|
case 'run-command': {
|
|
187
187
|
const data = task.data;
|
|
188
188
|
console.log(`\nRunning ${data.command}...`);
|
|
189
|
-
|
|
189
|
+
// stderr is captured rather than inherited so that a failure's warning
|
|
190
|
+
// carries the command's own error text; on success it is passed through.
|
|
191
|
+
const result = (0, child_process_1.spawnSync)(data.command, { cwd: targetDir, shell: true, stdio: ['inherit', 'inherit', 'pipe'] });
|
|
192
|
+
if (result.error)
|
|
193
|
+
throw result.error;
|
|
194
|
+
const stderr = result.stderr.toString();
|
|
195
|
+
if (result.status !== 0) {
|
|
196
|
+
const exit = result.signal ? `signal ${result.signal}` : `exit code ${result.status}`;
|
|
197
|
+
throw new Error(`Command failed (${exit}): ${data.command}\n${stderr}`);
|
|
198
|
+
}
|
|
199
|
+
process.stderr.write(stderr);
|
|
190
200
|
break;
|
|
191
201
|
}
|
|
192
202
|
}
|
|
@@ -65,19 +65,56 @@ const fs = __importStar(require("fs"));
|
|
|
65
65
|
const path = __importStar(require("path"));
|
|
66
66
|
/** Jest's in-band flag and its single-letter alias. */
|
|
67
67
|
const IN_BAND_FLAGS = new Set(['--runInBand', '--runInBand=true', '-i']);
|
|
68
|
+
/** Shell operators that end one command and start the next. */
|
|
69
|
+
const COMMAND_SEPARATORS = new Set(['&&', '||', ';', '|']);
|
|
68
70
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
71
|
+
* Words that run the command after them, so the word that follows (once their
|
|
72
|
+
* own flags are skipped) is still in command position: `npx jest`, `exec jest`,
|
|
73
|
+
* `node --expose-gc node_modules/.bin/jest`.
|
|
72
74
|
*/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const LAUNCHERS = new Set(['npx', 'exec', 'node', 'cross-env']);
|
|
76
|
+
/**
|
|
77
|
+
* Keys that cannot be run as scripts by convention. Projects keep explanatory
|
|
78
|
+
* prose there, and prose about Jest naturally mentions `jest` and `--runInBand`.
|
|
79
|
+
*/
|
|
80
|
+
function isCommentKey(name) {
|
|
81
|
+
return name.startsWith('_comment') || name.startsWith('//');
|
|
82
|
+
}
|
|
83
|
+
function isJest(token) {
|
|
84
|
+
const base = path.basename(token);
|
|
85
|
+
return base === 'jest' || base === 'jest.js';
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Group a command's words by the chained command each belongs to. A separator
|
|
89
|
+
* glued to the end of a word (`tsc;`) still ends that command.
|
|
90
|
+
*/
|
|
91
|
+
function groupCommands(tokens) {
|
|
92
|
+
let current = 0;
|
|
93
|
+
return tokens.map((token) => {
|
|
94
|
+
if (COMMAND_SEPARATORS.has(token))
|
|
95
|
+
return current++;
|
|
96
|
+
const index = current;
|
|
97
|
+
if (token.endsWith(';'))
|
|
98
|
+
current++;
|
|
99
|
+
return index;
|
|
79
100
|
});
|
|
80
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Does this one command run Jest? Only the word in command position counts —
|
|
104
|
+
* after any `KEY=value` assignments, launchers and the launchers' own flags —
|
|
105
|
+
* so `jest`, `npx jest` and `node_modules/.bin/jest` match, while `echo jest`
|
|
106
|
+
* and a command that merely contains the same characters do not.
|
|
107
|
+
*/
|
|
108
|
+
function runsJest(tokens) {
|
|
109
|
+
for (const token of tokens) {
|
|
110
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token))
|
|
111
|
+
continue;
|
|
112
|
+
if (LAUNCHERS.has(token) || token.startsWith('-'))
|
|
113
|
+
continue;
|
|
114
|
+
return isJest(token);
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
81
118
|
/**
|
|
82
119
|
* Pure: given a package.json `scripts` object, return the scripts whose command
|
|
83
120
|
* forces in-band Jest execution and therefore defeats the shipped config.
|
|
@@ -87,18 +124,21 @@ function findStaleRunnerScripts(scripts) {
|
|
|
87
124
|
return [];
|
|
88
125
|
const findings = [];
|
|
89
126
|
for (const [name, command] of Object.entries(scripts)) {
|
|
90
|
-
if (typeof command !== 'string')
|
|
127
|
+
if (typeof command !== 'string' || isCommentKey(name))
|
|
91
128
|
continue;
|
|
92
129
|
const tokens = command.split(/\s+/).filter(Boolean);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
130
|
+
const group = groupCommands(tokens);
|
|
131
|
+
const jestGroups = new Set([...new Set(group)].filter((g) => runsJest(tokens
|
|
132
|
+
.filter((t, i) => group[i] === g && !COMMAND_SEPARATORS.has(t))
|
|
133
|
+
.map((t) => t.replace(/;$/, '')))));
|
|
134
|
+
const offending = (t, i) => jestGroups.has(group[i]) && IN_BAND_FLAGS.has(t);
|
|
135
|
+
const flags = tokens.filter(offending);
|
|
96
136
|
if (flags.length === 0)
|
|
97
137
|
continue;
|
|
98
138
|
findings.push({
|
|
99
139
|
name,
|
|
100
140
|
command,
|
|
101
|
-
rewritten: tokens.filter((t) => !
|
|
141
|
+
rewritten: tokens.filter((t, i) => !offending(t, i)).join(' '),
|
|
102
142
|
flags,
|
|
103
143
|
});
|
|
104
144
|
}
|
package/docs/loop.md
CHANGED
|
@@ -68,6 +68,8 @@ If `--prefix` is also provided, it is passed to the planning session as a naming
|
|
|
68
68
|
| `--no-skip-perms` | off | Don't skip Claude permission prompts |
|
|
69
69
|
| `--json` | off | Output summary as JSON |
|
|
70
70
|
|
|
71
|
+
Every Claude session the loop spawns, planning and issue sessions alike, runs with `CLAUDE_CODE_ENTRYPOINT=boop-loop`, which marks its transcript as machine-driven, and `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1`, which keeps the operator's auto-memory out of it.
|
|
72
|
+
|
|
71
73
|
## Worktree Isolation (default)
|
|
72
74
|
|
|
73
75
|
By default, `boop loop` runs **inside its own git worktree** so that N loops can run concurrently without colliding. On start (with `--run`) the loop:
|
package/docs/skills.md
CHANGED
|
@@ -21,7 +21,9 @@ Every capability ships **agent skills** — focused `SKILL.md` guides that `boop
|
|
|
21
21
|
- **boop-product-design-feature-requirements** — When you need to write a feature spec, draft a spec document, or turn a brief or vague directive into requirements — i.e. define what a feature should accomplish before designing how to build it. This fires even when you frame the task to yourself as just 'writing a spec' or 'matching the house spec format': reading an existing spec to imitate its shape is not a substitute for the method, and the more competent you feel at the raw task the more this applies. Start with the user's jobs to be done, in frequency order; take and defend a stance; then derive goals, non-goals, and success metrics.
|
|
22
22
|
- **boop-product-design-reference-integration** — When you need to integrate new knowledge into the project's `reference/` directory after completing a feature, fixing a bug that changes behavior, or adding a capability.
|
|
23
23
|
- **boop-product-design-knowledge-backfill** — When you need to integrate new knowledge from a set of specs or completed work that was not documented in reference at the time it shipped. This often happens when a feature is developed under a tight deadline and the team prioritizes shipping over documentation, or when work was done before the reference system was fully established.
|
|
24
|
-
- **boop-product-design-
|
|
24
|
+
- **boop-product-design-docs-sweep-workflow** — When `specs/` has piled up with documents nobody has looked at in months and you need to know which are finished, which have gone stale, and which are the only surviving record of real work, follow this process: measure the set rather than read it, classify each document against what the issue tracker says (the close reason, not the status), pass the gate before archiving anything, and make the mechanical fixes while surfacing the judgement calls. The document-set sibling of issue-sweep. Not for folding one shipped feature into `reference/`.
|
|
25
|
+
- **boop-product-design-reference-restructure-workflow** — When `reference/` has grown by accretion and subjects a reader would look up have no page of their own (they live as sections inside pages about something else, or in pieces across several pages), follow this process: measure the whole set rather than read it, write down the candidate subjects, then promote each one in a worktree, one commit per subject, for the operator to review as a diff. The whole-set counterpart of reference-integration, which promotes a buried subject one change at a time.
|
|
26
|
+
- **boop-product-design-thought-partnership** — When the user wants to think out loud through a design — a feature, a capability, a strategic direction — or to drive a set of features forward with you interactively, through design, code, review and adjustment, be a thought partner, not an implementer: hold the conversation at user-and-value altitude, honor uncertainty with hypotheses rather than pretend-decisions, record each ruling where the next reader will find it, and keep software-shaped concerns downstream of the user's lived problems.
|
|
25
27
|
- **boop-writing-technical-prose** — When writing chat responses, summaries, bd issue bodies, specifications, specs, or any artifact a fresh reader will pick up cold, write grammatical conversational English. Avoid the agent-shorthand patterns that compress meaning at the cost of legibility.
|
|
26
28
|
- **boop-writing-documents** — When you need to write a document someone will read cold and act on — a proposal, strategy memo, analysis, recommendation, or status write-up — and want the discipline of the Amazon six-pager: narrative prose, a declared length budget, claims that carry their evidence, and the hard questions answered inside the document.
|
|
27
29
|
- **boop-writing-semantic-line-breaks** — When you need to write or edit prose in a markdown file under version control — a spec, a reference page, a README, a skill — break lines at semantic boundaries rather than at a fixed column, so that edits produce readable diffs.
|
|
@@ -55,13 +57,13 @@ Every capability ships **agent skills** — focused `SKILL.md` guides that `boop
|
|
|
55
57
|
### `ux` — UX design judgement and the discovery/delivery workflows that operate it
|
|
56
58
|
|
|
57
59
|
- **boop-ux-purpose-judgement** — When you are evaluating a rendered screen or page and need to judge whether each thing on it belongs there, in the right shape, for the right job. Asks the structural question that the visual lens does not — is this affordance the right idea, not just rendered well. Pairs with ux-visual-judgement (the question of how things look and scan).
|
|
58
|
-
- **boop-ux-visual-judgement** — When you are evaluating a rendered screen or page and need to form a principled judgement about how it looks and scans. Visual lens only — eye-path,
|
|
60
|
+
- **boop-ux-visual-judgement** — When you are evaluating a rendered screen or page and need to form a principled judgement about how it looks and scans. Visual lens only — eye-path, loudness, anatomy, scan-line, whitespace, density, voice. Pairs with ux-purpose-judgement (the question of whether each element belongs at all) and, when the project has the web capability, with boop-web-css (the grammar of how things are rendered).
|
|
59
61
|
- **boop-ux-eval-and-refine-workflow** — When you are tasked with iteratively evaluating and improving an existing rendered screen or page (discovery, not delivery), follow this workflow. Inner loop: capture → eval → recommend → implement → re-capture. Outer: knowledge integration.
|
|
60
62
|
- **boop-ux-app-ui-dev-workflow** — When you are tasked with improving an existing app-side screen or page (app UX, not marketing) end-to-end and the input is a target screen or page rather than a written spec, follow this beads-issue-per-step workflow. Sibling to feature-dev-workflow but stripped of backend phases. The judgement lenses are the planning input.
|
|
61
63
|
|
|
62
64
|
### `workflows` — Reusable end-to-end development and audit workflows for code
|
|
63
65
|
|
|
64
|
-
- **boop-feature-dev-workflow** — When you are tasked with building a feature end-to-end, follow this
|
|
66
|
+
- **boop-feature-dev-workflow** — When you are tasked with building a feature end-to-end, follow this beads-issue-per-step workflow: epic root → understand → plan → decompose → implement → review → punch-list → closeout → reconcile-and-finalize.
|
|
65
67
|
- **boop-light-dev-workflow** — When you are extending an existing system (front-end, back-end, or full-stack) toward a goal — wiring something new from data and patterns that already exist, not building greenfield — follow this grounded flow: goal → investigate the real system → design → critique → decompose into one scoped epic + child issues → run them → close out. Loop-drivable via boop loop --work --flow; lighter than feature-dev-workflow.
|
|
66
68
|
- **boop-refactor-dev-workflow** — When you need to change a contract, cardinality, or shape that existing code already depends on — usually to make a feature buildable — follow this workflow instead of the feature workflow.
|
|
67
69
|
- **boop-test-audit-workflow** — Use when a set of tests is not testing the purpose of the code: mocks that verify data the test itself fed in, tests that exercise the platform rather than our code, or a stubbed seam where the integration is the actual risk. Also use when tests are so coupled to the shape of the implementation that they check a label, an exact string, or a call sequence rather than a behaviour. This workflow removes tests that cannot go red for a real reason, rewrites the ones that check a real goal at the wrong level, merges duplicates, and does not lose coverage. It explicitly does not target good tests merely because a coming change will make them need updating. The sibling of light-dev-workflow for reworking tests rather than building features.
|
|
@@ -102,9 +104,11 @@ Every capability ships **agent skills** — focused `SKILL.md` guides that `boop
|
|
|
102
104
|
### `web` — Web UI with Handlebars + Tailwind
|
|
103
105
|
|
|
104
106
|
- **boop-web-web-ui-design** — When you need to design and implement user interfaces — including when you frame the task as 'build a page', 'add a component', 'style this surface', or 'wire up the UI' — following established patterns and philosophy.
|
|
105
|
-
- **boop-web-
|
|
107
|
+
- **boop-web-css** — When you need to write or change CSS — adding or renaming a class, writing a selector or a stylesheet rule, or editing a class attribute in a template — name classes for what their elements are for, nest each block's children and variants inside it, and never use BEM's __element or --modifier separators.
|
|
108
|
+
- **boop-web-testing** — When you need to write or run tests for the Express server — services, controllers, middleware, and HTTP routes — or to confirm in a browser that a change to something a person looks at does what its issue said, since a green suite proves behaviour but not what renders.
|
|
106
109
|
- **boop-web-website-dev-workflow** — When you are tasked with implementing website changes end-to-end (new pages, page rewrites, IA changes, content/CTA updates), follow this guidance.
|
|
107
110
|
- **boop-web-design-handoff-workflow** — When the operator hands off a design package (a README plus a drop-in view template and CSS) for a redesigned surface, follow this process: read the README, VERIFY its claims against the real code, surface any buried decision to the operator, file an issue, run it as a sub-agent (branch for UAT or merge-on-clean-visual-check), then close out with knowledge integration and archival. The README is confident; confidence is not correctness — verify before you run.
|
|
111
|
+
- **boop-web-design-handoff-bundle** — When the operator points at a design canvas (an artifact of artboards) and asks for it to be bundled, turned into a handoff, or made ready for the coding agent, turn it into a design handoff bundle under specs/design-handoffs/: a README, drop-in templates and CSS in the project's conventions, the decisions, and an eval checklist. The producing side of boop-web-design-handoff-workflow; the system wins over the prototype, and every departure is written down.
|
|
108
112
|
|
|
109
113
|
---
|
|
110
114
|
|
package/package.json
CHANGED