@aperant/framework 0.10.0 → 0.12.0

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 (199) hide show
  1. package/CHANGELOG.md +236 -1
  2. package/agents/apt-planner.md +15 -0
  3. package/agents/apt-verifier.md +15 -10
  4. package/bin/apt-proof-video.mjs +256 -76
  5. package/dist/cli/commands/apply-recipe.d.mts +6 -0
  6. package/dist/cli/commands/apply-recipe.d.mts.map +1 -0
  7. package/dist/cli/commands/apply-recipe.mjs +98 -0
  8. package/dist/cli/commands/apply-recipe.mjs.map +1 -0
  9. package/dist/cli/commands/catch-up.d.mts +9 -0
  10. package/dist/cli/commands/catch-up.d.mts.map +1 -0
  11. package/dist/cli/commands/catch-up.mjs +64 -0
  12. package/dist/cli/commands/catch-up.mjs.map +1 -0
  13. package/dist/cli/commands/mobile-prepare.d.mts +53 -0
  14. package/dist/cli/commands/mobile-prepare.d.mts.map +1 -0
  15. package/dist/cli/commands/mobile-prepare.mjs +139 -0
  16. package/dist/cli/commands/mobile-prepare.mjs.map +1 -0
  17. package/dist/cli/commands/pr-review.d.mts +16 -4
  18. package/dist/cli/commands/pr-review.d.mts.map +1 -1
  19. package/dist/cli/commands/pr-review.mjs +30 -11
  20. package/dist/cli/commands/pr-review.mjs.map +1 -1
  21. package/dist/cli/commands/recipe-diff.d.mts +6 -0
  22. package/dist/cli/commands/recipe-diff.d.mts.map +1 -0
  23. package/dist/cli/commands/recipe-diff.mjs +75 -0
  24. package/dist/cli/commands/recipe-diff.mjs.map +1 -0
  25. package/dist/cli/commands/roadmap.d.mts.map +1 -1
  26. package/dist/cli/commands/roadmap.mjs +53 -0
  27. package/dist/cli/commands/roadmap.mjs.map +1 -1
  28. package/dist/cli/commands/route.d.mts.map +1 -1
  29. package/dist/cli/commands/route.mjs +92 -2
  30. package/dist/cli/commands/route.mjs.map +1 -1
  31. package/dist/cli/commands/verify-sandbox.d.mts +6 -0
  32. package/dist/cli/commands/verify-sandbox.d.mts.map +1 -0
  33. package/dist/cli/commands/verify-sandbox.mjs +200 -0
  34. package/dist/cli/commands/verify-sandbox.mjs.map +1 -0
  35. package/dist/cli/config/share-policy.d.mts.map +1 -1
  36. package/dist/cli/config/share-policy.mjs +1 -0
  37. package/dist/cli/config/share-policy.mjs.map +1 -1
  38. package/dist/cli/config/upgrade-gitignore.d.mts.map +1 -1
  39. package/dist/cli/config/upgrade-gitignore.mjs +1 -0
  40. package/dist/cli/config/upgrade-gitignore.mjs.map +1 -1
  41. package/dist/cli/consistency/parse-qa.d.mts.map +1 -1
  42. package/dist/cli/consistency/parse-qa.mjs +18 -4
  43. package/dist/cli/consistency/parse-qa.mjs.map +1 -1
  44. package/dist/cli/consistency/rules/r5-verdict-consistency.d.mts.map +1 -1
  45. package/dist/cli/consistency/rules/r5-verdict-consistency.mjs +11 -10
  46. package/dist/cli/consistency/rules/r5-verdict-consistency.mjs.map +1 -1
  47. package/dist/cli/coordination/catch-up.d.mts +26 -0
  48. package/dist/cli/coordination/catch-up.d.mts.map +1 -0
  49. package/dist/cli/coordination/catch-up.mjs +239 -0
  50. package/dist/cli/coordination/catch-up.mjs.map +1 -0
  51. package/dist/cli/coordination/last-seen.d.mts +45 -0
  52. package/dist/cli/coordination/last-seen.d.mts.map +1 -0
  53. package/dist/cli/coordination/last-seen.mjs +128 -0
  54. package/dist/cli/coordination/last-seen.mjs.map +1 -0
  55. package/dist/cli/coordination/store.d.mts +15 -0
  56. package/dist/cli/coordination/store.d.mts.map +1 -1
  57. package/dist/cli/coordination/store.mjs +16 -0
  58. package/dist/cli/coordination/store.mjs.map +1 -1
  59. package/dist/cli/coverage-check/user-outcomes.d.mts +7 -2
  60. package/dist/cli/coverage-check/user-outcomes.d.mts.map +1 -1
  61. package/dist/cli/coverage-check/user-outcomes.mjs +50 -17
  62. package/dist/cli/coverage-check/user-outcomes.mjs.map +1 -1
  63. package/dist/cli/dispatch.d.mts.map +1 -1
  64. package/dist/cli/dispatch.mjs +30 -0
  65. package/dist/cli/dispatch.mjs.map +1 -1
  66. package/dist/cli/gate/gates/verify-approved.d.mts +0 -48
  67. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  68. package/dist/cli/gate/gates/verify-approved.mjs +23 -80
  69. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  70. package/dist/cli/git/default-branch.d.mts +51 -0
  71. package/dist/cli/git/default-branch.d.mts.map +1 -0
  72. package/dist/cli/git/default-branch.mjs +234 -0
  73. package/dist/cli/git/default-branch.mjs.map +1 -0
  74. package/dist/cli/git/identity.d.mts +3 -5
  75. package/dist/cli/git/identity.d.mts.map +1 -1
  76. package/dist/cli/git/identity.mjs +10 -4
  77. package/dist/cli/git/identity.mjs.map +1 -1
  78. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  79. package/dist/cli/install/legacy-paths.mjs +2 -0
  80. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  81. package/dist/cli/install/mcp-server-specs.d.mts +6 -5
  82. package/dist/cli/install/mcp-server-specs.d.mts.map +1 -1
  83. package/dist/cli/install/mcp-server-specs.mjs +22 -4
  84. package/dist/cli/install/mcp-server-specs.mjs.map +1 -1
  85. package/dist/cli/roadmap/backend-adapter.d.mts +43 -0
  86. package/dist/cli/roadmap/backend-adapter.d.mts.map +1 -0
  87. package/dist/cli/roadmap/backend-adapter.mjs +222 -0
  88. package/dist/cli/roadmap/backend-adapter.mjs.map +1 -0
  89. package/dist/cli/util/runtime-capabilities.d.mts +7 -0
  90. package/dist/cli/util/runtime-capabilities.d.mts.map +1 -1
  91. package/dist/cli/util/runtime-capabilities.mjs +65 -0
  92. package/dist/cli/util/runtime-capabilities.mjs.map +1 -1
  93. package/dist/cli/verify-proof/manifest-validator.mjs +13 -0
  94. package/dist/cli/verify-proof/manifest-validator.mjs.map +1 -1
  95. package/dist/cli/verify-proof/resolver.d.mts +10 -7
  96. package/dist/cli/verify-proof/resolver.d.mts.map +1 -1
  97. package/dist/cli/verify-proof/resolver.mjs +28 -7
  98. package/dist/cli/verify-proof/resolver.mjs.map +1 -1
  99. package/dist/cli/verify-proof/sandbox/apply.d.mts +64 -0
  100. package/dist/cli/verify-proof/sandbox/apply.d.mts.map +1 -0
  101. package/dist/cli/verify-proof/sandbox/apply.mjs +379 -0
  102. package/dist/cli/verify-proof/sandbox/apply.mjs.map +1 -0
  103. package/dist/cli/verify-proof/sandbox/gate-predicate.d.mts +63 -0
  104. package/dist/cli/verify-proof/sandbox/gate-predicate.d.mts.map +1 -0
  105. package/dist/cli/verify-proof/sandbox/gate-predicate.mjs +115 -0
  106. package/dist/cli/verify-proof/sandbox/gate-predicate.mjs.map +1 -0
  107. package/dist/cli/verify-proof/sandbox/pattern-matcher.d.mts +57 -0
  108. package/dist/cli/verify-proof/sandbox/pattern-matcher.d.mts.map +1 -0
  109. package/dist/cli/verify-proof/sandbox/pattern-matcher.mjs +124 -0
  110. package/dist/cli/verify-proof/sandbox/pattern-matcher.mjs.map +1 -0
  111. package/dist/cli/verify-proof/sandbox/recipe-diff.d.mts +28 -0
  112. package/dist/cli/verify-proof/sandbox/recipe-diff.d.mts.map +1 -0
  113. package/dist/cli/verify-proof/sandbox/recipe-diff.mjs +190 -0
  114. package/dist/cli/verify-proof/sandbox/recipe-diff.mjs.map +1 -0
  115. package/dist/cli/verify-proof/sandbox/sandbox-config.d.mts +25 -0
  116. package/dist/cli/verify-proof/sandbox/sandbox-config.d.mts.map +1 -0
  117. package/dist/cli/verify-proof/sandbox/sandbox-config.mjs +85 -0
  118. package/dist/cli/verify-proof/sandbox/sandbox-config.mjs.map +1 -0
  119. package/dist/cli/verify-proof/sandbox/self-test.d.mts +39 -0
  120. package/dist/cli/verify-proof/sandbox/self-test.d.mts.map +1 -0
  121. package/dist/cli/verify-proof/sandbox/self-test.mjs +248 -0
  122. package/dist/cli/verify-proof/sandbox/self-test.mjs.map +1 -0
  123. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  124. package/dist/plugin/agents/apt-planner.md +15 -0
  125. package/dist/plugin/agents/apt-verifier.md +15 -10
  126. package/dist/plugin/skills/apt/SKILL.md +29 -0
  127. package/dist/plugin/skills/apt-catch-up/SKILL.md +79 -0
  128. package/dist/plugin/skills/apt-plan/SKILL.md +23 -0
  129. package/dist/plugin/skills/apt-pr-review/SKILL.md +5 -4
  130. package/dist/plugin/skills/apt-run/SKILL.md +1 -1
  131. package/dist/plugin/skills/apt-setup/SKILL.md +132 -0
  132. package/dist/plugin/skills/apt-verify/SKILL.md +20 -15
  133. package/dist/plugin/skills/apt-verify-proof/SKILL.md +146 -11
  134. package/dist/proof-report.d.ts.map +1 -1
  135. package/dist/proof-report.js +4 -1
  136. package/dist/proof-report.js.map +1 -1
  137. package/dist/types/config.d.ts +42 -4
  138. package/dist/types/config.d.ts.map +1 -1
  139. package/dist/types/qa-scoring.d.ts +45 -9
  140. package/dist/types/qa-scoring.d.ts.map +1 -1
  141. package/dist/types/qa-scoring.js +34 -13
  142. package/dist/types/qa-scoring.js.map +1 -1
  143. package/dist/types/state.d.ts +7 -2
  144. package/dist/types/state.d.ts.map +1 -1
  145. package/dist/types/task-record.d.ts +10 -2
  146. package/dist/types/task-record.d.ts.map +1 -1
  147. package/drivers/mobile/README.md +40 -0
  148. package/drivers/mobile/driver.mjs +106 -0
  149. package/drivers/mobile/manifest.json +49 -0
  150. package/package.json +1 -1
  151. package/prompts/inbox-clarification.md +11 -0
  152. package/prompts/inbox-triage.md +20 -0
  153. package/prompts/qa_orchestrator_agentic.md +18 -17
  154. package/prompts/qa_reviewer.md +17 -15
  155. package/skills/apt/SKILL.md +29 -0
  156. package/skills/apt-catch-up/SKILL.md +79 -0
  157. package/skills/apt-plan/SKILL.md +23 -0
  158. package/skills/apt-pr-review/SKILL.md +5 -4
  159. package/skills/apt-run/SKILL.md +1 -1
  160. package/skills/apt-setup/SKILL.md +132 -0
  161. package/skills/apt-verifier.md +7 -4
  162. package/skills/apt-verify/SKILL.md +20 -15
  163. package/skills/apt-verify-proof/SKILL.md +146 -11
  164. package/src/cli/commands/apply-recipe.mjs +105 -0
  165. package/src/cli/commands/catch-up.mjs +67 -0
  166. package/src/cli/commands/mobile-prepare.mjs +151 -0
  167. package/src/cli/commands/pr-review.mjs +32 -11
  168. package/src/cli/commands/recipe-diff.mjs +81 -0
  169. package/src/cli/commands/roadmap.mjs +54 -0
  170. package/src/cli/commands/route.mjs +92 -1
  171. package/src/cli/commands/verify-sandbox.mjs +231 -0
  172. package/src/cli/config/share-policy.mjs +1 -0
  173. package/src/cli/config/upgrade-gitignore.mjs +1 -0
  174. package/src/cli/consistency/parse-qa.mjs +20 -4
  175. package/src/cli/consistency/rules/r5-verdict-consistency.mjs +11 -13
  176. package/src/cli/coordination/catch-up.mjs +231 -0
  177. package/src/cli/coordination/last-seen.mjs +131 -0
  178. package/src/cli/coordination/store.mjs +18 -0
  179. package/src/cli/coverage-check/user-outcomes.mjs +52 -17
  180. package/src/cli/dispatch.mjs +29 -0
  181. package/src/cli/gate/gates/verify-approved.mjs +22 -81
  182. package/src/cli/git/default-branch.mjs +250 -0
  183. package/src/cli/git/identity.mjs +9 -3
  184. package/src/cli/install/legacy-paths.mjs +2 -0
  185. package/src/cli/install/mcp-server-specs.mjs +24 -4
  186. package/src/cli/roadmap/backend-adapter.mjs +231 -0
  187. package/src/cli/util/runtime-capabilities.mjs +67 -0
  188. package/src/cli/verify-proof/manifest-validator.mjs +15 -0
  189. package/src/cli/verify-proof/resolver.mjs +27 -7
  190. package/src/cli/verify-proof/sandbox/apply.mjs +401 -0
  191. package/src/cli/verify-proof/sandbox/gate-predicate.mjs +126 -0
  192. package/src/cli/verify-proof/sandbox/pattern-matcher.mjs +127 -0
  193. package/src/cli/verify-proof/sandbox/recipe-diff.mjs +208 -0
  194. package/src/cli/verify-proof/sandbox/sandbox-config.mjs +82 -0
  195. package/src/cli/verify-proof/sandbox/sandbox-schema.json +33 -0
  196. package/src/cli/verify-proof/sandbox/self-test.mjs +265 -0
  197. package/templates/config.json +21 -1
  198. package/templates/proof-verification.md +27 -8
  199. package/workflows/verify-proof.md +376 -302
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @typedef {import('../../util/runtime-capabilities.mjs').RuntimeCapabilities} RuntimeCapabilities
3
+ */
4
+ /**
5
+ * @typedef {Object} SandboxRecipe
6
+ * @property {string} recipeId
7
+ * @property {string} recipeVersion
8
+ * @property {string} name
9
+ * @property {string} description
10
+ * @property {{capabilities: string[], deps: string[]}} match
11
+ * @property {{logPattern: string, timeoutMs: number}} readiness
12
+ * @property {Array<object>} changes
13
+ * @property {string[]} compatibleWithApplied
14
+ */
15
+ /**
16
+ * @typedef {Object} MatchResult
17
+ * @property {string|null} recipeId
18
+ * @property {string} reason
19
+ */
20
+ /**
21
+ * @param {RuntimeCapabilities} runtimeCaps
22
+ * @param {Record<string, unknown> | null} packageJson
23
+ * @param {SandboxRecipe[]} recipes
24
+ * @returns {MatchResult}
25
+ */
26
+ export function matchPattern(runtimeCaps: RuntimeCapabilities, packageJson: Record<string, unknown> | null, recipes: SandboxRecipe[]): MatchResult;
27
+ /**
28
+ * Convenience overload — load the project's package.json from disk.
29
+ *
30
+ * @param {string} projectDir
31
+ * @param {RuntimeCapabilities} runtimeCaps
32
+ * @param {SandboxRecipe[]} recipes
33
+ * @returns {MatchResult}
34
+ */
35
+ export function matchPatternForProject(projectDir: string, runtimeCaps: RuntimeCapabilities, recipes: SandboxRecipe[]): MatchResult;
36
+ export type RuntimeCapabilities = import("../../util/runtime-capabilities.mjs").RuntimeCapabilities;
37
+ export type SandboxRecipe = {
38
+ recipeId: string;
39
+ recipeVersion: string;
40
+ name: string;
41
+ description: string;
42
+ match: {
43
+ capabilities: string[];
44
+ deps: string[];
45
+ };
46
+ readiness: {
47
+ logPattern: string;
48
+ timeoutMs: number;
49
+ };
50
+ changes: Array<object>;
51
+ compatibleWithApplied: string[];
52
+ };
53
+ export type MatchResult = {
54
+ recipeId: string | null;
55
+ reason: string;
56
+ };
57
+ //# sourceMappingURL=pattern-matcher.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-matcher.d.mts","sourceRoot":"","sources":["../../../../src/cli/verify-proof/sandbox/pattern-matcher.mjs"],"names":[],"mappings":"AAuBA;;GAEG;AAEH;;;;;;;;;;GAUG;AAEH;;;;GAIG;AAEH;;;;;GAKG;AACH,0CALW,mBAAmB,eACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,WAC9B,aAAa,EAAE,GACb,WAAW,CA4BvB;AAED;;;;;;;GAOG;AACH,mDALW,MAAM,eACN,mBAAmB,WACnB,aAAa,EAAE,GACb,WAAW,CAavB;kCA1EY,OAAO,qCAAqC,EAAE,mBAAmB;;cAKhE,MAAM;mBACN,MAAM;UACN,MAAM;iBACN,MAAM;WACN;QAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAC;eACxC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC;aACvC,KAAK,CAAC,MAAM,CAAC;2BACb,MAAM,EAAE;;;cAKR,MAAM,GAAC,IAAI;YACX,MAAM"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * pattern-matcher.mjs — sandbox-recipe pattern matcher.
3
+ *
4
+ * Walks the registered recipes' `match.capabilities[]` + `match.deps[]`
5
+ * predicates against the loaded runtime capabilities + package.json deps,
6
+ * and returns the FIRST matching recipe (or null with a human reason).
7
+ *
8
+ * Two-arg shape:
9
+ * - `match.capabilities[]` — every entry must be `true` on the
10
+ * RuntimeCapabilities record (AND semantics — all required).
11
+ * - `match.deps[]` — at least one entry must appear in the merged
12
+ * dependencies + devDependencies of the project's root package.json
13
+ * (OR semantics — any-of).
14
+ *
15
+ * This split lets a recipe say "I need is_electron AND one of
16
+ * [@libsql/client, better-sqlite3]" without enumerating every combination.
17
+ *
18
+ * See: spec ID-04 (v1 ships ONE recipe — electron-libsql), AC5.
19
+ */
20
+ import { existsSync, readFileSync } from 'node:fs';
21
+ import { join, resolve } from 'node:path';
22
+ /**
23
+ * @typedef {import('../../util/runtime-capabilities.mjs').RuntimeCapabilities} RuntimeCapabilities
24
+ */
25
+ /**
26
+ * @typedef {Object} SandboxRecipe
27
+ * @property {string} recipeId
28
+ * @property {string} recipeVersion
29
+ * @property {string} name
30
+ * @property {string} description
31
+ * @property {{capabilities: string[], deps: string[]}} match
32
+ * @property {{logPattern: string, timeoutMs: number}} readiness
33
+ * @property {Array<object>} changes
34
+ * @property {string[]} compatibleWithApplied
35
+ */
36
+ /**
37
+ * @typedef {Object} MatchResult
38
+ * @property {string|null} recipeId
39
+ * @property {string} reason
40
+ */
41
+ /**
42
+ * @param {RuntimeCapabilities} runtimeCaps
43
+ * @param {Record<string, unknown> | null} packageJson
44
+ * @param {SandboxRecipe[]} recipes
45
+ * @returns {MatchResult}
46
+ */
47
+ export function matchPattern(runtimeCaps, packageJson, recipes) {
48
+ if (!Array.isArray(recipes) || recipes.length === 0) {
49
+ return { recipeId: null, reason: 'no recipes registered' };
50
+ }
51
+ const deps = mergedDeps(packageJson);
52
+ for (const recipe of recipes) {
53
+ const reqCaps = recipe.match?.capabilities ?? [];
54
+ const anyDeps = recipe.match?.deps ?? [];
55
+ const capsOk = reqCaps.every((cap) => runtimeCaps && runtimeCaps[cap] === true);
56
+ if (!capsOk)
57
+ continue;
58
+ // deps[] is any-of — recipe matches if at least one declared dep is in
59
+ // the project. An empty deps[] means "no dep constraint" (cap-only match).
60
+ const depsOk = anyDeps.length === 0 || anyDeps.some((d) => deps.has(d.toLowerCase()));
61
+ if (!depsOk)
62
+ continue;
63
+ return { recipeId: recipe.recipeId, reason: 'matched' };
64
+ }
65
+ return {
66
+ recipeId: null,
67
+ reason: `no recipe available for runtime: ${describeRuntime(runtimeCaps)}`,
68
+ };
69
+ }
70
+ /**
71
+ * Convenience overload — load the project's package.json from disk.
72
+ *
73
+ * @param {string} projectDir
74
+ * @param {RuntimeCapabilities} runtimeCaps
75
+ * @param {SandboxRecipe[]} recipes
76
+ * @returns {MatchResult}
77
+ */
78
+ export function matchPatternForProject(projectDir, runtimeCaps, recipes) {
79
+ const pkgPath = join(resolve(projectDir), 'package.json');
80
+ let pkg = null;
81
+ if (existsSync(pkgPath)) {
82
+ try {
83
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
84
+ }
85
+ catch {
86
+ /* fall through with pkg=null */
87
+ }
88
+ }
89
+ return matchPattern(runtimeCaps, pkg, recipes);
90
+ }
91
+ /**
92
+ * Build a Set of lowercased package names from dependencies + devDependencies.
93
+ *
94
+ * @param {Record<string, unknown> | null} packageJson
95
+ */
96
+ function mergedDeps(packageJson) {
97
+ const out = new Set();
98
+ if (packageJson === null || typeof packageJson !== 'object')
99
+ return out;
100
+ const d = /** @type {Record<string, unknown>} */ (packageJson.dependencies);
101
+ const dd = /** @type {Record<string, unknown>} */ (packageJson.devDependencies);
102
+ if (d && typeof d === 'object')
103
+ for (const k of Object.keys(d))
104
+ out.add(k.toLowerCase());
105
+ if (dd && typeof dd === 'object')
106
+ for (const k of Object.keys(dd))
107
+ out.add(k.toLowerCase());
108
+ return out;
109
+ }
110
+ /**
111
+ * One-line description of the detected runtime shape — used in the
112
+ * no-match reason so an adopter sees what the matcher saw.
113
+ *
114
+ * @param {RuntimeCapabilities} runtimeCaps
115
+ */
116
+ function describeRuntime(runtimeCaps) {
117
+ if (!runtimeCaps || typeof runtimeCaps !== 'object')
118
+ return 'unknown';
119
+ const active = Object.keys(runtimeCaps).filter((k) => runtimeCaps[k] === true);
120
+ if (active.length === 0)
121
+ return 'no capabilities detected';
122
+ return active.join(', ');
123
+ }
124
+ //# sourceMappingURL=pattern-matcher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-matcher.mjs","sourceRoot":"","sources":["../../../../src/cli/verify-proof/sandbox/pattern-matcher.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @param {{projectDir: string, recipeId: string, recipes: object[]}} args
3
+ * @returns {{
4
+ * format: 'markdown',
5
+ * body: string,
6
+ * changes: Array<{path: string, kind: string, summary: string}>,
7
+ * envelope: {status: 'ok'|'error', recipe_version: string|null, compatible: boolean}
8
+ * }}
9
+ */
10
+ export function generateRecipeDiff({ projectDir, recipeId, recipes }: {
11
+ projectDir: string;
12
+ recipeId: string;
13
+ recipes: object[];
14
+ }): {
15
+ format: "markdown";
16
+ body: string;
17
+ changes: Array<{
18
+ path: string;
19
+ kind: string;
20
+ summary: string;
21
+ }>;
22
+ envelope: {
23
+ status: "ok" | "error";
24
+ recipe_version: string | null;
25
+ compatible: boolean;
26
+ };
27
+ };
28
+ //# sourceMappingURL=recipe-diff.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-diff.d.mts","sourceRoot":"","sources":["../../../../src/cli/verify-proof/sandbox/recipe-diff.mjs"],"names":[],"mappings":"AA8CA;;;;;;;;GAQG;AACH,sEARW;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAC,GACvD;IACR,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAC9D,QAAQ,EAAE;QAAC,MAAM,EAAE,IAAI,GAAC,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,GAAC,IAAI,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAC,CAAA;CACnF,CAuDH"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * recipe-diff.mjs — read-only Markdown diff generator for a recipe.
3
+ *
4
+ * Loads the recipe, walks `changes[]`, and emits a Markdown report
5
+ * enumerating every proposed write WITH `--- before` / `+++ after`
6
+ * blocks. The generator is pure read — it never writes to the adopter's
7
+ * tree (ID-02). The companion `apply-recipe` command is the only
8
+ * writer.
9
+ *
10
+ * Public contract:
11
+ * generateRecipeDiff({projectDir, recipeId, recipes}) → {
12
+ * format: 'markdown',
13
+ * body: string,
14
+ * changes: [{path, kind, summary}],
15
+ * envelope: {status, recipe_version, compatible}
16
+ * }
17
+ *
18
+ * The Markdown is user-facing and load-bearing — drift = golden-snapshot
19
+ * test failure by design (subtask 2.1 / TD-03).
20
+ */
21
+ import { existsSync, readFileSync } from 'node:fs';
22
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
23
+ import { fileURLToPath } from 'node:url';
24
+ const __dirname = dirname(fileURLToPath(import.meta.url));
25
+ /**
26
+ * Defense-in-depth path-under-root check. Returns the validated absolute
27
+ * path or null when the candidate escapes the root. The diff generator
28
+ * surfaces a "template not found" envelope rather than throwing (read-
29
+ * only generator stays non-fatal).
30
+ *
31
+ * @param {string} rootAbs
32
+ * @param {string} relPath
33
+ * @returns {string|null}
34
+ */
35
+ function safeJoinUnderRoot(rootAbs, relPath) {
36
+ if (typeof relPath !== 'string' || relPath.length === 0)
37
+ return null;
38
+ if (isAbsolute(relPath))
39
+ return null;
40
+ const candidate = resolve(rootAbs, relPath);
41
+ const rel = relative(rootAbs, candidate);
42
+ if (rel.startsWith('..') || isAbsolute(rel))
43
+ return null;
44
+ return candidate;
45
+ }
46
+ /**
47
+ * @param {{projectDir: string, recipeId: string, recipes: object[]}} args
48
+ * @returns {{
49
+ * format: 'markdown',
50
+ * body: string,
51
+ * changes: Array<{path: string, kind: string, summary: string}>,
52
+ * envelope: {status: 'ok'|'error', recipe_version: string|null, compatible: boolean}
53
+ * }}
54
+ */
55
+ export function generateRecipeDiff({ projectDir, recipeId, recipes }) {
56
+ const recipe = (recipes ?? []).find((r) => r && r.recipeId === recipeId);
57
+ if (!recipe) {
58
+ return {
59
+ format: 'markdown',
60
+ body: `# No recipe available\n\nrecipeId \`${recipeId}\` is not in the registered recipes list.\n`,
61
+ changes: [],
62
+ envelope: { status: 'error', recipe_version: null, compatible: false },
63
+ };
64
+ }
65
+ const adopterRoot = resolve(projectDir);
66
+ const changeRows = [];
67
+ const sections = [];
68
+ for (const change of recipe.changes ?? []) {
69
+ // Defense-in-depth: a malicious recipe could otherwise read
70
+ // arbitrary host files via `change.path: '../../etc/passwd'`. The
71
+ // diff generator is read-only but the read CONTENT lands in the
72
+ // emitted Markdown — refuse to enumerate paths outside root.
73
+ const targetAbs = safeJoinUnderRoot(adopterRoot, change.path);
74
+ if (targetAbs === null) {
75
+ sections.push(`## ${change.path}\n\n_(rejected: change.path escapes project root — recipe defect)_\n`);
76
+ changeRows.push({
77
+ path: change.path,
78
+ kind: change.kind,
79
+ summary: 'rejected (path escapes root)',
80
+ });
81
+ continue;
82
+ }
83
+ const exists = existsSync(targetAbs);
84
+ const before = exists ? safeReadFile(targetAbs) : '';
85
+ const after = renderAfter(change, recipe, adopterRoot);
86
+ const summary = renderChangeSummary(change, exists);
87
+ changeRows.push({ path: change.path, kind: change.kind, summary });
88
+ sections.push(renderChangeSection(change, before, after, exists));
89
+ }
90
+ const body = renderBody(recipe, sections);
91
+ return {
92
+ format: 'markdown',
93
+ body,
94
+ changes: changeRows,
95
+ envelope: {
96
+ status: 'ok',
97
+ recipe_version: recipe.recipeVersion ?? null,
98
+ compatible: true,
99
+ },
100
+ };
101
+ }
102
+ function renderBody(recipe, sections) {
103
+ const lines = [];
104
+ lines.push(`# Recipe diff: ${recipe.name ?? recipe.recipeId}`);
105
+ lines.push('');
106
+ lines.push(`**Recipe:** \`${recipe.recipeId}\` v${recipe.recipeVersion} ` +
107
+ `(compatibleWithApplied: ${JSON.stringify(recipe.compatibleWithApplied ?? [])})`);
108
+ lines.push('');
109
+ lines.push(recipe.description ?? '');
110
+ lines.push('');
111
+ lines.push(`This recipe proposes **${(recipe.changes ?? []).length}** changes.`);
112
+ lines.push('No file is written until you run `apt-tools apply-recipe . --confirm` ' +
113
+ 'and accept each change individually.');
114
+ lines.push('');
115
+ for (const section of sections) {
116
+ lines.push(section);
117
+ lines.push('');
118
+ }
119
+ return lines.join('\n').replace(/\n{3,}/g, '\n\n');
120
+ }
121
+ function renderChangeSummary(change, exists) {
122
+ const verb = change.kind === 'file-create' ? 'create' : change.kind === 'json-merge' ? 'merge into' : 'edit';
123
+ const target = exists ? change.path : `${change.path} (new file)`;
124
+ return `${verb} ${target}`;
125
+ }
126
+ function renderChangeSection(change, before, after, exists) {
127
+ const lines = [];
128
+ lines.push(`## ${change.path}`);
129
+ lines.push('');
130
+ lines.push(`- kind: \`${change.kind}\``, `- marker: \`${change.marker ?? '(none)'}\``, `- target ${exists ? 'exists' : 'does not exist'} on disk`);
131
+ lines.push('');
132
+ lines.push('```diff');
133
+ const beforeBlock = before === '' ? '(empty / file does not exist)' : before;
134
+ for (const line of beforeBlock.split('\n'))
135
+ lines.push(`--- ${line}`);
136
+ lines.push('');
137
+ for (const line of after.split('\n'))
138
+ lines.push(`+++ ${line}`);
139
+ lines.push('```');
140
+ return lines.join('\n');
141
+ }
142
+ /**
143
+ * Render the "after" content per change kind. For json-merge we stringify
144
+ * the patch payload; for text-insert / file-create we load the template
145
+ * from the recipe directory (when present).
146
+ */
147
+ function renderAfter(change, recipe, adopterRoot) {
148
+ if (change.kind === 'json-merge') {
149
+ return JSON.stringify(change.after ?? {}, null, 2);
150
+ }
151
+ if (change.template) {
152
+ const tplPath = locateRecipeTemplate(recipe, change.template, adopterRoot);
153
+ if (tplPath && existsSync(tplPath)) {
154
+ return safeReadFile(tplPath);
155
+ }
156
+ return `(template not found on disk: ${change.template})`;
157
+ }
158
+ if (typeof change.after === 'string')
159
+ return change.after;
160
+ return '';
161
+ }
162
+ /**
163
+ * Resolve a recipe template path relative to the framework's recipes/
164
+ * directory. The framework ships recipes at packages/framework/recipes/
165
+ * <id>/templates/. We compute the directory from this module's location
166
+ * so the resolver works regardless of cwd.
167
+ */
168
+ function locateRecipeTemplate(recipe, templateName, _adopterRoot) {
169
+ // __dirname → packages/framework/src/cli/verify-proof/sandbox/
170
+ // → up four levels → packages/framework/
171
+ // Defense-in-depth: reject `../` escapes in templateName AND validate
172
+ // recipeId so a crafted id (e.g. '../../evil') cannot shift the anchor
173
+ // outside the framework root. discoverBundledRecipes() skips schema
174
+ // validation, so the ^[a-z][a-z0-9_-]*$ pattern may not have been enforced.
175
+ if (typeof recipe.recipeId !== 'string' || !/^[a-z][a-z0-9_-]*$/.test(recipe.recipeId)) {
176
+ return null;
177
+ }
178
+ const frameworkRoot = resolve(__dirname, '..', '..', '..', '..');
179
+ const templatesRoot = join(frameworkRoot, 'recipes', recipe.recipeId, 'templates');
180
+ return safeJoinUnderRoot(templatesRoot, templateName);
181
+ }
182
+ function safeReadFile(path) {
183
+ try {
184
+ return readFileSync(path, 'utf-8');
185
+ }
186
+ catch {
187
+ return '';
188
+ }
189
+ }
190
+ //# sourceMappingURL=recipe-diff.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-diff.mjs","sourceRoot":"","sources":["../../../../src/cli/verify-proof/sandbox/recipe-diff.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Resolve the framework's recipes directory at runtime. Works whether
3
+ * the module is loaded from source (packages/framework/src/...) or from
4
+ * the installed dist (.aperant/deps/node_modules/@aperant/framework/...).
5
+ *
6
+ * @returns {string}
7
+ */
8
+ export function resolveRecipesDir(): string;
9
+ /**
10
+ * Walk the recipes/ directory and return every recipe.json that parses.
11
+ * Each entry is the raw recipe object (NOT validated — callers can
12
+ * call validateManifest if they need schema guarantees).
13
+ *
14
+ * @returns {Array<object>}
15
+ */
16
+ export function discoverBundledRecipes(): Array<object>;
17
+ /**
18
+ * Read .aperant/config.json's verification.sandbox block, or null when
19
+ * absent / unparseable. Pure read — no defaults injected.
20
+ *
21
+ * @param {string} projectDir
22
+ * @returns {object|null}
23
+ */
24
+ export function readSandboxConfig(projectDir: string): object | null;
25
+ //# sourceMappingURL=sandbox-config.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-config.d.mts","sourceRoot":"","sources":["../../../../src/cli/verify-proof/sandbox/sandbox-config.mjs"],"names":[],"mappings":"AA0BA;;;;;;GAMG;AACH,qCAFa,MAAM,CAMlB;AAED;;;;;;GAMG;AACH,0CAFa,KAAK,CAAC,MAAM,CAAC,CAiBzB;AAED;;;;;;GAMG;AACH,8CAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAavB"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * sandbox-config.mjs — central loader for sandbox recipes + the
3
+ * adopter's verification.sandbox config block.
4
+ *
5
+ * Two responsibilities:
6
+ *
7
+ * 1. discoverBundledRecipes() — walk packages/framework/recipes/<id>/
8
+ * and return every parseable recipe.json. Mirrors the bundled-
9
+ * drivers discovery pattern in commands/detect-runtime.mjs.
10
+ *
11
+ * 2. readSandboxConfig(projectDir) — read .aperant/config.json's
12
+ * verification.sandbox block (or null if absent). Pure read; no
13
+ * merge with config.local.json — sandbox state is shared, not
14
+ * per-device.
15
+ *
16
+ * Stays in src/cli/verify-proof/sandbox/ so consumers under that tree
17
+ * share one loader. The CLI commands (recipe-diff, apply-recipe,
18
+ * verify-sandbox) all import from here.
19
+ */
20
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
21
+ import { dirname, join, resolve } from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+ const __dirname = dirname(fileURLToPath(import.meta.url));
24
+ /**
25
+ * Resolve the framework's recipes directory at runtime. Works whether
26
+ * the module is loaded from source (packages/framework/src/...) or from
27
+ * the installed dist (.aperant/deps/node_modules/@aperant/framework/...).
28
+ *
29
+ * @returns {string}
30
+ */
31
+ export function resolveRecipesDir() {
32
+ // __dirname → packages/framework/src/cli/verify-proof/sandbox/
33
+ // → up four levels → packages/framework/
34
+ return resolve(__dirname, '..', '..', '..', '..', 'recipes');
35
+ }
36
+ /**
37
+ * Walk the recipes/ directory and return every recipe.json that parses.
38
+ * Each entry is the raw recipe object (NOT validated — callers can
39
+ * call validateManifest if they need schema guarantees).
40
+ *
41
+ * @returns {Array<object>}
42
+ */
43
+ export function discoverBundledRecipes() {
44
+ const dir = resolveRecipesDir();
45
+ if (!existsSync(dir))
46
+ return [];
47
+ const out = [];
48
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
49
+ if (!entry.isDirectory())
50
+ continue;
51
+ const recipePath = join(dir, entry.name, 'recipe.json');
52
+ if (!existsSync(recipePath))
53
+ continue;
54
+ try {
55
+ out.push(JSON.parse(readFileSync(recipePath, 'utf-8')));
56
+ }
57
+ catch {
58
+ /* skip malformed recipes */
59
+ }
60
+ }
61
+ return out;
62
+ }
63
+ /**
64
+ * Read .aperant/config.json's verification.sandbox block, or null when
65
+ * absent / unparseable. Pure read — no defaults injected.
66
+ *
67
+ * @param {string} projectDir
68
+ * @returns {object|null}
69
+ */
70
+ export function readSandboxConfig(projectDir) {
71
+ const path = join(resolve(projectDir), '.aperant', 'config.json');
72
+ if (!existsSync(path))
73
+ return null;
74
+ try {
75
+ const config = JSON.parse(readFileSync(path, 'utf-8'));
76
+ const v = config?.verification;
77
+ if (v === null || typeof v !== 'object')
78
+ return null;
79
+ return v.sandbox ?? null;
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ }
85
+ //# sourceMappingURL=sandbox-config.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-config.mjs","sourceRoot":"","sources":["../../../../src/cli/verify-proof/sandbox/sandbox-config.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAEzD;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB;IAChC,+DAA+D;IAC/D,yCAAyC;IACzC,OAAO,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAA;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB;IACrC,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAA;IAC/B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAC/B,MAAM,GAAG,GAAG,EAAE,CAAA;IACd,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAQ;QAClC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;QACvD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,SAAQ;QACrC,IAAI,CAAC;YACJ,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;QACxD,CAAC;QAAC,MAAM,CAAC;YACR,4BAA4B;QAC7B,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAA;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAU;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,aAAa,CAAC,CAAA;IACjE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAClC,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;QACtD,MAAM,CAAC,GAAG,MAAM,EAAE,YAAY,CAAA;QAC9B,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACpD,OAAO,CAAC,CAAC,OAAO,IAAI,IAAI,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @typedef {Object} SelfTestVerdict
3
+ * @property {boolean} verified
4
+ * @property {'approved'|'needs_human'} verdict
5
+ * @property {null|'self_test_failed_sqlite_busy'|'self_test_failed_port_conflict'|'self_test_failed_app_init_timeout'} reason
6
+ * @property {number} durationMs
7
+ * @property {{A: ChildOutcome, B: ChildOutcome}} children
8
+ */
9
+ /**
10
+ * @typedef {Object} ChildOutcome
11
+ * @property {boolean} ready
12
+ * @property {string|null} failureReason
13
+ * @property {string} stderrTail
14
+ */
15
+ /**
16
+ * @param {{projectDir: string, recipeId: string, recipes: object[]}} args
17
+ * @returns {Promise<SelfTestVerdict>}
18
+ */
19
+ export function runSelfTest({ projectDir, recipeId, recipes }: {
20
+ projectDir: string;
21
+ recipeId: string;
22
+ recipes: object[];
23
+ }): Promise<SelfTestVerdict>;
24
+ export type SelfTestVerdict = {
25
+ verified: boolean;
26
+ verdict: "approved" | "needs_human";
27
+ reason: null | "self_test_failed_sqlite_busy" | "self_test_failed_port_conflict" | "self_test_failed_app_init_timeout";
28
+ durationMs: number;
29
+ children: {
30
+ A: ChildOutcome;
31
+ B: ChildOutcome;
32
+ };
33
+ };
34
+ export type ChildOutcome = {
35
+ ready: boolean;
36
+ failureReason: string | null;
37
+ stderrTail: string;
38
+ };
39
+ //# sourceMappingURL=self-test.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-test.d.mts","sourceRoot":"","sources":["../../../../src/cli/verify-proof/sandbox/self-test.mjs"],"names":[],"mappings":"AAgCA;;;;;;;GAOG;AAEH;;;;;GAKG;AAEH;;;GAGG;AACH,+DAHW;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAC,GACvD,OAAO,CAAC,eAAe,CAAC,CA4CpC;;cA5Da,OAAO;aACP,UAAU,GAAC,aAAa;YACxB,IAAI,GAAC,8BAA8B,GAAC,gCAAgC,GAAC,mCAAmC;gBACxG,MAAM;cACN;QAAC,CAAC,EAAE,YAAY,CAAC;QAAC,CAAC,EAAE,YAAY,CAAA;KAAC;;;WAKlC,OAAO;mBACP,MAAM,GAAC,IAAI;gBACX,MAAM"}