loadout-ai 0.9.0 → 0.9.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 +61 -0
- package/README.md +45 -50
- package/catalog/discovered.json +27154 -25927
- package/dist/src/commands/catalog-workflows.js +227 -9
- package/dist/src/commands/coordinate.js +148 -4
- package/dist/src/commands/coordination-discussions.js +24 -0
- package/dist/src/core/coordination/adapters/codex.js +25 -2
- package/dist/src/core/coordination/auto-contract.js +457 -0
- package/dist/src/core/coordination/daemon.js +6 -3
- package/dist/src/core/coordination/discussion-pipeline.js +313 -0
- package/dist/src/core/coordination/git-ownership.js +217 -0
- package/dist/src/core/coordination/lock.js +34 -4
- package/dist/src/core/coordination/quick-start.js +200 -0
- package/dist/src/core/coordination/retention.js +54 -2
- package/dist/src/core/delegation/handoff-bundle.js +253 -0
- package/dist/src/core/delegation/handoff-templates.js +222 -0
- package/dist/src/core/delegation/handoff-verification.js +117 -0
- package/dist/src/core/delegation/handoff.js +218 -26
- package/dist/src/core/install/snapshot.js +24 -5
- package/docs/DISCOVERED.md +250 -251
- package/docs/FEATURE_TEST_MATRIX.md +26 -11
- package/docs/LIVE_COLLABORATION.md +49 -0
- package/docs/REFERENCE.md +100 -0
- package/docs/USER_TEST_GUIDE.md +74 -1
- package/docs/evidence/coordination-provider-check-2026-09-05.md +33 -0
- package/docs/specs/HANDOFF_CONTEXT_BUNDLES.md +139 -0
- package/docs/specs/HANDOFF_VERIFICATION.md +83 -0
- package/docs/superpowers/plans/2026-09-04-handoff-context-bundles.md +109 -0
- package/docs/superpowers/plans/2026-09-04-handoff-verification.md +56 -0
- package/docs/superpowers/plans/2026-09-05-pre-release-hardening.md +175 -0
- package/docs/superpowers/plans/2026-09-05-public-readiness.md +20 -0
- package/package.json +3 -2
- package/skills/loadout-handoff/SKILL.md +68 -15
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { readFile, writeFile, mkdir, readdir, rename, rm, lstat, } from "node:fs/promises";
|
|
2
|
+
import { readFile, writeFile, mkdir, readdir, rename, rm, lstat, chmod, } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, resolve, sep } from "node:path";
|
|
4
4
|
import { loadoutHome, ensureDirectory, userHome } from "../agents/paths.js";
|
|
5
5
|
export async function createSnapshot(paths, options = {}) {
|
|
@@ -36,12 +36,18 @@ export async function createSnapshot(paths, options = {}) {
|
|
|
36
36
|
existed: true,
|
|
37
37
|
content: (await readFile(path)).toString("base64"),
|
|
38
38
|
encoding: "base64",
|
|
39
|
+
mode: info.mode & 0o777,
|
|
39
40
|
});
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
if (!info.isDirectory())
|
|
43
44
|
throw new Error(`Refusing unsupported snapshot target: ${path}`);
|
|
44
|
-
snapshot.files.push({
|
|
45
|
+
snapshot.files.push({
|
|
46
|
+
path,
|
|
47
|
+
existed: true,
|
|
48
|
+
directory: true,
|
|
49
|
+
mode: info.mode & 0o777,
|
|
50
|
+
});
|
|
45
51
|
const entries = (await readdir(path, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name));
|
|
46
52
|
for (const entry of entries) {
|
|
47
53
|
const child = join(path, entry.name);
|
|
@@ -49,13 +55,16 @@ export async function createSnapshot(paths, options = {}) {
|
|
|
49
55
|
throw new Error(`Refusing to snapshot symlink: ${child}`);
|
|
50
56
|
if (entry.isDirectory())
|
|
51
57
|
await capture(child);
|
|
52
|
-
else if (entry.isFile())
|
|
58
|
+
else if (entry.isFile()) {
|
|
59
|
+
const childInfo = await lstat(child);
|
|
53
60
|
snapshot.files.push({
|
|
54
61
|
path: child,
|
|
55
62
|
existed: true,
|
|
56
63
|
content: (await readFile(child)).toString("base64"),
|
|
57
64
|
encoding: "base64",
|
|
65
|
+
mode: childInfo.mode & 0o777,
|
|
58
66
|
});
|
|
67
|
+
}
|
|
59
68
|
else
|
|
60
69
|
throw new Error(`Refusing unsupported snapshot target: ${child}`);
|
|
61
70
|
}
|
|
@@ -78,8 +87,11 @@ export async function restoreSnapshot(snapshot, options = {}) {
|
|
|
78
87
|
await rm(root, { recursive: true, force: true });
|
|
79
88
|
for (const directory of snapshot.files
|
|
80
89
|
.filter((file) => file.existed && file.directory)
|
|
81
|
-
.sort((a, b) => a.path.length - b.path.length))
|
|
90
|
+
.sort((a, b) => a.path.length - b.path.length)) {
|
|
82
91
|
await mkdir(directory.path, { recursive: true });
|
|
92
|
+
if (directory.mode !== undefined)
|
|
93
|
+
await chmod(directory.path, directory.mode);
|
|
94
|
+
}
|
|
83
95
|
for (const file of snapshot.files) {
|
|
84
96
|
if (!file.existed || file.directory)
|
|
85
97
|
continue;
|
|
@@ -87,6 +99,8 @@ export async function restoreSnapshot(snapshot, options = {}) {
|
|
|
87
99
|
await writeFile(file.path, file.encoding === "base64"
|
|
88
100
|
? Buffer.from(file.content ?? "", "base64")
|
|
89
101
|
: (file.content ?? ""));
|
|
102
|
+
if (file.mode !== undefined)
|
|
103
|
+
await chmod(file.path, file.mode);
|
|
90
104
|
}
|
|
91
105
|
}
|
|
92
106
|
/** Attach the committed state used to make later user-requested rollback safe. */
|
|
@@ -240,7 +254,12 @@ function validateSnapshotFiles(files, roots, label) {
|
|
|
240
254
|
typeof file.existed !== "boolean" ||
|
|
241
255
|
(file.directory !== undefined && typeof file.directory !== "boolean") ||
|
|
242
256
|
(file.content !== undefined && typeof file.content !== "string") ||
|
|
243
|
-
(file.encoding !== undefined && file.encoding !== "base64")
|
|
257
|
+
(file.encoding !== undefined && file.encoding !== "base64") ||
|
|
258
|
+
(file.mode !== undefined &&
|
|
259
|
+
(typeof file.mode !== "number" ||
|
|
260
|
+
!Number.isInteger(file.mode) ||
|
|
261
|
+
file.mode < 0 ||
|
|
262
|
+
file.mode > 0o7777)))
|
|
244
263
|
throw new Error(`${label} file ${index} is invalid`);
|
|
245
264
|
const filePath = file.path;
|
|
246
265
|
if (resolve(filePath) !== filePath)
|