@boopkit/cli 0.8.0 → 0.8.1
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 +8 -6
- package/dist/capability-catalog.json +10 -3
- package/dist/capability-versions.json +11 -10
- package/dist/cli.js +0 -0
- package/dist/commands/add.js +35 -7
- package/dist/commands/adopt.js +1 -1
- package/dist/commands/loop-merge.d.ts +11 -0
- package/dist/commands/loop-merge.js +135 -14
- package/dist/commands/loop.js +1 -1
- package/dist/commands/migrate-skill-edits.js +2 -2
- package/dist/commands/status.d.ts +10 -0
- package/dist/commands/status.js +27 -2
- package/dist/commands/update.js +157 -21
- package/dist/lib/capability.js +4 -1
- package/dist/lib/execute.js +19 -2
- package/dist/lib/install-layout.d.ts +5 -0
- package/dist/lib/install-layout.js +8 -0
- package/dist/lib/manifest.d.ts +1 -1
- package/dist/lib/moves.d.ts +88 -0
- package/dist/lib/moves.js +381 -0
- package/dist/lib/original-text.d.ts +2 -0
- package/dist/lib/original-text.js +15 -7
- package/dist/lib/preflight.d.ts +14 -1
- package/dist/lib/preflight.js +53 -17
- package/dist/lib/project.d.ts +17 -0
- package/dist/lib/project.js +26 -0
- package/dist/lib/scaffold.d.ts +12 -1
- package/dist/lib/scaffold.js +43 -4
- package/dist/lib/update.d.ts +36 -2
- package/dist/lib/update.js +82 -4
- package/docs/init.md +22 -0
- package/docs/loop.md +11 -3
- package/docs/skills.md +17 -13
- package/package.json +1 -1
- package/profiles/base/profile.md +13 -10
- package/profiles/development/profile.md +10 -8
- package/profiles/web-app/profile.md +6 -4
- package/profiles/web-app/reference/message-queue-patterns.ref.md +8 -13
- package/src/templates/kernel/tests/app.test.ts.hbs +17 -0
package/dist/lib/scaffold.d.ts
CHANGED
|
@@ -31,6 +31,12 @@ export declare function renderTemplate(templateContent: string, variables: Templ
|
|
|
31
31
|
*/
|
|
32
32
|
export declare function getTemplatesDir(profileName: string): string;
|
|
33
33
|
export declare function profileHasTemplates(profileName: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The minimal package.json the `base` profile ships, rendered for a project.
|
|
36
|
+
* boop records every capability as an npm package there, so a project without
|
|
37
|
+
* one (a native iOS app, say) gets this one before its first capability.
|
|
38
|
+
*/
|
|
39
|
+
export declare function renderBasePackageJson(projectName: string): string;
|
|
34
40
|
/**
|
|
35
41
|
* Copy and render every file in a templates directory (the kernel templates
|
|
36
42
|
* by default) into a target directory. Creates directories as needed. Does
|
|
@@ -51,7 +57,7 @@ export declare function hashContent(content: Buffer): string;
|
|
|
51
57
|
* Tries package resolution first (scaffold/ and skills/ dirs), falls back to source tree.
|
|
52
58
|
* Returns list of copied file paths relative to targetDir, and file hashes.
|
|
53
59
|
*/
|
|
54
|
-
export declare function copyCapabilityFiles(targetDir: string, capabilityName: string, searchPaths?: string[]): string[];
|
|
60
|
+
export declare function copyCapabilityFiles(targetDir: string, capabilityName: string, searchPaths?: string[], excludePaths?: string[]): string[];
|
|
55
61
|
/**
|
|
56
62
|
* Symlink each skill folder under `<targetDir>/skills/` into
|
|
57
63
|
* `<targetDir>/.claude/skills/` so Claude Code's built-in Skill tool
|
|
@@ -66,6 +72,11 @@ export declare function copyCapabilityFiles(targetDir: string, capabilityName: s
|
|
|
66
72
|
* `boop init` link all of it; the boop repository mirrors the whole library
|
|
67
73
|
* there and needs to offer only the part its manifest selects.
|
|
68
74
|
*
|
|
75
|
+
* A link left pointing into `skills/` at a folder that no longer stands there
|
|
76
|
+
* (the project deleted the skill, or a capability stopped shipping it) is
|
|
77
|
+
* removed, so the links follow the folders. Only symlinks into `skills/` are
|
|
78
|
+
* pruned; a link the project made to anywhere else is never touched.
|
|
79
|
+
*
|
|
69
80
|
* Idempotent. Returns the relative paths of symlinks created or already present.
|
|
70
81
|
*/
|
|
71
82
|
export declare function linkSkillsToClaude(targetDir: string, only?: Iterable<string>): string[];
|
package/dist/lib/scaffold.js
CHANGED
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.renderTemplate = renderTemplate;
|
|
40
40
|
exports.getTemplatesDir = getTemplatesDir;
|
|
41
41
|
exports.profileHasTemplates = profileHasTemplates;
|
|
42
|
+
exports.renderBasePackageJson = renderBasePackageJson;
|
|
42
43
|
exports.scaffoldKernel = scaffoldKernel;
|
|
43
44
|
exports.hashFile = hashFile;
|
|
44
45
|
exports.hashContent = hashContent;
|
|
@@ -97,6 +98,15 @@ function profileHasTemplates(profileName) {
|
|
|
97
98
|
function getProfileTemplatesDir(profileName) {
|
|
98
99
|
return path.join((0, config_1.getBoopRoot)(), 'profiles', profileName, 'templates');
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* The minimal package.json the `base` profile ships, rendered for a project.
|
|
103
|
+
* boop records every capability as an npm package there, so a project without
|
|
104
|
+
* one (a native iOS app, say) gets this one before its first capability.
|
|
105
|
+
*/
|
|
106
|
+
function renderBasePackageJson(projectName) {
|
|
107
|
+
const template = fs.readFileSync(path.join(getProfileTemplatesDir('base'), 'package.json.hbs'), 'utf-8');
|
|
108
|
+
return renderTemplate(template, { projectName });
|
|
109
|
+
}
|
|
100
110
|
/**
|
|
101
111
|
* Copy and render every file in a templates directory (the kernel templates
|
|
102
112
|
* by default) into a target directory. Creates directories as needed. Does
|
|
@@ -156,7 +166,7 @@ function hashContent(content) {
|
|
|
156
166
|
* Tries package resolution first (scaffold/ and skills/ dirs), falls back to source tree.
|
|
157
167
|
* Returns list of copied file paths relative to targetDir, and file hashes.
|
|
158
168
|
*/
|
|
159
|
-
function copyCapabilityFiles(targetDir, capabilityName, searchPaths) {
|
|
169
|
+
function copyCapabilityFiles(targetDir, capabilityName, searchPaths, excludePaths = []) {
|
|
160
170
|
// Default to the project itself, so this resolves the capability to the same
|
|
161
171
|
// package `planAdd` did. Without it a capability that lives only under the
|
|
162
172
|
// project resolved here to nothing, and `boop add` reported every file copied
|
|
@@ -168,7 +178,7 @@ function copyCapabilityFiles(targetDir, capabilityName, searchPaths) {
|
|
|
168
178
|
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
169
179
|
const boopkit = pkgJson.boopkit;
|
|
170
180
|
if (boopkit?.capability) {
|
|
171
|
-
return copyFromPackage(targetDir, capabilityName, pkgDir);
|
|
181
|
+
return copyFromPackage(targetDir, capabilityName, pkgDir, excludePaths);
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
// Fallback to source tree
|
|
@@ -200,8 +210,9 @@ function copyPlacements(targetDir, placements) {
|
|
|
200
210
|
/**
|
|
201
211
|
* Copy capability files from a package directory.
|
|
202
212
|
*/
|
|
203
|
-
function copyFromPackage(targetDir, capabilityName, pkgDir) {
|
|
204
|
-
const
|
|
213
|
+
function copyFromPackage(targetDir, capabilityName, pkgDir, excludePaths) {
|
|
214
|
+
const placements = (0, install_layout_1.capabilityPlacementsFromDir)(pkgDir).filter(p => !(0, install_layout_1.isUnderAny)(p.dest, excludePaths));
|
|
215
|
+
const { copied, fileHashes } = copyPlacements(targetDir, placements);
|
|
205
216
|
// Store file hashes and version in .boop/config.json
|
|
206
217
|
if (Object.keys(fileHashes).length > 0) {
|
|
207
218
|
const pkgJson = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8'));
|
|
@@ -223,10 +234,16 @@ function copyFromPackage(targetDir, capabilityName, pkgDir) {
|
|
|
223
234
|
* `boop init` link all of it; the boop repository mirrors the whole library
|
|
224
235
|
* there and needs to offer only the part its manifest selects.
|
|
225
236
|
*
|
|
237
|
+
* A link left pointing into `skills/` at a folder that no longer stands there
|
|
238
|
+
* (the project deleted the skill, or a capability stopped shipping it) is
|
|
239
|
+
* removed, so the links follow the folders. Only symlinks into `skills/` are
|
|
240
|
+
* pruned; a link the project made to anywhere else is never touched.
|
|
241
|
+
*
|
|
226
242
|
* Idempotent. Returns the relative paths of symlinks created or already present.
|
|
227
243
|
*/
|
|
228
244
|
function linkSkillsToClaude(targetDir, only) {
|
|
229
245
|
const skillsDir = path.join(targetDir, 'skills');
|
|
246
|
+
pruneDanglingSkillLinks(targetDir);
|
|
230
247
|
if (!fs.existsSync(skillsDir))
|
|
231
248
|
return [];
|
|
232
249
|
const selected = only === undefined ? null : new Set(only);
|
|
@@ -255,6 +272,28 @@ function linkSkillsToClaude(targetDir, only) {
|
|
|
255
272
|
}
|
|
256
273
|
return linked;
|
|
257
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Remove each symlink in `<targetDir>/.claude/skills/` that points into
|
|
277
|
+
* `<targetDir>/skills/` at an entry that no longer exists.
|
|
278
|
+
*/
|
|
279
|
+
function pruneDanglingSkillLinks(targetDir) {
|
|
280
|
+
const skillsDir = path.join(targetDir, 'skills');
|
|
281
|
+
const claudeSkillsDir = path.join(targetDir, '.claude', 'skills');
|
|
282
|
+
if (!fs.existsSync(claudeSkillsDir))
|
|
283
|
+
return;
|
|
284
|
+
for (const entry of fs.readdirSync(claudeSkillsDir, { withFileTypes: true })) {
|
|
285
|
+
if (!entry.isSymbolicLink())
|
|
286
|
+
continue;
|
|
287
|
+
const linkPath = path.join(claudeSkillsDir, entry.name);
|
|
288
|
+
const resolved = path.resolve(claudeSkillsDir, fs.readlinkSync(linkPath));
|
|
289
|
+
if (!resolved.startsWith(skillsDir + path.sep))
|
|
290
|
+
continue;
|
|
291
|
+
// lstat, so a skills/ entry that is itself a symlink counts as present.
|
|
292
|
+
if (fs.lstatSync(resolved, { throwIfNoEntry: false }))
|
|
293
|
+
continue;
|
|
294
|
+
fs.unlinkSync(linkPath);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
258
297
|
/**
|
|
259
298
|
* The names of the skill folders under `<targetDir>/skills/` that boop installed.
|
|
260
299
|
*
|
package/dist/lib/update.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface FileUpdateAction {
|
|
|
17
17
|
/** Relative path from project root */
|
|
18
18
|
relativePath: string;
|
|
19
19
|
/** What will happen to this file */
|
|
20
|
-
action: 'overwrite' | 'skip' | 'add' | 'deprecate' | 'migrate' | 'record-hash';
|
|
20
|
+
action: 'overwrite' | 'skip' | 'add' | 'deprecate' | 'migrate' | 'record-hash' | 'move' | 'remove';
|
|
21
21
|
/** Why this action was chosen */
|
|
22
22
|
reason: string;
|
|
23
23
|
/** Stored hash from config */
|
|
@@ -38,6 +38,12 @@ export interface FileUpdateAction {
|
|
|
38
38
|
instruction?: string;
|
|
39
39
|
/** boop's current copy of the file, when the instruction points at it */
|
|
40
40
|
upstreamPath?: string;
|
|
41
|
+
/** For a path another capability ships now: where it ships */
|
|
42
|
+
movedTo?: string;
|
|
43
|
+
/** For a path another capability ships now: that capability */
|
|
44
|
+
toCapability?: string;
|
|
45
|
+
/** For the move of a skill folder: whether its PROJECT.md held more than the template */
|
|
46
|
+
projectFileCarried?: boolean;
|
|
41
47
|
}
|
|
42
48
|
export interface FileInstruction {
|
|
43
49
|
relativePath: string;
|
|
@@ -82,6 +88,16 @@ export interface UpdateResult {
|
|
|
82
88
|
unchangedCount: number;
|
|
83
89
|
/** What to do about the files this update skipped or replaced, where the report says anything */
|
|
84
90
|
instructions: FileInstruction[];
|
|
91
|
+
/** Paths moved to another capability, by the move pass */
|
|
92
|
+
moved: {
|
|
93
|
+
from: string;
|
|
94
|
+
to: string;
|
|
95
|
+
toCapability: string;
|
|
96
|
+
}[];
|
|
97
|
+
/** Skill folders deleted because another capability ships them now and this project does not install it */
|
|
98
|
+
removed: string[];
|
|
99
|
+
/** Moved paths left where they are until the instruction for them is followed */
|
|
100
|
+
movesWaiting: string[];
|
|
85
101
|
}
|
|
86
102
|
/** The skill that splits a drifted SKILL.md — keep in step with the folder name boop-rm2l.8 gives it */
|
|
87
103
|
export declare const TRIAGE_SKILL = "boop-skills-edit-triage";
|
|
@@ -91,11 +107,21 @@ export declare const SKILL_EDITS_INSTRUCTION = "Move each skill's edits into the
|
|
|
91
107
|
* Compare a file's current hash against a stored hash.
|
|
92
108
|
*/
|
|
93
109
|
export declare function compareFileHash(filePath: string, storedHash: string): FileState;
|
|
110
|
+
export interface PlanOptions {
|
|
111
|
+
/**
|
|
112
|
+
* The move pass's actions for paths leaving this capability. Each replaces
|
|
113
|
+
* whatever this planner would plan for the paths it covers; a skip whose
|
|
114
|
+
* `relativePath` is a folder covers every stored path inside it.
|
|
115
|
+
*/
|
|
116
|
+
moves?: FileUpdateAction[];
|
|
117
|
+
/** Paths arriving in this capability by a move not yet carried out, which the move lists instead */
|
|
118
|
+
arriving?: string[];
|
|
119
|
+
}
|
|
94
120
|
/**
|
|
95
121
|
* Plan an update for a single capability. Returns null if the capability
|
|
96
122
|
* is not installed or has no package.
|
|
97
123
|
*/
|
|
98
|
-
export declare function planCapabilityUpdate(targetDir: string, capabilityName: string, projectManifest: BoopManifest): UpdatePlan | null;
|
|
124
|
+
export declare function planCapabilityUpdate(targetDir: string, capabilityName: string, projectManifest: BoopManifest, options?: PlanOptions): UpdatePlan | null;
|
|
99
125
|
/**
|
|
100
126
|
* Execute an update plan, writing files and updating config.
|
|
101
127
|
* If force is true, overwrites even locally modified files (with backup).
|
|
@@ -105,6 +131,14 @@ export declare function executeUpdatePlan(plan: UpdatePlan, options?: {
|
|
|
105
131
|
restore?: boolean;
|
|
106
132
|
prune?: boolean;
|
|
107
133
|
}): UpdateResult;
|
|
134
|
+
/**
|
|
135
|
+
* Rewrite each capability's stored env record (capabilityConfig[cap].env) from
|
|
136
|
+
* the env its installed package declares. A variable the package still declares
|
|
137
|
+
* keeps its `set` flag; one it no longer declares is dropped, and a capability
|
|
138
|
+
* that declares none loses its record. `boop status` prefers this record over
|
|
139
|
+
* the package, so a stale one would list a moved variable under both capabilities.
|
|
140
|
+
*/
|
|
141
|
+
export declare function refreshCapabilityEnv(targetDir: string, capabilities: string[]): void;
|
|
108
142
|
/**
|
|
109
143
|
* Count the skills a capability's project copy has edited in SKILL.md, and how
|
|
110
144
|
* many of those boop has changed since — from the same comparison `boop update`
|
package/dist/lib/update.js
CHANGED
|
@@ -43,6 +43,7 @@ exports.SKILL_EDITS_INSTRUCTION = exports.TRIAGE_SKILL = void 0;
|
|
|
43
43
|
exports.compareFileHash = compareFileHash;
|
|
44
44
|
exports.planCapabilityUpdate = planCapabilityUpdate;
|
|
45
45
|
exports.executeUpdatePlan = executeUpdatePlan;
|
|
46
|
+
exports.refreshCapabilityEnv = refreshCapabilityEnv;
|
|
46
47
|
exports.countSkillEdits = countSkillEdits;
|
|
47
48
|
exports.formatUpdateSummary = formatUpdateSummary;
|
|
48
49
|
const fs = __importStar(require("fs"));
|
|
@@ -71,7 +72,7 @@ function compareFileHash(filePath, storedHash) {
|
|
|
71
72
|
* Plan an update for a single capability. Returns null if the capability
|
|
72
73
|
* is not installed or has no package.
|
|
73
74
|
*/
|
|
74
|
-
function planCapabilityUpdate(targetDir, capabilityName, projectManifest) {
|
|
75
|
+
function planCapabilityUpdate(targetDir, capabilityName, projectManifest, options = {}) {
|
|
75
76
|
// Capability must be installed
|
|
76
77
|
if (!projectManifest.capabilities.includes(capabilityName)) {
|
|
77
78
|
return null;
|
|
@@ -102,8 +103,14 @@ function planCapabilityUpdate(targetDir, capabilityName, projectManifest) {
|
|
|
102
103
|
for (const placement of (0, install_layout_1.capabilityPlacementsFromDir)(pkgDir)) {
|
|
103
104
|
packageFiles.set(placement.dest, placement.source);
|
|
104
105
|
}
|
|
106
|
+
const arriving = options.arriving ?? [];
|
|
107
|
+
const moves = options.moves ?? [];
|
|
108
|
+
// A held move is one action for a whole folder, named without its trailing slash
|
|
109
|
+
const coveredByMove = (relPath) => moves.some(m => (0, install_layout_1.isUnderAny)(relPath, [m.relativePath, m.relativePath + '/']));
|
|
105
110
|
// For each file in the package, determine action
|
|
106
111
|
for (const [relPath, srcPath] of packageFiles) {
|
|
112
|
+
if ((0, install_layout_1.isUnderAny)(relPath, arriving))
|
|
113
|
+
continue;
|
|
107
114
|
const destPath = path.join(targetDir, relPath);
|
|
108
115
|
const storedHash = storedHashes[relPath];
|
|
109
116
|
const newHash = (0, scaffold_1.hashFile)(srcPath);
|
|
@@ -210,7 +217,7 @@ function planCapabilityUpdate(targetDir, capabilityName, projectManifest) {
|
|
|
210
217
|
}
|
|
211
218
|
// Check for files removed from package (in stored hashes but not in package)
|
|
212
219
|
for (const relPath of Object.keys(storedHashes)) {
|
|
213
|
-
if (!packageFiles.has(relPath)) {
|
|
220
|
+
if (!packageFiles.has(relPath) && !coveredByMove(relPath)) {
|
|
214
221
|
actions.push({
|
|
215
222
|
relativePath: relPath,
|
|
216
223
|
action: 'deprecate',
|
|
@@ -269,6 +276,7 @@ function planCapabilityUpdate(targetDir, capabilityName, projectManifest) {
|
|
|
269
276
|
// oldState === 'missing': old file already gone, add new files normally
|
|
270
277
|
}
|
|
271
278
|
}
|
|
279
|
+
actions.push(...moves);
|
|
272
280
|
// A capability that ships a new command ships it as a file and a script; an
|
|
273
281
|
// update that delivered only the file left nothing able to run it (boop-ldou).
|
|
274
282
|
const scriptActions = (0, script_merge_1.planScriptUpdates)(capJson.scripts, (0, script_merge_1.readProjectScripts)(targetDir));
|
|
@@ -304,6 +312,9 @@ function executeUpdatePlan(plan, options = {}) {
|
|
|
304
312
|
scriptsKept: [],
|
|
305
313
|
unchangedCount: plan.unchangedCount,
|
|
306
314
|
instructions: [],
|
|
315
|
+
moved: [],
|
|
316
|
+
removed: [],
|
|
317
|
+
movesWaiting: [],
|
|
307
318
|
};
|
|
308
319
|
const pkgJson = JSON.parse(fs.readFileSync(path.join(plan.packageDir, 'package.json'), 'utf-8'));
|
|
309
320
|
const boopkit = pkgJson.boopkit;
|
|
@@ -335,7 +346,13 @@ function executeUpdatePlan(plan, options = {}) {
|
|
|
335
346
|
break;
|
|
336
347
|
case 'skip': {
|
|
337
348
|
const isLocallyDeleted = action.reason === 'Locally deleted';
|
|
338
|
-
if (
|
|
349
|
+
if (action.movedTo) {
|
|
350
|
+
// A move waiting on something only a person or another command can settle; --force and --restore do not apply
|
|
351
|
+
result.skipped.push(action.relativePath);
|
|
352
|
+
result.movesWaiting.push(action.relativePath);
|
|
353
|
+
result.instructions.push({ relativePath: action.relativePath, instruction: action.instruction });
|
|
354
|
+
}
|
|
355
|
+
else if (isLocallyDeleted && options.restore) {
|
|
339
356
|
// Restore mode: re-add locally deleted files
|
|
340
357
|
const srcPath = resolveSourcePath(plan.packageDir, action.relativePath);
|
|
341
358
|
if (srcPath) {
|
|
@@ -428,15 +445,27 @@ function executeUpdatePlan(plan, options = {}) {
|
|
|
428
445
|
if (!manifest.capabilityMeta) {
|
|
429
446
|
manifest.capabilityMeta = {};
|
|
430
447
|
}
|
|
448
|
+
const previous = manifest.capabilityMeta[plan.capability];
|
|
431
449
|
manifest.capabilityMeta[plan.capability] = {
|
|
432
450
|
installedAt: new Date().toISOString(),
|
|
433
451
|
version: plan.newVersion,
|
|
434
452
|
fileHashes: newHashes,
|
|
453
|
+
...movedFromStill(previous, newHashes),
|
|
435
454
|
};
|
|
436
455
|
(0, project_1.writeProjectManifest)(plan.targetDir, manifest);
|
|
437
456
|
}
|
|
438
457
|
return result;
|
|
439
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* The `movedFrom` entries still worth keeping: once this capability writes a
|
|
461
|
+
* file's hash itself, the base is a file it shipped, and the old path no longer
|
|
462
|
+
* leads to the original text.
|
|
463
|
+
*/
|
|
464
|
+
function movedFromStill(previous, newHashes) {
|
|
465
|
+
const kept = Object.entries(previous?.movedFrom ?? {})
|
|
466
|
+
.filter(([p]) => newHashes[p] !== undefined && newHashes[p] === previous.fileHashes[p]);
|
|
467
|
+
return kept.length > 0 ? { movedFrom: Object.fromEntries(kept) } : {};
|
|
468
|
+
}
|
|
440
469
|
/**
|
|
441
470
|
* What to tell whoever ran the update about a file it skipped, or nothing.
|
|
442
471
|
* `drift` is absent for a file boop has no stored hash for.
|
|
@@ -486,6 +515,40 @@ function displayPath(targetDir, filePath) {
|
|
|
486
515
|
const relative = path.relative(targetDir, filePath);
|
|
487
516
|
return relative.startsWith('..') || path.isAbsolute(relative) ? filePath : relative;
|
|
488
517
|
}
|
|
518
|
+
/**
|
|
519
|
+
* Rewrite each capability's stored env record (capabilityConfig[cap].env) from
|
|
520
|
+
* the env its installed package declares. A variable the package still declares
|
|
521
|
+
* keeps its `set` flag; one it no longer declares is dropped, and a capability
|
|
522
|
+
* that declares none loses its record. `boop status` prefers this record over
|
|
523
|
+
* the package, so a stale one would list a moved variable under both capabilities.
|
|
524
|
+
*/
|
|
525
|
+
function refreshCapabilityEnv(targetDir, capabilities) {
|
|
526
|
+
const manifest = (0, project_1.readProjectManifest)(targetDir);
|
|
527
|
+
if (!manifest)
|
|
528
|
+
return;
|
|
529
|
+
const config = manifest.capabilityConfig ?? {};
|
|
530
|
+
for (const cap of capabilities) {
|
|
531
|
+
const meta = (0, capability_1.getCapability)(cap, [targetDir]);
|
|
532
|
+
if (!meta)
|
|
533
|
+
continue;
|
|
534
|
+
const stored = config[cap]?.env ?? {};
|
|
535
|
+
if (meta.env && meta.env.length > 0) {
|
|
536
|
+
const env = {};
|
|
537
|
+
for (const e of meta.env) {
|
|
538
|
+
env[e.name] = { required: e.required, set: stored[e.name]?.set ?? false };
|
|
539
|
+
}
|
|
540
|
+
config[cap] = { env };
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
delete config[cap];
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (Object.keys(config).length > 0)
|
|
547
|
+
manifest.capabilityConfig = config;
|
|
548
|
+
else
|
|
549
|
+
delete manifest.capabilityConfig;
|
|
550
|
+
(0, project_1.writeProjectManifest)(targetDir, manifest);
|
|
551
|
+
}
|
|
489
552
|
/**
|
|
490
553
|
* Count the skills a capability's project copy has edited in SKILL.md, and how
|
|
491
554
|
* many of those boop has changed since — from the same comparison `boop update`
|
|
@@ -523,6 +586,17 @@ function resolveSourcePath(pkgDir, relativePath) {
|
|
|
523
586
|
*/
|
|
524
587
|
function formatUpdateSummary(result) {
|
|
525
588
|
const parts = [];
|
|
589
|
+
const movedTo = new Map();
|
|
590
|
+
for (const m of result.moved ?? [])
|
|
591
|
+
movedTo.set(m.toCapability, [...(movedTo.get(m.toCapability) ?? []), m]);
|
|
592
|
+
for (const [capability, moves] of movedTo) {
|
|
593
|
+
const noun = moves.every(m => m.from.endsWith('/')) ? 'skill' : 'file';
|
|
594
|
+
parts.push(`moved ${moves.length} ${noun}${moves.length === 1 ? '' : 's'} to ${capability}`);
|
|
595
|
+
}
|
|
596
|
+
const removed = result.removed?.length || 0;
|
|
597
|
+
if (removed > 0) {
|
|
598
|
+
parts.push(`removed ${removed} skill${removed === 1 ? '' : 's'}`);
|
|
599
|
+
}
|
|
526
600
|
if (result.migrated && result.migrated.length > 0) {
|
|
527
601
|
parts.push(`migrated ${result.migrated.length} skill${result.migrated.length === 1 ? '' : 's'}`);
|
|
528
602
|
}
|
|
@@ -533,7 +607,11 @@ function formatUpdateSummary(result) {
|
|
|
533
607
|
if (result.forcedOverwrite.length > 0) {
|
|
534
608
|
parts.push(`force-overwrote ${result.forcedOverwrite.length} modified file${result.forcedOverwrite.length === 1 ? '' : 's'}`);
|
|
535
609
|
}
|
|
536
|
-
const
|
|
610
|
+
const waiting = result.movesWaiting?.length || 0;
|
|
611
|
+
if (waiting > 0) {
|
|
612
|
+
parts.push(`${waiting} move${waiting === 1 ? '' : 's'} waiting`);
|
|
613
|
+
}
|
|
614
|
+
const skippedModified = result.skipped.length - (result.skippedDeleted?.length || 0) - waiting;
|
|
537
615
|
if (skippedModified > 0) {
|
|
538
616
|
parts.push(`skipped ${skippedModified} (locally modified)`);
|
|
539
617
|
}
|
package/docs/init.md
CHANGED
|
@@ -66,6 +66,7 @@ It has neither a kernel nor templates, so there is nothing for `boop init` to wr
|
|
|
66
66
|
and `boop init --profile development` is refused with a pointer to the two ways it is meant to be used:
|
|
67
67
|
naming it as the `profile` in an existing project's `.boop/config.json`,
|
|
68
68
|
or `boop add` for the capabilities you want.
|
|
69
|
+
The second is the usual one; see [Bringing Boop Into an Existing Project](#bringing-boop-into-an-existing-project).
|
|
69
70
|
|
|
70
71
|
## What It Creates
|
|
71
72
|
|
|
@@ -207,6 +208,27 @@ The counts change as capabilities grow.
|
|
|
207
208
|
A `base` project's summary counts template files rather than kernel files,
|
|
208
209
|
and its next steps stop at `boop status`, because there is no development server to start.
|
|
209
210
|
|
|
211
|
+
## Bringing Boop Into an Existing Project
|
|
212
|
+
|
|
213
|
+
`boop init` only creates new projects: it refuses a folder that is not empty.
|
|
214
|
+
To bring boop's development capabilities into a project something else made —
|
|
215
|
+
an app from `create-expo-app` or Xcode, a CLI, a library —
|
|
216
|
+
run `boop add` in that project for each capability you want:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
cd my-expo-app
|
|
220
|
+
boop add project-mgmt
|
|
221
|
+
boop add coding
|
|
222
|
+
boop status
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The first `boop add` creates the `.boop/config.json` the project lacks and says so in its output.
|
|
226
|
+
The project is named after the `name` in its `package.json`, or after its folder when there is none,
|
|
227
|
+
and its profile is recorded as `development`.
|
|
228
|
+
A project with no `package.json` at all, such as a native iOS or Android app, also gets a minimal one,
|
|
229
|
+
the same one the `base` profile writes, because boop delivers every capability as an npm package recorded there.
|
|
230
|
+
If an add is refused — a capability that needs another one added first, for example — it creates neither file.
|
|
231
|
+
|
|
210
232
|
## What To Do After Init
|
|
211
233
|
|
|
212
234
|
Run the commands under "Next steps".
|
package/docs/loop.md
CHANGED
|
@@ -115,14 +115,22 @@ For each ready branch (one at a time, with `main` as the integration branch):
|
|
|
115
115
|
|
|
116
116
|
1. **Readiness** — a branch is ready when every beads issue in its scope is closed (scope is read from the worktree's `.boop-worktree.json`). `bd ready` is insufficient here — it only lists *unblocked* issues — so `--merge` lists by status and filters by scope. An explicit `boop loop --merge work/<scope>` skips this check.
|
|
117
117
|
2. **Rebase onto main** inside the worktree. Only **`package.json` dependency conflicts** are auto-resolved (union of dependencies; same-key version conflicts take the higher semver). Any other conflict aborts the rebase, files a beads bug, and skips to the next branch.
|
|
118
|
-
|
|
118
|
+
A rebase that does not complete for any other reason is handled the same way, before any test runs: the branch is skipped, its worktree and `main` are left as they were, and the filed bug says why.
|
|
119
|
+
The common case is a worktree with uncommitted changes to tracked files (the loop's `npm install` can rewrite `package-lock.json`); the bug then names those files, and you commit or discard them in the worktree before running `--merge` again.
|
|
120
|
+
When git refuses the rebase for another reason, such as an untracked file that `main` would overwrite, the bug carries git's own message.
|
|
121
|
+
3. **Run `npm test`** in the worktree. On failure the branch is reset to its pre-rebase commit, a beads bug is filed, and the branch is skipped. The bug names the branch and the restored commit, and carries the first failures and the Jest summary, each capped in length; the full test output is saved to `labs/claude-runs/_merge_<branch>_<timestamp>.log` and the bug gives its path.
|
|
119
122
|
4. **Fast-forward merge** into `main`, then remove the worktree and delete the merged branch.
|
|
120
123
|
|
|
121
|
-
**Safety contract:** `main` is never left dirty. The integration tree must be on a clean `main` before merging; a rebase conflict aborts cleanly, and a post-rebase test failure resets the feature branch. Every merge runs the test suite before completing, so no merge lands untested.
|
|
124
|
+
**Safety contract:** `main` is never left dirty. The integration tree must be on a clean `main` before merging; a rebase conflict aborts cleanly, a rebase that does not complete is never tested or merged, and a post-rebase test failure resets the feature branch. Every merge runs the test suite before completing, so no merge lands untested.
|
|
125
|
+
|
|
126
|
+
**Beads export files do not block a merge.** `bd` rewrites `.beads/interactions.jsonl` in the main checkout whenever it runs, including every `bd` command a loop session runs from inside its own worktree, so after any loop `main` shows that file modified.
|
|
127
|
+
`--merge` treats an uncommitted change to `.beads/interactions.jsonl` or `.beads/issues.jsonl` as beads bookkeeping rather than work in progress: it merges past it and leaves the change in place for you to commit when you choose.
|
|
128
|
+
Any other uncommitted change still stops the merge, and the message names the files.
|
|
129
|
+
One exception keeps the fast-forward from running over your change: if a branch itself changes an export file that is uncommitted in `main`, that branch is skipped with a message saying so, and the other ready branches still merge. Commit the file in `main` and run `--merge` again.
|
|
122
130
|
|
|
123
131
|
## Logs
|
|
124
132
|
|
|
125
|
-
Session logs are written to `labs/claude-runs/` in the project directory. Each issue produces a log file named `<issue-id>_<timestamp>.log` containing the full Claude stream-json output.
|
|
133
|
+
Session logs are written to `labs/claude-runs/` in the project directory. Each issue produces a log file named `<issue-id>_<timestamp>.log` containing the full Claude stream-json output. A `--merge` test failure saves the full test output as `_merge_<branch>_<timestamp>.log`.
|
|
126
134
|
|
|
127
135
|
## Safety
|
|
128
136
|
|
package/docs/skills.md
CHANGED
|
@@ -20,15 +20,18 @@ Every capability ships **agent skills** — focused `SKILL.md` guides that `boop
|
|
|
20
20
|
- **boop-product-design-feature-documentation** — When you need to document a complex feature, function, or system — including when you frame the task as 'write the docs', 'write a README', 'write up how X works', or 'explain this for the next reader who picks it up cold'.
|
|
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
|
+
- **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.
|
|
23
24
|
- **boop-product-design-thought-partnership** — When the user wants to think out loud through a design — a feature, a capability, a strategic direction — be a thought partner, not an implementer: hold the conversation at user-and-value altitude, honor uncertainty with hypotheses rather than pretend-decisions, and keep software-shaped concerns downstream of the user's lived problems.
|
|
24
25
|
- **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.
|
|
25
26
|
- **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.
|
|
26
27
|
- **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.
|
|
27
28
|
|
|
28
|
-
### `project-mgmt` — Project management with beads
|
|
29
|
+
### `project-mgmt` — Project management with beads: planning, issue sweeps and stray disposition
|
|
29
30
|
|
|
30
31
|
- **boop-project-mgmt-beads-planning** — When you need to write and organize beads issues so that any fresh agent can pick up work.
|
|
31
32
|
- **boop-project-mgmt-engineering-planning** — When you need to decompose a design into an executable plan of work — including when you frame the task as 'make a plan', 'break this into tasks or steps', 'figure out the order of work', or planning a build before you start writing issues. Order the work by uncertainty reduction, not just demonstrable value.
|
|
33
|
+
- **boop-project-mgmt-issue-sweep-workflow** — When the bd queue has accumulated a noisy backlog of lingering P1/P2 items — paper-cuts, small refactors, dead code, cosmetic bugs, taxonomy drift — and you want to clear them autonomously in a sequence that supports the week's goals, follow this process: inventory → cluster by theme → pick a cluster with the operator → run autonomously in one sweep worktree → hand off for a single UAT pass → log. The work lands in a worktree for one operator review rather than on main; it arrives with no decisions left in it.
|
|
34
|
+
- **boop-project-mgmt-stray-disposition** — When an autonomous run (a loop, a sweep, a sub-agent session) has finished and filed follow-up issues along the way, settle each one before you write the closing summary. Every stray gets a recorded disposition — re-parented and reachable from the roadmap, folded into a spec and closed, or triaged away. Use this whenever a run ends with issues in the queue that nobody has decided to do.
|
|
32
35
|
|
|
33
36
|
### `skills` — Skill authoring
|
|
34
37
|
|
|
@@ -40,31 +43,29 @@ Every capability ships **agent skills** — focused `SKILL.md` guides that `boop
|
|
|
40
43
|
- **boop-skills-hyperlinking** — When you need to add, review, or repair a `## Related` section that points one skill at another, follow these rules for what earns a link, how to phrase it, which way it points, how many are too many, and how to write a path that survives `boop add`.
|
|
41
44
|
- **boop-skills-edit-triage** — When you need to move a project's edits out of a boop skill's SKILL.md — because `boop update` or `boop status` reports the skill as edited here, or `boop migrate-skill-edits` wrote a plan for it — decide whether each edit is kept in this project, offered to boop, or dropped, and rewrite each kept edit as a standalone rule for the PROJECT.md beside it. Not for reference pages, which are blended rather than split.
|
|
42
45
|
|
|
43
|
-
### `testing` —
|
|
46
|
+
### `testing` — Testing method: the testing principles and the TDD-Lite workflow
|
|
44
47
|
|
|
45
48
|
- **boop-testing-tdd-light** — When you need to build features, implement changes, or fix bugs, follow the Light TDD Flow. This includes bug fixes found during UAT — write the failing test *before* applying the fix.
|
|
46
|
-
- **boop-testing-testing-express-web** — When you need to write or run tests for the Express server — services, controllers, middleware, and HTTP routes.
|
|
47
49
|
- **boop-testing-testing** — When you need to write, run, or reason about tests in this project.
|
|
48
50
|
|
|
51
|
+
### `typescript` — TypeScript toolchain: Jest with ts-jest, and the whole-program type check
|
|
52
|
+
|
|
53
|
+
- **boop-typescript-testing** — When you need to run, configure, or debug tests in a TypeScript project — how `npm test` and `npm run typecheck` behave under Jest, ts-jest and `tsc`.
|
|
54
|
+
|
|
49
55
|
### `ux` — UX design judgement and the discovery/delivery workflows that operate it
|
|
50
56
|
|
|
51
|
-
- **boop-ux-purpose-judgement** — When you are evaluating a rendered 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).
|
|
52
|
-
- **boop-ux-visual-judgement** — When you are evaluating a rendered page and need to form a principled judgement about how it looks and scans. Visual lens only — eye-path, register, anatomy, scan-line, gestalt, density, voice. Pairs with ux-purpose-judgement (the question of whether each element belongs at all) and with the CSS skill (the grammar of how things are rendered).
|
|
53
|
-
- **boop-ux-eval-and-refine-workflow** — When you are tasked with iteratively evaluating and improving an existing rendered page (discovery, not delivery), follow this workflow. Inner loop: capture → eval → recommend → implement → re-capture. Outer: knowledge integration.
|
|
54
|
-
- **boop-ux-app-ui-dev-workflow** — When you are tasked with improving an existing app-side page (app UX, not marketing) end-to-end and the input is a target 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.
|
|
57
|
+
- **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, register, anatomy, scan-line, gestalt, density, voice. Pairs with ux-purpose-judgement (the question of whether each element belongs at all) and with the CSS skill (the grammar of how things are rendered).
|
|
59
|
+
- **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
|
+
- **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.
|
|
55
61
|
|
|
56
|
-
### `workflows` — Reusable end-to-end development and
|
|
62
|
+
### `workflows` — Reusable end-to-end development and audit workflows for code
|
|
57
63
|
|
|
58
64
|
- **boop-feature-dev-workflow** — When you are tasked with building a feature end-to-end, follow this guidance.
|
|
59
65
|
- **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.
|
|
60
66
|
- **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.
|
|
61
67
|
- **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.
|
|
62
68
|
- **boop-architecture-audit-workflow** — When you need to judge whether an area of shipped code is structurally healthy and decide what is worth fixing — undesired coupling, concerns bleeding across layers or domain boundaries, interfaces that are not composable because they are use-case specific or unparameterized — follow this process: scope one area and name the properties it should uphold → check the tests can catch you → measure before reading → grade every property and classify every finding against a named change → critique → operator gate → decompose → execute via refactor-dev-workflow. The sibling of test-audit-workflow for production code rather than tests. Not for finding bugs, and not for executing a structural change you have already decided on.
|
|
63
|
-
- **boop-issue-sweep-workflow** — When the bd queue has accumulated a noisy backlog of lingering P1/P2 items — paper-cuts, small refactors, dead code, cosmetic bugs, taxonomy drift — and you want to clear them autonomously in a sequence that supports the week's goals, follow this process: inventory → cluster by theme → pick a cluster with the operator → run autonomously in one sweep worktree → hand off for a single UAT pass → log. The work lands in a worktree for one operator review rather than on main; it arrives with no decisions left in it.
|
|
64
|
-
- **boop-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.
|
|
65
|
-
- **boop-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.
|
|
66
|
-
- **boop-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.
|
|
67
|
-
- **boop-stray-disposition** — When an autonomous run (a loop, a sweep, a sub-agent session) has finished and filed follow-up issues along the way, settle each one before you write the closing summary. Every stray gets a recorded disposition — re-parented and reachable from the roadmap, folded into a spec and closed, or triaged away. Use this whenever a run ends with issues in the queue that nobody has decided to do.
|
|
68
69
|
|
|
69
70
|
## Application capabilities
|
|
70
71
|
|
|
@@ -101,6 +102,9 @@ Every capability ships **agent skills** — focused `SKILL.md` guides that `boop
|
|
|
101
102
|
### `web` — Web UI with Handlebars + Tailwind
|
|
102
103
|
|
|
103
104
|
- **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-testing** — When you need to write or run tests for the Express server — services, controllers, middleware, and HTTP routes.
|
|
106
|
+
- **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
|
+
- **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.
|
|
104
108
|
|
|
105
109
|
---
|
|
106
110
|
|
package/package.json
CHANGED
package/profiles/base/profile.md
CHANGED
|
@@ -31,8 +31,8 @@ and if a file only makes sense for an application, it does not belong here.
|
|
|
31
31
|
Three capabilities, all of them `type: development`,
|
|
32
32
|
whose skills apply to work that isn't code without any edits:
|
|
33
33
|
|
|
34
|
-
- **project-mgmt** — beads issue tracking and
|
|
35
|
-
- **product-design** — requirements, design, documentation and writing skills
|
|
34
|
+
- **project-mgmt** — beads issue tracking, planning, issue sweeps and stray disposition
|
|
35
|
+
- **product-design** — requirements, design, documentation, knowledge backfill and writing skills
|
|
36
36
|
- **skills** — skill authoring, discovery and maintenance
|
|
37
37
|
|
|
38
38
|
The profile also ships its own `templates/` folder,
|
|
@@ -53,25 +53,28 @@ and `boop update` would have nothing to update.
|
|
|
53
53
|
## Three Deliberate Omissions
|
|
54
54
|
|
|
55
55
|
The development capabilities that every other profile carries —
|
|
56
|
-
`coding`, `testing` and `workflows` —
|
|
56
|
+
`coding`, `testing` and `workflows`, and the `typescript` toolchain beside them —
|
|
57
57
|
are left out on purpose.
|
|
58
58
|
An agent takes the skills and files in a project as a hint about what kind of work happens there,
|
|
59
59
|
so every code-shaped thing in the scaffold is an invitation to do code-shaped work.
|
|
60
60
|
|
|
61
61
|
### No testing
|
|
62
62
|
|
|
63
|
-
`testing` installs
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
`testing` installs the testing skills, and `typescript`, which comes with it in the other profiles,
|
|
64
|
+
installs Jest, `ts-jest`, a `tsconfig.test.json` and a `tests/` folder.
|
|
65
|
+
A project with nothing to test would carry testing guidance and a test runner nobody uses,
|
|
66
|
+
and an agent that finds them will reasonably suggest writing tests.
|
|
66
67
|
|
|
67
68
|
### No workflows
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
feature development, refactoring, test audits
|
|
70
|
+
Every `workflows` skill is for code:
|
|
71
|
+
feature development, refactoring, test audits and architecture audits.
|
|
71
72
|
An agent asked to "set up the network" could reasonably pick one of them up
|
|
72
73
|
and start a feature branch for something that has no code in it.
|
|
73
|
-
The
|
|
74
|
-
|
|
74
|
+
The workflows that suit any project that tracks its work in beads —
|
|
75
|
+
issue sweeps, stray disposition and knowledge backfill —
|
|
76
|
+
ship in `project-mgmt` and `product-design` instead,
|
|
77
|
+
so this profile receives them without `workflows`.
|
|
75
78
|
|
|
76
79
|
### No coding
|
|
77
80
|
|
|
@@ -4,6 +4,7 @@ description: Development skills only — for a project with no application domai
|
|
|
4
4
|
defaultCapabilities:
|
|
5
5
|
# Development
|
|
6
6
|
- coding
|
|
7
|
+
- typescript
|
|
7
8
|
- testing
|
|
8
9
|
- project-mgmt
|
|
9
10
|
- product-design
|
|
@@ -31,16 +32,17 @@ but they are all developed the same way.
|
|
|
31
32
|
|
|
32
33
|
## What You Get
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
plus the
|
|
35
|
+
Seven capabilities, all of them `type: development` —
|
|
36
|
+
skill bundles that guide agent behaviour,
|
|
37
|
+
plus the TypeScript toolchain those skills assume:
|
|
37
38
|
|
|
38
39
|
- **coding** — development conventions, code style, commit and debugging skills
|
|
39
|
-
- **
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
40
|
+
- **typescript** — the TypeScript toolchain: Jest with ts-jest, `tsconfig.test.json`, and the `test` and `typecheck` scripts
|
|
41
|
+
- **testing** — the testing principles and the TDD-Lite workflow
|
|
42
|
+
- **project-mgmt** — beads issue tracking, planning, issue sweeps and stray disposition
|
|
43
|
+
- **product-design** — feature requirements, engineering design, documentation and knowledge backfill skills
|
|
42
44
|
- **skills** — skill authoring, discovery and maintenance
|
|
43
|
-
- **workflows** — the end-to-end development and
|
|
45
|
+
- **workflows** — the end-to-end code development and audit workflows
|
|
44
46
|
|
|
45
47
|
No web layer, no API layer, no service layer, no database.
|
|
46
48
|
Add any of those with `boop add` if the project grows an application domain.
|
|
@@ -72,7 +74,7 @@ and the consequence is deliberate: `boop init --profile development` is refused
|
|
|
72
74
|
with a message pointing at the two ways this profile is actually meant to be used —
|
|
73
75
|
name it in an existing project's `.boop/config.json`,
|
|
74
76
|
or `boop add` these capabilities one at a time.
|
|
75
|
-
The capabilities carry their own dependencies (`jest
|
|
77
|
+
The capabilities carry their own dependencies (`typescript` brings `jest` and `ts-jest`, and `project-mgmt` brings `beads`),
|
|
76
78
|
so nothing is lost by having no kernel to merge them from.
|
|
77
79
|
|
|
78
80
|
### No reference/
|