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.
Files changed (33) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +45 -50
  3. package/catalog/discovered.json +27154 -25927
  4. package/dist/src/commands/catalog-workflows.js +227 -9
  5. package/dist/src/commands/coordinate.js +148 -4
  6. package/dist/src/commands/coordination-discussions.js +24 -0
  7. package/dist/src/core/coordination/adapters/codex.js +25 -2
  8. package/dist/src/core/coordination/auto-contract.js +457 -0
  9. package/dist/src/core/coordination/daemon.js +6 -3
  10. package/dist/src/core/coordination/discussion-pipeline.js +313 -0
  11. package/dist/src/core/coordination/git-ownership.js +217 -0
  12. package/dist/src/core/coordination/lock.js +34 -4
  13. package/dist/src/core/coordination/quick-start.js +200 -0
  14. package/dist/src/core/coordination/retention.js +54 -2
  15. package/dist/src/core/delegation/handoff-bundle.js +253 -0
  16. package/dist/src/core/delegation/handoff-templates.js +222 -0
  17. package/dist/src/core/delegation/handoff-verification.js +117 -0
  18. package/dist/src/core/delegation/handoff.js +218 -26
  19. package/dist/src/core/install/snapshot.js +24 -5
  20. package/docs/DISCOVERED.md +250 -251
  21. package/docs/FEATURE_TEST_MATRIX.md +26 -11
  22. package/docs/LIVE_COLLABORATION.md +49 -0
  23. package/docs/REFERENCE.md +100 -0
  24. package/docs/USER_TEST_GUIDE.md +74 -1
  25. package/docs/evidence/coordination-provider-check-2026-09-05.md +33 -0
  26. package/docs/specs/HANDOFF_CONTEXT_BUNDLES.md +139 -0
  27. package/docs/specs/HANDOFF_VERIFICATION.md +83 -0
  28. package/docs/superpowers/plans/2026-09-04-handoff-context-bundles.md +109 -0
  29. package/docs/superpowers/plans/2026-09-04-handoff-verification.md +56 -0
  30. package/docs/superpowers/plans/2026-09-05-pre-release-hardening.md +175 -0
  31. package/docs/superpowers/plans/2026-09-05-public-readiness.md +20 -0
  32. package/package.json +3 -2
  33. 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({ path, existed: true, directory: true });
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)