@boopkit/cli 0.6.14 → 0.6.15
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 +5 -5
- package/dist/cli.js +0 -0
- package/dist/commands/init.js +1 -1
- package/dist/commands/update.js +30 -1
- package/dist/lib/execute.js +3 -12
- package/dist/lib/scaffold.d.ts +36 -6
- package/dist/lib/scaffold.js +79 -10
- package/dist/lib/script-merge.d.ts +55 -0
- package/dist/lib/script-merge.js +144 -0
- package/dist/lib/stale-scripts.d.ts +3 -2
- package/dist/lib/stale-scripts.js +3 -2
- package/dist/lib/update.d.ts +7 -0
- package/dist/lib/update.js +67 -47
- package/package.json +1 -1
- package/dist/commands/loop.js.orig-pre-maxbuffer +0 -1455
- package/dist/commands/status.test.d.ts +0 -1
- package/dist/commands/status.test.js +0 -118
- package/dist/lib/project.test.d.ts +0 -1
- package/dist/lib/project.test.js +0 -118
package/dist/lib/update.js
CHANGED
|
@@ -52,6 +52,7 @@ const capability_1 = require("./capability");
|
|
|
52
52
|
const project_1 = require("./project");
|
|
53
53
|
const safe_io_1 = require("./safe-io");
|
|
54
54
|
const install_layout_1 = require("./install-layout");
|
|
55
|
+
const script_merge_1 = require("./script-merge");
|
|
55
56
|
/** The skill that splits a drifted SKILL.md — keep in step with the folder name boop-rm2l.8 gives it */
|
|
56
57
|
exports.TRIAGE_SKILL = 'boop-skills-edit-triage';
|
|
57
58
|
/** What to do about skills edited in SKILL.md, for a report that counts them */
|
|
@@ -87,6 +88,10 @@ function planCapabilityUpdate(targetDir, capabilityName, projectManifest) {
|
|
|
87
88
|
if (!boopkit?.capability)
|
|
88
89
|
return null;
|
|
89
90
|
const newVersion = pkgJson.version || '0.0.0';
|
|
91
|
+
// The capability as this package declares it: the npm scripts merged below,
|
|
92
|
+
// and the skill migrations applied further down.
|
|
93
|
+
const capJsonPath = path.join(pkgDir, 'capability.json');
|
|
94
|
+
const capJson = fs.existsSync(capJsonPath) ? JSON.parse(fs.readFileSync(capJsonPath, 'utf-8')) : {};
|
|
90
95
|
const meta = projectManifest.capabilityMeta?.[capabilityName];
|
|
91
96
|
const storedHashes = meta?.fileHashes || {};
|
|
92
97
|
const actions = [];
|
|
@@ -215,60 +220,59 @@ function planCapabilityUpdate(targetDir, capabilityName, projectManifest) {
|
|
|
215
220
|
}
|
|
216
221
|
}
|
|
217
222
|
// Apply skill migration logic: upgrade deprecate+add pairs to migrate actions
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
action.reason = `Skipped: old skill file '${oldRelPath}' was locally modified`;
|
|
238
|
-
action.oldPath = oldRelPath;
|
|
239
|
-
action.instruction = `boop installs this once ${oldRelPath} is gone; see what to do about that file.`;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
// Change deprecate to skip for the old file
|
|
243
|
-
const oldAction = actions.find(a => a.relativePath === oldRelPath);
|
|
244
|
-
if (oldAction) {
|
|
245
|
-
const projectFile = path.join('skills', newSkillName, 'PROJECT.md');
|
|
246
|
-
oldAction.action = 'skip';
|
|
247
|
-
oldAction.reason = 'Locally modified (migration skipped)';
|
|
248
|
-
oldAction.replacedBy = newSkillName;
|
|
249
|
-
oldAction.instruction = `This is the old single-file form of the ${newSkillName} skill, and this project edited it. ` +
|
|
250
|
-
`Move the rules this project needs from it into ${projectFile}, following the ${exports.TRIAGE_SKILL} skill, then delete it and run \`boop update\`.`;
|
|
223
|
+
const migrations = capJson.skill_migrations;
|
|
224
|
+
if (migrations) {
|
|
225
|
+
for (const [newSkillName, oldFlatFilename] of Object.entries(migrations)) {
|
|
226
|
+
const oldRelPath = path.join('skills', oldFlatFilename);
|
|
227
|
+
const newPrefix = path.join('skills', newSkillName) + path.sep;
|
|
228
|
+
const oldStoredHash = storedHashes[oldRelPath];
|
|
229
|
+
if (!oldStoredHash)
|
|
230
|
+
continue;
|
|
231
|
+
const oldFilePath = path.join(targetDir, oldRelPath);
|
|
232
|
+
const oldState = compareFileHash(oldFilePath, oldStoredHash);
|
|
233
|
+
if (oldState === 'modified') {
|
|
234
|
+
// User modified the old file — keep it, and don't install the folder that replaces it.
|
|
235
|
+
// A folder file already installed has its own comparison, so only an install is held back.
|
|
236
|
+
for (const action of actions) {
|
|
237
|
+
if (action.action === 'add' && (action.relativePath.startsWith(newPrefix) || action.relativePath === path.join('skills', newSkillName))) {
|
|
238
|
+
action.action = 'skip';
|
|
239
|
+
action.reason = `Skipped: old skill file '${oldRelPath}' was locally modified`;
|
|
240
|
+
action.oldPath = oldRelPath;
|
|
241
|
+
action.instruction = `boop installs this once ${oldRelPath} is gone; see what to do about that file.`;
|
|
251
242
|
}
|
|
252
243
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
244
|
+
// Change deprecate to skip for the old file
|
|
245
|
+
const oldAction = actions.find(a => a.relativePath === oldRelPath);
|
|
246
|
+
if (oldAction) {
|
|
247
|
+
const projectFile = path.join('skills', newSkillName, 'PROJECT.md');
|
|
248
|
+
oldAction.action = 'skip';
|
|
249
|
+
oldAction.reason = 'Locally modified (migration skipped)';
|
|
250
|
+
oldAction.replacedBy = newSkillName;
|
|
251
|
+
oldAction.instruction = `This is the old single-file form of the ${newSkillName} skill, and this project edited it. ` +
|
|
252
|
+
`Move the rules this project needs from it into ${projectFile}, following the ${exports.TRIAGE_SKILL} skill, then delete it and run \`boop update\`.`;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else if (oldState === 'match') {
|
|
256
|
+
// Old file unmodified — convert deprecate to migrate
|
|
257
|
+
const oldAction = actions.find(a => a.relativePath === oldRelPath);
|
|
258
|
+
if (oldAction) {
|
|
259
|
+
oldAction.action = 'migrate';
|
|
260
|
+
oldAction.reason = `Migrated to folder: skills/${newSkillName}/`;
|
|
261
|
+
}
|
|
262
|
+
// Tag new folder files with the old path they're replacing
|
|
263
|
+
for (const action of actions) {
|
|
264
|
+
if (action.relativePath.startsWith(newPrefix) || action.relativePath === path.join('skills', newSkillName)) {
|
|
265
|
+
action.oldPath = oldRelPath;
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
|
-
// oldState === 'missing': old file already gone, add new files normally
|
|
268
268
|
}
|
|
269
|
+
// oldState === 'missing': old file already gone, add new files normally
|
|
269
270
|
}
|
|
270
271
|
}
|
|
271
|
-
|
|
272
|
+
// A capability that ships a new command ships it as a file and a script; an
|
|
273
|
+
// update that delivered only the file left nothing able to run it (boop-ldou).
|
|
274
|
+
const scriptActions = (0, script_merge_1.planScriptUpdates)(capJson.scripts, (0, script_merge_1.readProjectScripts)(targetDir));
|
|
275
|
+
const upToDate = actions.length === 0 && !scriptActions.some(a => a.action === 'add');
|
|
272
276
|
return {
|
|
273
277
|
capability: capabilityName,
|
|
274
278
|
targetDir,
|
|
@@ -276,6 +280,7 @@ function planCapabilityUpdate(targetDir, capabilityName, projectManifest) {
|
|
|
276
280
|
currentVersion: meta?.version,
|
|
277
281
|
newVersion,
|
|
278
282
|
actions,
|
|
283
|
+
scriptActions,
|
|
279
284
|
unchangedCount,
|
|
280
285
|
upToDate,
|
|
281
286
|
};
|
|
@@ -295,6 +300,8 @@ function executeUpdatePlan(plan, options = {}) {
|
|
|
295
300
|
skippedDeleted: [],
|
|
296
301
|
pruned: [],
|
|
297
302
|
migrated: [],
|
|
303
|
+
scriptsAdded: [],
|
|
304
|
+
scriptsKept: [],
|
|
298
305
|
unchangedCount: plan.unchangedCount,
|
|
299
306
|
instructions: [],
|
|
300
307
|
};
|
|
@@ -411,6 +418,11 @@ function executeUpdatePlan(plan, options = {}) {
|
|
|
411
418
|
}
|
|
412
419
|
}
|
|
413
420
|
}
|
|
421
|
+
// Merge the capability's npm scripts, so a command it ships as file + script
|
|
422
|
+
// arrives whole. Additive only: a name the project already uses is left alone.
|
|
423
|
+
const scriptActions = plan.scriptActions ?? [];
|
|
424
|
+
result.scriptsAdded = (0, script_merge_1.applyScriptUpdates)(plan.targetDir, scriptActions);
|
|
425
|
+
result.scriptsKept = scriptActions.filter(a => a.action === 'keep');
|
|
414
426
|
// Update .boop/config.json
|
|
415
427
|
if (manifest) {
|
|
416
428
|
if (!manifest.capabilityMeta) {
|
|
@@ -531,6 +543,14 @@ function formatUpdateSummary(result) {
|
|
|
531
543
|
if (result.added.length > 0) {
|
|
532
544
|
parts.push(`added ${result.added.length} new file${result.added.length === 1 ? '' : 's'}`);
|
|
533
545
|
}
|
|
546
|
+
const scriptsAdded = result.scriptsAdded?.length || 0;
|
|
547
|
+
if (scriptsAdded > 0) {
|
|
548
|
+
parts.push(`added ${scriptsAdded} script${scriptsAdded === 1 ? '' : 's'}`);
|
|
549
|
+
}
|
|
550
|
+
const scriptsKept = result.scriptsKept?.length || 0;
|
|
551
|
+
if (scriptsKept > 0) {
|
|
552
|
+
parts.push(`kept ${scriptsKept} customised script${scriptsKept === 1 ? '' : 's'}`);
|
|
553
|
+
}
|
|
534
554
|
if (result.pruned && result.pruned.length > 0) {
|
|
535
555
|
parts.push(`pruned ${result.pruned.length} deprecated file${result.pruned.length === 1 ? '' : 's'}`);
|
|
536
556
|
}
|
package/package.json
CHANGED