loadout-ai 0.5.1 → 0.5.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/CHANGELOG.md +11 -0
- package/MASTER_PLAN.md +8 -0
- package/README.md +2 -2
- package/dist/src/cli.js +1 -1
- package/dist/src/core/active-policy.js +15 -2
- package/dist/src/core/active-set.js +30 -3
- package/dist/src/core/recommend.js +4 -2
- package/docs/USER_TEST_GUIDE.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.5.2 - 2026-07-21
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Revalidate and apply the exact skill-and-agent activation delta shown in a project
|
|
10
|
+
preview instead of broadening package selections across every requested agent.
|
|
11
|
+
- Reject browser-testing skills and Playwright MCP recommendations for CLI-only
|
|
12
|
+
TypeScript projects without a browser or frontend signal.
|
|
13
|
+
- Replace unexplained ranking numbers and a duplicated full activation delta with a
|
|
14
|
+
concise exact-transaction summary in the default project optimization output.
|
|
15
|
+
|
|
5
16
|
## 0.5.1 - 2026-07-21
|
|
6
17
|
|
|
7
18
|
### Fixed
|
package/MASTER_PLAN.md
CHANGED
|
@@ -277,6 +277,14 @@ merged `codex/relatable-readme-hero` remote branch remains safe to delete after
|
|
|
277
277
|
previews an unblocked removal of 29 packages/2,316 disabled records, and health
|
|
278
278
|
reports `library ready (nothing active)` with explicit counts. Release as
|
|
279
279
|
`0.5.1` before continuing the complete-uninstall acceptance step.
|
|
280
|
+
- [x] `P18-38 [TERRA]` Fix the founder-discovered asymmetric project-activation
|
|
281
|
+
scope bug. The `0.5.1` preview correctly budgeted 22 Codex and 18 Claude
|
|
282
|
+
additions, but apply-time revalidation broadened four Codex-only selectors
|
|
283
|
+
onto Claude. Revalidation now preserves the exact package, skill, and agent
|
|
284
|
+
tuple from the preview; a two-agent regression proves exact 30/18 applied
|
|
285
|
+
counts. CLI-only TypeScript projects also reject browser-testing and
|
|
286
|
+
Playwright false positives, and the default preview removes unexplained scores
|
|
287
|
+
and duplicated delta output. Release as `0.5.2` before resuming activation.
|
|
280
288
|
|
|
281
289
|
### Release 0.3 lifecycle hardening
|
|
282
290
|
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</p>
|
|
27
27
|
|
|
28
28
|
> [!IMPORTANT]
|
|
29
|
-
> Installation is version-pinned so the code you test matches these docs. The commands below target `loadout-ai@0.5.
|
|
29
|
+
> Installation is version-pinned so the code you test matches these docs. The commands below target `loadout-ai@0.5.2`; review the preview before every apply.
|
|
30
30
|
|
|
31
31
|
## How it works
|
|
32
32
|
|
|
@@ -76,7 +76,7 @@ Skills, plugins, MCP servers, and agent settings tend to accumulate one experime
|
|
|
76
76
|
You need Node.js 20 or newer and Git.
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
npm install --global loadout-ai@0.5.
|
|
79
|
+
npm install --global loadout-ai@0.5.2
|
|
80
80
|
loadout --version
|
|
81
81
|
loadout guide
|
|
82
82
|
```
|
package/dist/src/cli.js
CHANGED
|
@@ -263,7 +263,7 @@ async function runSetup(options) {
|
|
|
263
263
|
reader?.close();
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
-
const LOADOUT_VERSION = "0.5.
|
|
266
|
+
const LOADOUT_VERSION = "0.5.2";
|
|
267
267
|
function durableSchedulerLauncher() {
|
|
268
268
|
return [
|
|
269
269
|
join(dirname(process.execPath), process.platform === "win32" ? "npx.cmd" : "npx"),
|
|
@@ -57,6 +57,10 @@ function hasProjectCompatibility(name, project) {
|
|
|
57
57
|
if (/(?:publish-to-pages|github-pages|cloudflare-pages)/.test(name) &&
|
|
58
58
|
!project.frameworks.some((framework) => ["react", "next.js", "vue", "svelte"].includes(framework)))
|
|
59
59
|
return false;
|
|
60
|
+
if (/playwright|e2e|webapp-testing|browser-testing/.test(name) &&
|
|
61
|
+
!project.frameworks.includes("playwright") &&
|
|
62
|
+
!project.frameworks.some((framework) => ["react", "next.js", "vue", "svelte"].includes(framework)))
|
|
63
|
+
return false;
|
|
60
64
|
return true;
|
|
61
65
|
}
|
|
62
66
|
const SOURCE_PRIORITY = {
|
|
@@ -396,11 +400,20 @@ export function formatProjectActivation(plan) {
|
|
|
396
400
|
...plan.agentPlans.flatMap((agentPlan) => [
|
|
397
401
|
`${agentPlan.displayName}: ${agentPlan.activeBefore} active (${agentPlan.managedBefore} managed, ${agentPlan.unmanagedBefore} unmanaged); ${agentPlan.capacity}/${plan.limit} slots available`,
|
|
398
402
|
`Proposed additions for ${agentPlan.displayName}: ${agentPlan.selected.length}`,
|
|
399
|
-
...agentPlan.selected.map((item) => ` + ${item.selector}
|
|
403
|
+
...agentPlan.selected.map((item) => ` + ${item.selector} — ${item.reasons.join(", ")}`),
|
|
400
404
|
...agentPlan.alternatives.map((item) => ` = ${item.unitId}: selected ${item.selected}; deferred equivalent source(s): ${item.deferred.join(", ")}`),
|
|
401
405
|
]),
|
|
402
406
|
...(plan.activation
|
|
403
|
-
?
|
|
407
|
+
? plan.activation.blocked
|
|
408
|
+
? [
|
|
409
|
+
"",
|
|
410
|
+
"Blocked activation details:",
|
|
411
|
+
formatActivationPlan(plan.activation),
|
|
412
|
+
]
|
|
413
|
+
: [
|
|
414
|
+
"",
|
|
415
|
+
`Activation transaction: ${plan.activation.changes.length} exact skill change(s), applied together with one rollback snapshot.`,
|
|
416
|
+
]
|
|
404
417
|
: []),
|
|
405
418
|
...plan.warnings.map((warning) => `Warning: ${warning}`),
|
|
406
419
|
].join("\n");
|
|
@@ -287,12 +287,39 @@ export async function planActivationChange(action, packageIds, options = {}) {
|
|
|
287
287
|
function activationKey(record) {
|
|
288
288
|
return `${record.packageId}\0${record.agent}\0${record.unitId ?? ""}`;
|
|
289
289
|
}
|
|
290
|
+
async function revalidateExactActivationPlan(plan) {
|
|
291
|
+
const selectorsByAgent = new Map();
|
|
292
|
+
for (const change of plan.changes) {
|
|
293
|
+
const selectors = selectorsByAgent.get(change.agent) ?? [];
|
|
294
|
+
selectors.push(change.unitId ? `${change.packageId}/${change.unitId}` : change.packageId);
|
|
295
|
+
selectorsByAgent.set(change.agent, selectors);
|
|
296
|
+
}
|
|
297
|
+
const fragments = await Promise.all([...selectorsByAgent].map(([agent, selectors]) => planActivationChange(plan.action, selectors, { agents: [agent] })));
|
|
298
|
+
const changes = fragments.flatMap((fragment) => fragment.changes);
|
|
299
|
+
const plannedKeys = plan.changes.map(activationKey);
|
|
300
|
+
const freshKeys = changes.map(activationKey);
|
|
301
|
+
if (new Set(plannedKeys).size !== plannedKeys.length ||
|
|
302
|
+
new Set(freshKeys).size !== freshKeys.length ||
|
|
303
|
+
plannedKeys.length !== freshKeys.length ||
|
|
304
|
+
plannedKeys.some((key) => !freshKeys.includes(key)))
|
|
305
|
+
throw new Error("Activation selection changed after preview; generate a new plan before applying.");
|
|
306
|
+
const order = new Map(plannedKeys.map((key, index) => [key, index]));
|
|
307
|
+
changes.sort((left, right) => order.get(activationKey(left)) - order.get(activationKey(right)));
|
|
308
|
+
const warnings = fragments.flatMap((fragment) => fragment.warnings);
|
|
309
|
+
return {
|
|
310
|
+
action: plan.action,
|
|
311
|
+
packages: plan.packages,
|
|
312
|
+
...(plan.requestedAgents ? { requestedAgents: plan.requestedAgents } : {}),
|
|
313
|
+
changes,
|
|
314
|
+
skipped: fragments.flatMap((fragment) => fragment.skipped),
|
|
315
|
+
blocked: warnings.length > 0,
|
|
316
|
+
warnings,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
290
319
|
export async function applyActivationChange(plan, options = {}) {
|
|
291
320
|
const applied = await runMutationTransaction(async () => {
|
|
292
321
|
await options.preflight?.();
|
|
293
|
-
const fresh = await
|
|
294
|
-
...(plan.requestedAgents ? { agents: plan.requestedAgents } : {}),
|
|
295
|
-
});
|
|
322
|
+
const fresh = await revalidateExactActivationPlan(plan);
|
|
296
323
|
if (fresh.blocked)
|
|
297
324
|
throw new Error(fresh.warnings.join("; "));
|
|
298
325
|
if (!fresh.changes.length)
|
|
@@ -179,8 +179,10 @@ export function recommendPackages(signals, catalog) {
|
|
|
179
179
|
if (signals.frameworks.some((item) => ["react", "next.js", "vue", "svelte"].includes(item)))
|
|
180
180
|
add("ui-ux-pro-max", `Frontend framework detected: ${signals.frameworks.join(", ")}.`, "high");
|
|
181
181
|
if (signals.frameworks.includes("playwright") ||
|
|
182
|
-
signals.
|
|
183
|
-
add("playwright-mcp",
|
|
182
|
+
signals.frameworks.some((item) => ["react", "next.js", "vue", "svelte"].includes(item)))
|
|
183
|
+
add("playwright-mcp", signals.frameworks.includes("playwright")
|
|
184
|
+
? "Playwright is already configured in this project."
|
|
185
|
+
: "Browser verification may help test the detected frontend framework.", signals.frameworks.includes("playwright") ? "high" : "medium");
|
|
184
186
|
if (signals.files.includes(".git"))
|
|
185
187
|
add("github-mcp-server", "A Git repository was detected; GitHub tools may help with issues and pull requests.", "medium");
|
|
186
188
|
if (signals.roles.includes("obsidian-vault"))
|
package/docs/USER_TEST_GUIDE.md
CHANGED
|
@@ -183,7 +183,7 @@ cleanup deliberately deletes Loadout's snapshots, so it is the last lifecycle te
|
|
|
183
183
|
## Troubleshooting and recovery
|
|
184
184
|
|
|
185
185
|
- **`loadout` is not found after installation:** confirm `npm install --global
|
|
186
|
-
loadout-ai@0.5.
|
|
186
|
+
loadout-ai@0.5.2` completed, run `hash -r`, and confirm npm's global binary
|
|
187
187
|
directory is on `PATH`. For a source checkout, run `npm run build` and `npm link`.
|
|
188
188
|
- **A preview asks for `--approve-risk`:** read the reported scripts, domains,
|
|
189
189
|
credentials, binaries, or instruction findings. If you accept that specific plan,
|