loadout-ai 0.5.0 → 0.5.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/CHANGELOG.md +15 -0
- package/MASTER_PLAN.md +7 -0
- package/README.md +2 -2
- package/dist/src/cli.js +1 -1
- package/dist/src/core/health.js +12 -4
- package/dist/src/core/remove.js +5 -3
- package/dist/src/core/uninstall.js +11 -1
- package/docs/USER_TEST_GUIDE.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.5.1 - 2026-07-21
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Remove disabled Maximum-library copies during package removal and complete
|
|
10
|
+
uninstall without inspecting or deleting unrelated skills that later occupy the
|
|
11
|
+
original agent path.
|
|
12
|
+
- Describe a disabled-only Maximum library as ready with nothing active, including
|
|
13
|
+
explicit active and disabled skill counts, instead of calling it a healthy active
|
|
14
|
+
installation.
|
|
15
|
+
- Name modified managed paths in blocked complete-uninstall previews so users can
|
|
16
|
+
review real drift before deciding whether to force removal.
|
|
17
|
+
|
|
18
|
+
## 0.5.0 - 2026-07-21
|
|
19
|
+
|
|
5
20
|
### Added
|
|
6
21
|
|
|
7
22
|
- Add the pinned MIT-licensed Obsidian Skills collection and recommend it only when an
|
package/MASTER_PLAN.md
CHANGED
|
@@ -270,6 +270,13 @@ merged `codex/relatable-readme-hero` remote branch remains safe to delete after
|
|
|
270
270
|
publish attempt. Every build now removes `dist` first on every platform, and the
|
|
271
271
|
package smoke test rejects removed dashboard/demo JavaScript if it ever leaks
|
|
272
272
|
back into the tarball. The failed OTP-gated attempt published nothing.
|
|
273
|
+
- [x] `P18-37 [TERRA]` Fix the founder-discovered disabled-library uninstall bug.
|
|
274
|
+
Removal now resolves each managed file to its disabled Maximum-library copy,
|
|
275
|
+
never an unmanaged skill that later occupies the original active path. A
|
|
276
|
+
regression test preserves the replacement bytes, the real founder state now
|
|
277
|
+
previews an unblocked removal of 29 packages/2,316 disabled records, and health
|
|
278
|
+
reports `library ready (nothing active)` with explicit counts. Release as
|
|
279
|
+
`0.5.1` before continuing the complete-uninstall acceptance step.
|
|
273
280
|
|
|
274
281
|
### Release 0.3 lifecycle hardening
|
|
275
282
|
|
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.1`; 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.1
|
|
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.1";
|
|
267
267
|
function durableSchedulerLauncher() {
|
|
268
268
|
return [
|
|
269
269
|
join(dirname(process.execPath), process.platform === "win32" ? "npx.cmd" : "npx"),
|
package/dist/src/core/health.js
CHANGED
|
@@ -100,18 +100,26 @@ export async function buildHealthReport(options = {}) {
|
|
|
100
100
|
fix: "Check connectivity and retry.",
|
|
101
101
|
});
|
|
102
102
|
const configured = state.installs.length > 0 || (state.mcpInstalls ?? []).length > 0;
|
|
103
|
+
const activeSkills = (state.activations ?? []).filter((entry) => entry.installationState === "installed" &&
|
|
104
|
+
entry.activationState === "active").length;
|
|
105
|
+
const disabledSkills = (state.activations ?? []).filter((entry) => entry.installationState === "installed" &&
|
|
106
|
+
entry.activationState === "disabled").length;
|
|
103
107
|
const status = findings.some((finding) => finding.level === "error")
|
|
104
108
|
? "unhealthy"
|
|
105
109
|
: !configured
|
|
106
110
|
? "not-configured"
|
|
107
111
|
: findings.some((finding) => finding.level === "warning")
|
|
108
112
|
? "attention"
|
|
109
|
-
:
|
|
113
|
+
: activeSkills === 0 && disabledSkills > 0
|
|
114
|
+
? "library-only"
|
|
115
|
+
: "healthy";
|
|
110
116
|
return {
|
|
111
117
|
status,
|
|
112
118
|
generatedAt: new Date().toISOString(),
|
|
113
119
|
agents,
|
|
114
120
|
installedPackages: state.installs.length,
|
|
121
|
+
activeSkills,
|
|
122
|
+
disabledSkills,
|
|
115
123
|
updatesChecked: Boolean(options.updates || options.checkUpdates),
|
|
116
124
|
updatesAvailable: available.length,
|
|
117
125
|
driftedFiles: drifted.length,
|
|
@@ -120,7 +128,7 @@ export async function buildHealthReport(options = {}) {
|
|
|
120
128
|
};
|
|
121
129
|
}
|
|
122
130
|
export function formatHealthReport(report) {
|
|
123
|
-
const icon = report.status === "not-configured"
|
|
131
|
+
const icon = report.status === "not-configured" || report.status === "library-only"
|
|
124
132
|
? "•"
|
|
125
133
|
: report.status === "healthy"
|
|
126
134
|
? "✓"
|
|
@@ -128,8 +136,8 @@ export function formatHealthReport(report) {
|
|
|
128
136
|
? "!"
|
|
129
137
|
: "✗";
|
|
130
138
|
const lines = [
|
|
131
|
-
`${icon} Loadout health: ${report.status === "not-configured" ? "not configured" : report.status}`,
|
|
132
|
-
`Packages: ${report.installedPackages}
|
|
139
|
+
`${icon} Loadout health: ${report.status === "not-configured" ? "not configured" : report.status === "library-only" ? "library ready (nothing active)" : report.status}`,
|
|
140
|
+
`Packages: ${report.installedPackages} managed; skills: ${report.activeSkills ?? 0} active, ${report.disabledSkills ?? 0} disabled; ${report.updatesChecked ? `${report.updatesAvailable} update(s)` : "updates not checked (use --updates)"}; ${report.driftedFiles} drifted file(s), ${report.driftedMcpServers} drifted MCP server(s)`,
|
|
133
141
|
];
|
|
134
142
|
for (const finding of report.findings)
|
|
135
143
|
lines.push(`${finding.level === "ok" ? "✓" : finding.level === "error" ? "✗" : finding.level === "warning" ? "!" : "•"} ${finding.message}${finding.fix ? ` ${finding.fix}` : ""}`);
|
package/dist/src/core/remove.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFile, rm } from "node:fs/promises";
|
|
|
3
3
|
import { forgetInstall, installStatePath, readInstallState } from "./state.js";
|
|
4
4
|
import { writeMcpConfigPlan } from "./mcp.js";
|
|
5
5
|
import { runMutationTransaction } from "./transaction.js";
|
|
6
|
+
import { managedFileReadPath } from "./active-set.js";
|
|
6
7
|
import { codexMcpServerFingerprint, removeCodexMcpServerBlock, writeCodexMcpConfigContent, } from "./codex-mcp.js";
|
|
7
8
|
export async function planRemove(packageId) {
|
|
8
9
|
const state = await readInstallState();
|
|
@@ -11,19 +12,20 @@ export async function planRemove(packageId) {
|
|
|
11
12
|
if (!record && !trackedMcp.length)
|
|
12
13
|
throw new Error(`Package is not managed by Loadout: ${packageId}`);
|
|
13
14
|
const files = await Promise.all((record?.files ?? []).map(async (file) => {
|
|
15
|
+
const managedPath = managedFileReadPath(packageId, file.path, state.activations ?? []);
|
|
14
16
|
try {
|
|
15
17
|
const digest = createHash("sha256")
|
|
16
|
-
.update(await readFile(
|
|
18
|
+
.update(await readFile(managedPath))
|
|
17
19
|
.digest("hex");
|
|
18
20
|
return {
|
|
19
|
-
path:
|
|
21
|
+
path: managedPath,
|
|
20
22
|
status: digest === file.sha256
|
|
21
23
|
? "unchanged"
|
|
22
24
|
: "modified",
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
catch {
|
|
26
|
-
return { path:
|
|
28
|
+
return { path: managedPath, status: "missing" };
|
|
27
29
|
}
|
|
28
30
|
}));
|
|
29
31
|
const modified = files.filter((file) => file.status === "modified");
|
|
@@ -113,6 +113,9 @@ export async function uninstallGlobalCli() {
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
export function formatUninstallPlan(plan) {
|
|
116
|
+
const modifiedPaths = plan.packages.flatMap((entry) => entry.files
|
|
117
|
+
.filter((file) => file.status === "modified")
|
|
118
|
+
.map((file) => file.path));
|
|
116
119
|
return [
|
|
117
120
|
"Complete Loadout uninstall preview",
|
|
118
121
|
"",
|
|
@@ -122,7 +125,14 @@ export function formatUninstallPlan(plan) {
|
|
|
122
125
|
`Daily jobs to remove: ${plan.schedulers.map((item) => item.job).join(", ") || "none"}`,
|
|
123
126
|
`State and cache: ${plan.stateHome}`,
|
|
124
127
|
...(plan.blocked
|
|
125
|
-
? [
|
|
128
|
+
? [
|
|
129
|
+
"",
|
|
130
|
+
"BLOCKED: managed files were changed outside Loadout.",
|
|
131
|
+
...modifiedPaths.slice(0, 10).map((path) => ` Modified: ${path}`),
|
|
132
|
+
...(modifiedPaths.length > 10
|
|
133
|
+
? [` …and ${modifiedPaths.length - 10} more`]
|
|
134
|
+
: []),
|
|
135
|
+
]
|
|
126
136
|
: []),
|
|
127
137
|
"",
|
|
128
138
|
...plan.warnings.map((warning) => `Warning: ${warning}`),
|
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.1` 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,
|