@boopkit/cli 0.7.1 → 0.8.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/dist/capability-versions.json +2 -2
- package/dist/cli.js +0 -0
- package/dist/commands/loop.d.ts +19 -1
- package/dist/commands/loop.js +71 -3
- package/docs/loop.md +6 -0
- package/package.json +1 -1
|
@@ -8,9 +8,9 @@
|
|
|
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.
|
|
11
|
+
"@boopkit/cap-oauth-server": "0.3.2",
|
|
12
12
|
"@boopkit/cap-product-design": "0.6.11",
|
|
13
|
-
"@boopkit/cap-project-mgmt": "0.4.
|
|
13
|
+
"@boopkit/cap-project-mgmt": "0.4.13",
|
|
14
14
|
"@boopkit/cap-services": "0.4.3",
|
|
15
15
|
"@boopkit/cap-skills": "0.5.6",
|
|
16
16
|
"@boopkit/cap-testing": "0.5.8",
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/commands/loop.d.ts
CHANGED
|
@@ -244,6 +244,17 @@ export declare function getMatchingReadyIssues(prefix: string, includeFeatures:
|
|
|
244
244
|
maxBuffer?: number;
|
|
245
245
|
}) => string;
|
|
246
246
|
}): ReadyIssue[];
|
|
247
|
+
/** A skill folder in a project's skills/ directory. */
|
|
248
|
+
export interface InstalledSkill {
|
|
249
|
+
name: string;
|
|
250
|
+
hasProjectFile: boolean;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The skills installed in a project folder: each folder under skills/ that holds
|
|
254
|
+
* a SKILL.md. statSync follows symlinks, which matters because boop's own
|
|
255
|
+
* skills/ entries are symlinks into each capability package's skills/ folder.
|
|
256
|
+
*/
|
|
257
|
+
export declare function readInstalledSkills(projectDir: string): InstalledSkill[];
|
|
247
258
|
/**
|
|
248
259
|
* The instructions every loop session starts from. A session acts on this text
|
|
249
260
|
* literally and reads it whether or not it opens a skill, so step 5 carries the
|
|
@@ -251,8 +262,15 @@ export declare function getMatchingReadyIssues(prefix: string, includeFeatures:
|
|
|
251
262
|
* when the issue asks) and names no command, because the text is the same in
|
|
252
263
|
* every project and the commands are not. For the same reason it names no
|
|
253
264
|
* project.
|
|
265
|
+
*
|
|
266
|
+
* Step 3 lists, as paths, the installed skills the issue names: each SKILL.md,
|
|
267
|
+
* plus the PROJECT.md beside it where there is one. Sessions open a skill they
|
|
268
|
+
* only see named about one time in seven, but read a path the prompt spells out.
|
|
269
|
+
* An issue that names no installed skill is pointed at boop-skills-discovery
|
|
270
|
+
* instead, and without `installed` (or without discovery installed) step 3 keeps
|
|
271
|
+
* the general "read relevant skills" wording.
|
|
254
272
|
*/
|
|
255
|
-
export declare function buildPrompt(issue: IssueInfo): string;
|
|
273
|
+
export declare function buildPrompt(issue: IssueInfo, installed?: InstalledSkill[]): string;
|
|
256
274
|
/**
|
|
257
275
|
* Default per-issue budget cap in USD. The planning phase and each task each get
|
|
258
276
|
* this cap. Single source of truth — imported by cli.ts for its --budget help
|
package/dist/commands/loop.js
CHANGED
|
@@ -52,6 +52,7 @@ exports.isIssueInScope = isIssueInScope;
|
|
|
52
52
|
exports.isClaimableReadyIssue = isClaimableReadyIssue;
|
|
53
53
|
exports.shouldCloseFeatureRoot = shouldCloseFeatureRoot;
|
|
54
54
|
exports.getMatchingReadyIssues = getMatchingReadyIssues;
|
|
55
|
+
exports.readInstalledSkills = readInstalledSkills;
|
|
55
56
|
exports.buildPrompt = buildPrompt;
|
|
56
57
|
exports.shouldRotateSession = shouldRotateSession;
|
|
57
58
|
exports.sessionDidWork = sessionDidWork;
|
|
@@ -883,6 +884,65 @@ function isIssueClosed(id) {
|
|
|
883
884
|
return false;
|
|
884
885
|
}
|
|
885
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* The skills installed in a project folder: each folder under skills/ that holds
|
|
889
|
+
* a SKILL.md. statSync follows symlinks, which matters because boop's own
|
|
890
|
+
* skills/ entries are symlinks into each capability package's skills/ folder.
|
|
891
|
+
*/
|
|
892
|
+
function readInstalledSkills(projectDir) {
|
|
893
|
+
const skillsDir = path.join(projectDir, 'skills');
|
|
894
|
+
let entries;
|
|
895
|
+
try {
|
|
896
|
+
entries = fs.readdirSync(skillsDir);
|
|
897
|
+
}
|
|
898
|
+
catch {
|
|
899
|
+
return [];
|
|
900
|
+
}
|
|
901
|
+
const skills = [];
|
|
902
|
+
for (const name of entries) {
|
|
903
|
+
const dir = path.join(skillsDir, name);
|
|
904
|
+
try {
|
|
905
|
+
if (!fs.statSync(dir).isDirectory() || !fs.existsSync(path.join(dir, 'SKILL.md')))
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
skills.push({ name, hasProjectFile: fs.existsSync(path.join(dir, 'PROJECT.md')) });
|
|
912
|
+
}
|
|
913
|
+
return skills;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* The installed skills an issue description names, by bare name or by path, in
|
|
917
|
+
* the order each is first mentioned. A name only matches whole, so
|
|
918
|
+
* boop-testing-testing does not match inside boop-testing-testing-express-web.
|
|
919
|
+
*/
|
|
920
|
+
function namedSkills(description, installed) {
|
|
921
|
+
const found = [];
|
|
922
|
+
for (const skill of installed) {
|
|
923
|
+
const escaped = skill.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
924
|
+
const match = new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`).exec(description);
|
|
925
|
+
if (match)
|
|
926
|
+
found.push({ skill, at: match.index });
|
|
927
|
+
}
|
|
928
|
+
return found.sort((a, b) => a.at - b.at).map((f) => f.skill);
|
|
929
|
+
}
|
|
930
|
+
function skillStep(issue, installed) {
|
|
931
|
+
const general = '3. Read relevant skills from skills/ (list folders, read each SKILL.md) before making changes';
|
|
932
|
+
if (!installed)
|
|
933
|
+
return general;
|
|
934
|
+
const named = namedSkills(issue.description, installed);
|
|
935
|
+
if (named.length > 0) {
|
|
936
|
+
const lines = named.map((s) => s.hasProjectFile
|
|
937
|
+
? ` - skills/${s.name}/SKILL.md and skills/${s.name}/PROJECT.md`
|
|
938
|
+
: ` - skills/${s.name}/SKILL.md`);
|
|
939
|
+
return `3. Before making changes, read each of these in full, unless you have already read it in this session:\n${lines.join('\n')}`;
|
|
940
|
+
}
|
|
941
|
+
if (installed.some((s) => s.name === 'boop-skills-discovery')) {
|
|
942
|
+
return '3. Follow skills/boop-skills-discovery/SKILL.md to choose the skills relevant to this work, and read those skills before making changes';
|
|
943
|
+
}
|
|
944
|
+
return general;
|
|
945
|
+
}
|
|
886
946
|
/**
|
|
887
947
|
* The instructions every loop session starts from. A session acts on this text
|
|
888
948
|
* literally and reads it whether or not it opens a skill, so step 5 carries the
|
|
@@ -890,8 +950,15 @@ function isIssueClosed(id) {
|
|
|
890
950
|
* when the issue asks) and names no command, because the text is the same in
|
|
891
951
|
* every project and the commands are not. For the same reason it names no
|
|
892
952
|
* project.
|
|
953
|
+
*
|
|
954
|
+
* Step 3 lists, as paths, the installed skills the issue names: each SKILL.md,
|
|
955
|
+
* plus the PROJECT.md beside it where there is one. Sessions open a skill they
|
|
956
|
+
* only see named about one time in seven, but read a path the prompt spells out.
|
|
957
|
+
* An issue that names no installed skill is pointed at boop-skills-discovery
|
|
958
|
+
* instead, and without `installed` (or without discovery installed) step 3 keeps
|
|
959
|
+
* the general "read relevant skills" wording.
|
|
893
960
|
*/
|
|
894
|
-
function buildPrompt(issue) {
|
|
961
|
+
function buildPrompt(issue, installed) {
|
|
895
962
|
return `You are working on issue ${issue.id} in this project.
|
|
896
963
|
|
|
897
964
|
## Issue: ${issue.title}
|
|
@@ -903,7 +970,7 @@ ${issue.description}
|
|
|
903
970
|
## Instructions
|
|
904
971
|
1. Read AGENTS.md for project orientation
|
|
905
972
|
2. Claim the issue: bd update ${issue.id} --claim
|
|
906
|
-
|
|
973
|
+
${skillStep(issue, installed)}
|
|
907
974
|
4. Implement the changes following TDD-Lite: observe, implement, verify
|
|
908
975
|
5. Run the tests this issue needs, and no more:
|
|
909
976
|
- If the issue description states which tests to run, run exactly those and skip the rest of this step. The issue's scope replaces the default below; do not run both.
|
|
@@ -1201,7 +1268,8 @@ async function processIssue(issue, opts) {
|
|
|
1201
1268
|
}
|
|
1202
1269
|
}
|
|
1203
1270
|
// Initial run
|
|
1204
|
-
|
|
1271
|
+
// opts.cwd is the worktree the session runs in, so its skills/ is the one the session sees.
|
|
1272
|
+
const prompt = buildPrompt(issue, readInstalledSkills(opts.cwd ?? process.cwd()));
|
|
1205
1273
|
console.log(`\x1b[32m[bd-loop]\x1b[0m 🚀 Starting Claude session (budget: $${opts.budget}, session: ${sessionId.substring(0, 8)}...)`);
|
|
1206
1274
|
const initialRun = await runClaude(prompt, {
|
|
1207
1275
|
budget: opts.budget,
|
package/docs/loop.md
CHANGED
|
@@ -29,6 +29,12 @@ Without `--run`, the command shows what it would do without executing anything.
|
|
|
29
29
|
|
|
30
30
|
Each Claude session gets a full prompt with issue details and instructions to work autonomously through the complete workflow: claim, implement, test, commit, close, and push.
|
|
31
31
|
|
|
32
|
+
The prompt tells a session which skills to read before it makes changes.
|
|
33
|
+
The loop matches the names of the skills installed in the session's own `skills/` folder against the issue description, accepting either a bare name such as `boop-testing-testing` or a path such as `skills/boop-testing-testing/SKILL.md`.
|
|
34
|
+
It lists each skill the issue names, once and in the order first mentioned, as the path to its `SKILL.md`, adding the `PROJECT.md` beside it where there is one, and asks the session to read each in full unless it has already read it in this session.
|
|
35
|
+
When the issue names no installed skill, the prompt instead tells the session to follow `skills/boop-skills-discovery/SKILL.md` to choose the relevant skills.
|
|
36
|
+
Spelling out the paths matters: a session starts with every skill's name and description in view but none of their content, and it opens a skill it only sees named about one time in seven.
|
|
37
|
+
|
|
32
38
|
The prompt does not tell a session to run the whole test suite. If the issue says which tests to run, the session runs those. Otherwise it runs the focused tests for the files it changed plus the project's static check, using the commands the `boop-testing-testing` skill and the `PROJECT.md` beside it give. The full suite runs only when an issue asks for it, which is normally the task that completes the branch, and again when `boop loop --merge` integrates the branch.
|
|
33
39
|
|
|
34
40
|
### Planning mode (`--work` + `--flow`)
|
package/package.json
CHANGED