@garygentry/feature-forge 0.1.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 (231) hide show
  1. package/LICENSE +21 -0
  2. package/adapters/GENERATION-REPORT.md +128 -0
  3. package/adapters/claude/agents/forge-researcher.md +137 -0
  4. package/adapters/claude/agents/forge-spec-writer.md +115 -0
  5. package/adapters/claude/agents/forge-verifier.md +121 -0
  6. package/adapters/claude/references/epic-manifest-schema.json +120 -0
  7. package/adapters/claude/references/forge-config-schema.json +166 -0
  8. package/adapters/claude/references/pipeline-state-schema.json +110 -0
  9. package/adapters/claude/references/portable-root.md +56 -0
  10. package/adapters/claude/references/process-overview.md +123 -0
  11. package/adapters/claude/references/ralph-loop-contract.md +221 -0
  12. package/adapters/claude/references/shared-conventions.md +144 -0
  13. package/adapters/claude/references/skill-frontmatter.schema.json +17 -0
  14. package/adapters/claude/references/stack-resolution.md +51 -0
  15. package/adapters/claude/references/stacks/_generic.md +90 -0
  16. package/adapters/claude/references/stacks/go.md +138 -0
  17. package/adapters/claude/references/stacks/python.md +163 -0
  18. package/adapters/claude/references/stacks/rust.md +151 -0
  19. package/adapters/claude/references/stacks/typescript.md +111 -0
  20. package/adapters/claude/references/vendor-construct-inventory.md +49 -0
  21. package/adapters/claude/scripts/forge-root.sh +50 -0
  22. package/adapters/claude/skills/forge/SKILL.md +165 -0
  23. package/adapters/claude/skills/forge-0-epic/SKILL.md +303 -0
  24. package/adapters/claude/skills/forge-0-epic/references/edit-mode.md +222 -0
  25. package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +64 -0
  26. package/adapters/claude/skills/forge-1-prd/SKILL.md +121 -0
  27. package/adapters/claude/skills/forge-1-prd/references/prd-template.md +106 -0
  28. package/adapters/claude/skills/forge-2-tech/SKILL.md +198 -0
  29. package/adapters/claude/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
  30. package/adapters/claude/skills/forge-3-specs/SKILL.md +154 -0
  31. package/adapters/claude/skills/forge-3-specs/references/spec-archetypes.md +106 -0
  32. package/adapters/claude/skills/forge-3-specs/references/spec-examples.md +71 -0
  33. package/adapters/claude/skills/forge-4-backlog/SKILL.md +146 -0
  34. package/adapters/claude/skills/forge-5-loop/SKILL.md +303 -0
  35. package/adapters/claude/skills/forge-5-loop/references/result-reporting.md +63 -0
  36. package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +214 -0
  37. package/adapters/claude/skills/forge-6-docs/SKILL.md +179 -0
  38. package/adapters/claude/skills/forge-6-docs/references/doc-conventions.md +126 -0
  39. package/adapters/claude/skills/forge-fix/SKILL.md +65 -0
  40. package/adapters/claude/skills/forge-init/SKILL.md +29 -0
  41. package/adapters/claude/skills/forge-verify/SKILL.md +219 -0
  42. package/adapters/claude/skills/forge-verify/references/verification-checklists.md +379 -0
  43. package/adapters/codex/agents/forge-researcher.md +133 -0
  44. package/adapters/codex/agents/forge-spec-writer.md +112 -0
  45. package/adapters/codex/agents/forge-verifier.md +115 -0
  46. package/adapters/codex/agents/openai.yaml +10 -0
  47. package/adapters/codex/references/epic-manifest-schema.json +120 -0
  48. package/adapters/codex/references/forge-config-schema.json +166 -0
  49. package/adapters/codex/references/pipeline-state-schema.json +110 -0
  50. package/adapters/codex/references/portable-root.md +56 -0
  51. package/adapters/codex/references/process-overview.md +123 -0
  52. package/adapters/codex/references/ralph-loop-contract.md +221 -0
  53. package/adapters/codex/references/shared-conventions.md +144 -0
  54. package/adapters/codex/references/skill-frontmatter.schema.json +17 -0
  55. package/adapters/codex/references/stack-resolution.md +51 -0
  56. package/adapters/codex/references/stacks/_generic.md +90 -0
  57. package/adapters/codex/references/stacks/go.md +138 -0
  58. package/adapters/codex/references/stacks/python.md +163 -0
  59. package/adapters/codex/references/stacks/rust.md +151 -0
  60. package/adapters/codex/references/stacks/typescript.md +111 -0
  61. package/adapters/codex/references/vendor-construct-inventory.md +49 -0
  62. package/adapters/codex/scripts/forge-root.sh +50 -0
  63. package/adapters/codex/skills/forge/forge.md +164 -0
  64. package/adapters/codex/skills/forge-0-epic/forge-0-epic.md +302 -0
  65. package/adapters/codex/skills/forge-0-epic/references/edit-mode.md +222 -0
  66. package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +64 -0
  67. package/adapters/codex/skills/forge-1-prd/forge-1-prd.md +120 -0
  68. package/adapters/codex/skills/forge-1-prd/references/prd-template.md +106 -0
  69. package/adapters/codex/skills/forge-2-tech/forge-2-tech.md +197 -0
  70. package/adapters/codex/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
  71. package/adapters/codex/skills/forge-3-specs/forge-3-specs.md +153 -0
  72. package/adapters/codex/skills/forge-3-specs/references/spec-archetypes.md +106 -0
  73. package/adapters/codex/skills/forge-3-specs/references/spec-examples.md +71 -0
  74. package/adapters/codex/skills/forge-4-backlog/forge-4-backlog.md +145 -0
  75. package/adapters/codex/skills/forge-5-loop/forge-5-loop.md +302 -0
  76. package/adapters/codex/skills/forge-5-loop/references/result-reporting.md +63 -0
  77. package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +214 -0
  78. package/adapters/codex/skills/forge-6-docs/forge-6-docs.md +178 -0
  79. package/adapters/codex/skills/forge-6-docs/references/doc-conventions.md +126 -0
  80. package/adapters/codex/skills/forge-fix/forge-fix.md +64 -0
  81. package/adapters/codex/skills/forge-init/forge-init.md +29 -0
  82. package/adapters/codex/skills/forge-verify/forge-verify.md +218 -0
  83. package/adapters/codex/skills/forge-verify/references/verification-checklists.md +379 -0
  84. package/adapters/copilot/agents/forge-researcher.md +133 -0
  85. package/adapters/copilot/agents/forge-spec-writer.md +112 -0
  86. package/adapters/copilot/agents/forge-verifier.md +115 -0
  87. package/adapters/copilot/references/epic-manifest-schema.json +120 -0
  88. package/adapters/copilot/references/forge-config-schema.json +166 -0
  89. package/adapters/copilot/references/pipeline-state-schema.json +110 -0
  90. package/adapters/copilot/references/portable-root.md +56 -0
  91. package/adapters/copilot/references/process-overview.md +123 -0
  92. package/adapters/copilot/references/ralph-loop-contract.md +221 -0
  93. package/adapters/copilot/references/shared-conventions.md +144 -0
  94. package/adapters/copilot/references/skill-frontmatter.schema.json +17 -0
  95. package/adapters/copilot/references/stack-resolution.md +51 -0
  96. package/adapters/copilot/references/stacks/_generic.md +90 -0
  97. package/adapters/copilot/references/stacks/go.md +138 -0
  98. package/adapters/copilot/references/stacks/python.md +163 -0
  99. package/adapters/copilot/references/stacks/rust.md +151 -0
  100. package/adapters/copilot/references/stacks/typescript.md +111 -0
  101. package/adapters/copilot/references/vendor-construct-inventory.md +49 -0
  102. package/adapters/copilot/scripts/forge-root.sh +50 -0
  103. package/adapters/copilot/skills/forge/forge.md +164 -0
  104. package/adapters/copilot/skills/forge-0-epic/forge-0-epic.md +302 -0
  105. package/adapters/copilot/skills/forge-0-epic/references/edit-mode.md +222 -0
  106. package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +64 -0
  107. package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +120 -0
  108. package/adapters/copilot/skills/forge-1-prd/references/prd-template.md +106 -0
  109. package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +197 -0
  110. package/adapters/copilot/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
  111. package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +153 -0
  112. package/adapters/copilot/skills/forge-3-specs/references/spec-archetypes.md +106 -0
  113. package/adapters/copilot/skills/forge-3-specs/references/spec-examples.md +71 -0
  114. package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +145 -0
  115. package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +302 -0
  116. package/adapters/copilot/skills/forge-5-loop/references/result-reporting.md +63 -0
  117. package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +214 -0
  118. package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +178 -0
  119. package/adapters/copilot/skills/forge-6-docs/references/doc-conventions.md +126 -0
  120. package/adapters/copilot/skills/forge-fix/forge-fix.md +64 -0
  121. package/adapters/copilot/skills/forge-init/forge-init.md +29 -0
  122. package/adapters/copilot/skills/forge-verify/forge-verify.md +218 -0
  123. package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +379 -0
  124. package/adapters/cursor/agents/forge-researcher.mdc +134 -0
  125. package/adapters/cursor/agents/forge-spec-writer.mdc +113 -0
  126. package/adapters/cursor/agents/forge-verifier.mdc +116 -0
  127. package/adapters/cursor/references/epic-manifest-schema.json +120 -0
  128. package/adapters/cursor/references/forge-config-schema.json +166 -0
  129. package/adapters/cursor/references/pipeline-state-schema.json +110 -0
  130. package/adapters/cursor/references/portable-root.md +56 -0
  131. package/adapters/cursor/references/process-overview.md +123 -0
  132. package/adapters/cursor/references/ralph-loop-contract.md +221 -0
  133. package/adapters/cursor/references/shared-conventions.md +144 -0
  134. package/adapters/cursor/references/skill-frontmatter.schema.json +17 -0
  135. package/adapters/cursor/references/stack-resolution.md +51 -0
  136. package/adapters/cursor/references/stacks/_generic.md +90 -0
  137. package/adapters/cursor/references/stacks/go.md +138 -0
  138. package/adapters/cursor/references/stacks/python.md +163 -0
  139. package/adapters/cursor/references/stacks/rust.md +151 -0
  140. package/adapters/cursor/references/stacks/typescript.md +111 -0
  141. package/adapters/cursor/references/vendor-construct-inventory.md +49 -0
  142. package/adapters/cursor/scripts/forge-root.sh +50 -0
  143. package/adapters/cursor/skills/forge/forge.mdc +165 -0
  144. package/adapters/cursor/skills/forge-0-epic/forge-0-epic.mdc +303 -0
  145. package/adapters/cursor/skills/forge-0-epic/references/edit-mode.md +222 -0
  146. package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +64 -0
  147. package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +121 -0
  148. package/adapters/cursor/skills/forge-1-prd/references/prd-template.md +106 -0
  149. package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +198 -0
  150. package/adapters/cursor/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
  151. package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +154 -0
  152. package/adapters/cursor/skills/forge-3-specs/references/spec-archetypes.md +106 -0
  153. package/adapters/cursor/skills/forge-3-specs/references/spec-examples.md +71 -0
  154. package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +146 -0
  155. package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +303 -0
  156. package/adapters/cursor/skills/forge-5-loop/references/result-reporting.md +63 -0
  157. package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +214 -0
  158. package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +179 -0
  159. package/adapters/cursor/skills/forge-6-docs/references/doc-conventions.md +126 -0
  160. package/adapters/cursor/skills/forge-fix/forge-fix.mdc +65 -0
  161. package/adapters/cursor/skills/forge-init/forge-init.mdc +30 -0
  162. package/adapters/cursor/skills/forge-verify/forge-verify.mdc +219 -0
  163. package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +379 -0
  164. package/adapters/gemini/agents/forge-researcher.md +133 -0
  165. package/adapters/gemini/agents/forge-spec-writer.md +112 -0
  166. package/adapters/gemini/agents/forge-verifier.md +115 -0
  167. package/adapters/gemini/gemini-extension.json +54 -0
  168. package/adapters/gemini/references/epic-manifest-schema.json +120 -0
  169. package/adapters/gemini/references/forge-config-schema.json +166 -0
  170. package/adapters/gemini/references/pipeline-state-schema.json +110 -0
  171. package/adapters/gemini/references/portable-root.md +56 -0
  172. package/adapters/gemini/references/process-overview.md +123 -0
  173. package/adapters/gemini/references/ralph-loop-contract.md +221 -0
  174. package/adapters/gemini/references/shared-conventions.md +144 -0
  175. package/adapters/gemini/references/skill-frontmatter.schema.json +17 -0
  176. package/adapters/gemini/references/stack-resolution.md +51 -0
  177. package/adapters/gemini/references/stacks/_generic.md +90 -0
  178. package/adapters/gemini/references/stacks/go.md +138 -0
  179. package/adapters/gemini/references/stacks/python.md +163 -0
  180. package/adapters/gemini/references/stacks/rust.md +151 -0
  181. package/adapters/gemini/references/stacks/typescript.md +111 -0
  182. package/adapters/gemini/references/vendor-construct-inventory.md +49 -0
  183. package/adapters/gemini/scripts/forge-root.sh +50 -0
  184. package/adapters/gemini/skills/forge/forge.md +164 -0
  185. package/adapters/gemini/skills/forge-0-epic/forge-0-epic.md +302 -0
  186. package/adapters/gemini/skills/forge-0-epic/references/edit-mode.md +222 -0
  187. package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +64 -0
  188. package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +120 -0
  189. package/adapters/gemini/skills/forge-1-prd/references/prd-template.md +106 -0
  190. package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +197 -0
  191. package/adapters/gemini/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
  192. package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +153 -0
  193. package/adapters/gemini/skills/forge-3-specs/references/spec-archetypes.md +106 -0
  194. package/adapters/gemini/skills/forge-3-specs/references/spec-examples.md +71 -0
  195. package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +145 -0
  196. package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +302 -0
  197. package/adapters/gemini/skills/forge-5-loop/references/result-reporting.md +63 -0
  198. package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +214 -0
  199. package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +178 -0
  200. package/adapters/gemini/skills/forge-6-docs/references/doc-conventions.md +126 -0
  201. package/adapters/gemini/skills/forge-fix/forge-fix.md +64 -0
  202. package/adapters/gemini/skills/forge-init/forge-init.md +29 -0
  203. package/adapters/gemini/skills/forge-verify/forge-verify.md +218 -0
  204. package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +379 -0
  205. package/dist/agent-targets.d.ts +70 -0
  206. package/dist/agent-targets.js +111 -0
  207. package/dist/apply.d.ts +49 -0
  208. package/dist/apply.js +246 -0
  209. package/dist/cli.d.ts +94 -0
  210. package/dist/cli.js +508 -0
  211. package/dist/detect.d.ts +45 -0
  212. package/dist/detect.js +72 -0
  213. package/dist/fsutil.d.ts +56 -0
  214. package/dist/fsutil.js +175 -0
  215. package/dist/hash.d.ts +50 -0
  216. package/dist/hash.js +107 -0
  217. package/dist/index.d.ts +8 -0
  218. package/dist/index.js +9 -0
  219. package/dist/manifest.d.ts +72 -0
  220. package/dist/manifest.js +222 -0
  221. package/dist/plan.d.ts +66 -0
  222. package/dist/plan.js +166 -0
  223. package/dist/rauf.d.ts +83 -0
  224. package/dist/rauf.js +118 -0
  225. package/dist/report.d.ts +35 -0
  226. package/dist/report.js +110 -0
  227. package/dist/source.d.ts +69 -0
  228. package/dist/source.js +164 -0
  229. package/dist/types.d.ts +264 -0
  230. package/dist/types.js +57 -0
  231. package/package.json +42 -0
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The run reporter (spec 07 §3.5/§3.6). Pure: turns a `RunReport` into a string;
3
+ * the caller (`cli.ts`) writes it. No I/O. Imports only types from `./types.js`.
4
+ *
5
+ * The `--json` form emits the same `RunReport` data — the machine surface the
6
+ * `agent-detection-map` consumers (OS-matrix dry-runs) read (REQ-DET-05).
7
+ */
8
+ import { type FileActionKind, type InstallerError, type RunReport } from "./types.js";
9
+ /** Options for rendering a run report. */
10
+ export interface RenderOpts {
11
+ /** Emit machine-readable JSON instead of human text (REQ-DET-05, REQ-OBS-01). */
12
+ readonly json: boolean;
13
+ }
14
+ /**
15
+ * Render a RunReport to a string (REQ-OBS-01). Pure — no I/O; the caller writes it.
16
+ *
17
+ * Human form (default): a header line, then per agent a block, then a final
18
+ * `Summary: N ok, N failed (exit X)` line. JSON form: `JSON.stringify(report)`
19
+ * (the same RunReport data) so non-Node consumers can parse it (REQ-DET-05).
20
+ *
21
+ * @param report - the assembled run report.
22
+ * @param opts - { json } selecting the output form.
23
+ * @returns the rendered string (no trailing newline; the caller adds one).
24
+ */
25
+ export declare function renderReport(report: RunReport, opts: RenderOpts): string;
26
+ /** Map a FileActionKind to its human verb (REQ-OBS-01 vocabulary). */
27
+ export declare function actionVerb(kind: FileActionKind): string;
28
+ /**
29
+ * Compose a single actionable line from a structured error (REQ-OBS-02): always prefer
30
+ * the error's own `message`/`remedy`; supply a per-code default remedy when absent. Pure.
31
+ *
32
+ * @param e - the structured error.
33
+ * @returns "<message> — path: <path> — remedy: <remedy>" (agent is already in the header).
34
+ */
35
+ export declare function formatError(e: InstallerError): string;
package/dist/report.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * The run reporter (spec 07 §3.5/§3.6). Pure: turns a `RunReport` into a string;
3
+ * the caller (`cli.ts`) writes it. No I/O. Imports only types from `./types.js`.
4
+ *
5
+ * The `--json` form emits the same `RunReport` data — the machine surface the
6
+ * `agent-detection-map` consumers (OS-matrix dry-runs) read (REQ-DET-05).
7
+ */
8
+ /**
9
+ * Render a RunReport to a string (REQ-OBS-01). Pure — no I/O; the caller writes it.
10
+ *
11
+ * Human form (default): a header line, then per agent a block, then a final
12
+ * `Summary: N ok, N failed (exit X)` line. JSON form: `JSON.stringify(report)`
13
+ * (the same RunReport data) so non-Node consumers can parse it (REQ-DET-05).
14
+ *
15
+ * @param report - the assembled run report.
16
+ * @param opts - { json } selecting the output form.
17
+ * @returns the rendered string (no trailing newline; the caller adds one).
18
+ */
19
+ export function renderReport(report, opts) {
20
+ if (opts.json) {
21
+ return JSON.stringify(report);
22
+ }
23
+ return renderHuman(report);
24
+ }
25
+ /** Human-readable rendering (REQ-OBS-01/02). */
26
+ function renderHuman(report) {
27
+ const out = [];
28
+ const dr = report.dryRun ? " — dry-run" : "";
29
+ out.push(`${report.subcommand} (${report.scope}, ${report.mode})${dr}`);
30
+ for (const a of report.agents) {
31
+ out.push(...renderAgent(report.subcommand, a));
32
+ }
33
+ // Run-level rauf preflight failure (spec 07 §3.2): skills still installed, but surface it.
34
+ if (report.raufError) {
35
+ out.push(`rauf: FAILED — ${report.raufError.code}`);
36
+ out.push(` ${formatError(report.raufError)}`);
37
+ }
38
+ const okCount = report.agents.filter((a) => a.ok).length;
39
+ const failCount = report.agents.length - okCount;
40
+ out.push(`Summary: ${okCount} ok, ${failCount} failed (exit ${report.exitCode})`);
41
+ return out.join("\n");
42
+ }
43
+ /** One agent's block: status line, per-file actions, and (if failed) the actionable error. */
44
+ function renderAgent(subcommand, a) {
45
+ const lines = [];
46
+ if (subcommand === "list") {
47
+ // Decode the synthetic status rows (§3.3) into one human line.
48
+ const status = a.actions.map((f) => f.relpath).join(" ");
49
+ lines.push(`${a.agent}: ${a.detected ? "detected" : "not detected"} ${status}`);
50
+ return lines;
51
+ }
52
+ if (!a.ok && a.error) {
53
+ lines.push(`${a.agent}: FAILED — ${a.error.code}`);
54
+ lines.push(` ${formatError(a.error)}`); // actionable: agent + path + remedy (REQ-OBS-02)
55
+ return lines;
56
+ }
57
+ lines.push(`${a.agent}: ok`);
58
+ for (const f of a.actions) {
59
+ lines.push(` ${actionVerb(f.action)} ${f.relpath}`);
60
+ }
61
+ if (a.raufPin)
62
+ lines.push(` rauf default runner pinned: ${a.raufPin}`);
63
+ return lines;
64
+ }
65
+ /** Map a FileActionKind to its human verb (REQ-OBS-01 vocabulary). */
66
+ export function actionVerb(kind) {
67
+ switch (kind) {
68
+ case "create":
69
+ return "create ";
70
+ case "overwrite":
71
+ return "overwrite";
72
+ case "skip-modified":
73
+ return "skip "; // (locally modified — see error/remedy)
74
+ case "unchanged":
75
+ return "unchanged";
76
+ case "remove":
77
+ return "remove ";
78
+ }
79
+ }
80
+ /**
81
+ * Compose a single actionable line from a structured error (REQ-OBS-02): always prefer
82
+ * the error's own `message`/`remedy`; supply a per-code default remedy when absent. Pure.
83
+ *
84
+ * @param e - the structured error.
85
+ * @returns "<message> — path: <path> — remedy: <remedy>" (agent is already in the header).
86
+ */
87
+ export function formatError(e) {
88
+ const parts = [e.message];
89
+ if (e.path)
90
+ parts.push(`path: ${e.path}`);
91
+ const remedy = e.remedy ?? DEFAULT_REMEDY[e.code];
92
+ if (remedy)
93
+ parts.push(`remedy: ${remedy}`);
94
+ return parts.join(" — ");
95
+ }
96
+ /**
97
+ * Per-code default remedy when the error did not carry one (REQ-OBS-02). Intentionally
98
+ * `Partial` over `ErrorCode`: `UNEXPECTED` is surfaced via the `cli.ts` boundary message
99
+ * (spec 07 §3.1/§4), not `formatError`, so it has no entry here.
100
+ */
101
+ const DEFAULT_REMEDY = {
102
+ USAGE: "run 'feature-forge --help' for usage",
103
+ SOURCE_MISSING: "run the adapters build to generate adapters/<agent>/",
104
+ SOURCE_INVALID: "regenerate the bundle (run the adapters build)",
105
+ LOCALLY_MODIFIED: "re-run with --force to overwrite local changes",
106
+ WRITE_DENIED: "check write permission to the path, or choose --global vs project scope",
107
+ PATH_ESCAPE: "report this — a destination resolved outside the agent config dir",
108
+ RAUF_UNRESOLVABLE: "the default loop will be unavailable until rauf publishes (see packaging-docs-ci); skills were still installed",
109
+ MANIFEST_CORRUPT: "remove the corrupt .feature-forge.<scope>.json and re-run install",
110
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Source bundle location and integrity checking (spec 03, REQ-OPS-06, D7).
3
+ *
4
+ * Resolves the read-only adapter bundle for an agent from one of three locations (explicit
5
+ * `--source`, the packaged copy, or the in-repo `../adapters/`), runs the minimal integrity
6
+ * check, and fingerprints it (sourceHash + skills + files). Fallible operations return
7
+ * `Result<T, E>` and never throw for expected errors. Zero runtime dependencies.
8
+ */
9
+ import { type AgentId, type Result } from "./types.js";
10
+ /** Options for bundle resolution. */
11
+ export interface LocateBundleOpts {
12
+ /**
13
+ * Hidden test hook (`--source <dir>`, D7). When set, the bundle is resolved as
14
+ * `<source>/<agent>` and the packaged / in-repo locations are NOT consulted.
15
+ */
16
+ source?: string;
17
+ }
18
+ /**
19
+ * Aggregate of a located, integrity-checked, fingerprinted bundle (spec 03 §3.7).
20
+ */
21
+ export interface LocatedSource {
22
+ /** Absolute path to the agent bundle root (e.g. `.../adapters/claude`). */
23
+ readonly root: string;
24
+ /** sha256 over the bundle's sorted-path file set — the drift anchor (`manifest.sourceHash`). */
25
+ readonly sourceHash: string;
26
+ /** Installed skill ids (the bundle's `skills/*` dir names) for `manifest.skills`. */
27
+ readonly skills: readonly string[];
28
+ /** Per-file inventory (`{ relpath, sha256 }`, sorted by POSIX relpath) — the planner's input. */
29
+ readonly files: ReadonlyArray<{
30
+ readonly relpath: string;
31
+ readonly sha256: string;
32
+ }>;
33
+ }
34
+ /**
35
+ * Locate the read-only adapter bundle directory for `agent` (D7, REQ-OPS-06).
36
+ *
37
+ * Resolution order (first existing directory wins): `opts.source/<agent>`, then
38
+ * `<installerPkgRoot>/adapters/<agent>`, then `<repoRoot>/adapters/<agent>` (both derived from
39
+ * `import.meta.url`, cwd-independent).
40
+ *
41
+ * @returns ok(absolutePath) when a candidate dir exists; err(SOURCE_MISSING) naming the expected
42
+ * path + remedy otherwise.
43
+ */
44
+ export declare function locateBundle(agent: AgentId, opts?: LocateBundleOpts): Result<string>;
45
+ /**
46
+ * Minimal integrity check for a located bundle (REQ-OPS-06). Valid iff the `BUNDLE_REQUIRED_PATHS`
47
+ * are present: `skills/` is a non-empty dir, `scripts/forge-root.sh` exists, and (gemini)
48
+ * `gemini-extension.json` exists. Does NOT require `.claude-plugin/plugin.json` or
49
+ * `epic-manifest.py` (IR-1 / OQ-A).
50
+ *
51
+ * @returns ok(undefined) when every required path is present; err(SOURCE_INVALID) naming the
52
+ * first missing/invalid required path otherwise.
53
+ */
54
+ export declare function checkIntegrity(bundlePath: string, agent: AgentId): Result<void>;
55
+ /**
56
+ * List the skill ids a bundle contains: the directory names directly under
57
+ * `<bundlePath>/skills/` (REQ-SCALE-02). Enumerates DIRECTORY NAMES ONLY and never opens a skill
58
+ * file. Returned sorted (byte-wise).
59
+ */
60
+ export declare function listBundleSkills(bundlePath: string): string[];
61
+ /**
62
+ * Locate, integrity-check, and fingerprint one agent's bundle in a single call (spec 03 §3.7).
63
+ *
64
+ * @returns ok(LocatedSource) when the bundle exists and passes the integrity check; otherwise the
65
+ * exact err(SOURCE_MISSING) / err(SOURCE_INVALID) from locateBundle/checkIntegrity.
66
+ */
67
+ export declare function locateSource(agent: AgentId, opts?: {
68
+ source?: string;
69
+ }): Result<LocatedSource>;
package/dist/source.js ADDED
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Source bundle location and integrity checking (spec 03, REQ-OPS-06, D7).
3
+ *
4
+ * Resolves the read-only adapter bundle for an agent from one of three locations (explicit
5
+ * `--source`, the packaged copy, or the in-repo `../adapters/`), runs the minimal integrity
6
+ * check, and fingerprints it (sourceHash + skills + files). Fallible operations return
7
+ * `Result<T, E>` and never throw for expected errors. Zero runtime dependencies.
8
+ */
9
+ import * as fs from "node:fs";
10
+ import * as path from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { ok, err, BUNDLE_REQUIRED_PATHS, } from "./types.js";
13
+ import { computeSourceHash, listBundleFiles } from "./hash.js";
14
+ /**
15
+ * Locate the read-only adapter bundle directory for `agent` (D7, REQ-OPS-06).
16
+ *
17
+ * Resolution order (first existing directory wins): `opts.source/<agent>`, then
18
+ * `<installerPkgRoot>/adapters/<agent>`, then `<repoRoot>/adapters/<agent>` (both derived from
19
+ * `import.meta.url`, cwd-independent).
20
+ *
21
+ * @returns ok(absolutePath) when a candidate dir exists; err(SOURCE_MISSING) naming the expected
22
+ * path + remedy otherwise.
23
+ */
24
+ export function locateBundle(agent, opts = {}) {
25
+ for (const candidate of bundleCandidates(agent, opts)) {
26
+ if (isDirectory(candidate))
27
+ return ok(candidate);
28
+ }
29
+ const expected = primaryExpectedPath(agent, opts);
30
+ return err({
31
+ code: "SOURCE_MISSING",
32
+ agent,
33
+ path: expected,
34
+ message: `no source bundle for agent "${agent}" — expected an adapters directory at ${expected}. ` +
35
+ `The bundle is generated by the adapters build; run it (or pass --source <dir>) before installing.`,
36
+ remedy: "run the adapters build to generate adapters/<agent>/, or pass --source <dir>",
37
+ });
38
+ }
39
+ /**
40
+ * Minimal integrity check for a located bundle (REQ-OPS-06). Valid iff the `BUNDLE_REQUIRED_PATHS`
41
+ * are present: `skills/` is a non-empty dir, `scripts/forge-root.sh` exists, and (gemini)
42
+ * `gemini-extension.json` exists. Does NOT require `.claude-plugin/plugin.json` or
43
+ * `epic-manifest.py` (IR-1 / OQ-A).
44
+ *
45
+ * @returns ok(undefined) when every required path is present; err(SOURCE_INVALID) naming the
46
+ * first missing/invalid required path otherwise.
47
+ */
48
+ export function checkIntegrity(bundlePath, agent) {
49
+ const skillsDir = path.join(bundlePath, "skills");
50
+ if (!isDirectory(skillsDir) || !hasEntries(skillsDir)) {
51
+ return invalid(agent, skillsDir, "skills/ is missing or empty");
52
+ }
53
+ const required = requiredPathsFor(agent).filter((rel) => rel !== "skills");
54
+ for (const rel of required) {
55
+ const abs = path.join(bundlePath, rel);
56
+ if (!fs.existsSync(abs)) {
57
+ return invalid(agent, abs, `required path "${rel}" is missing`);
58
+ }
59
+ }
60
+ return ok(undefined);
61
+ }
62
+ /**
63
+ * List the skill ids a bundle contains: the directory names directly under
64
+ * `<bundlePath>/skills/` (REQ-SCALE-02). Enumerates DIRECTORY NAMES ONLY and never opens a skill
65
+ * file. Returned sorted (byte-wise).
66
+ */
67
+ export function listBundleSkills(bundlePath) {
68
+ const skillsDir = path.join(bundlePath, "skills");
69
+ let entries;
70
+ try {
71
+ entries = fs.readdirSync(skillsDir, { withFileTypes: true });
72
+ }
73
+ catch {
74
+ return [];
75
+ }
76
+ return entries
77
+ .filter((e) => e.isDirectory())
78
+ .map((e) => e.name)
79
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
80
+ }
81
+ /**
82
+ * Locate, integrity-check, and fingerprint one agent's bundle in a single call (spec 03 §3.7).
83
+ *
84
+ * @returns ok(LocatedSource) when the bundle exists and passes the integrity check; otherwise the
85
+ * exact err(SOURCE_MISSING) / err(SOURCE_INVALID) from locateBundle/checkIntegrity.
86
+ */
87
+ export function locateSource(agent, opts) {
88
+ const located = locateBundle(agent, opts);
89
+ if (!located.ok)
90
+ return located; // SOURCE_MISSING
91
+ const integrity = checkIntegrity(located.value, agent);
92
+ if (!integrity.ok)
93
+ return integrity; // SOURCE_INVALID
94
+ return ok({
95
+ root: located.value,
96
+ sourceHash: computeSourceHash(located.value),
97
+ skills: listBundleSkills(located.value),
98
+ files: listBundleFiles(located.value),
99
+ });
100
+ }
101
+ // ---------------------------------------------------------------------------
102
+ // Internal helpers (module-private — spec 03 §4.1, §4.2)
103
+ // ---------------------------------------------------------------------------
104
+ /** dist/source.js at runtime → its dir is <installerPkgRoot>/dist. */
105
+ function moduleDir() {
106
+ return path.dirname(fileURLToPath(import.meta.url));
107
+ }
108
+ /** <installerPkgRoot> = parent of dist/. */
109
+ function installerPkgRoot() {
110
+ return path.resolve(moduleDir(), "..");
111
+ }
112
+ /** <repoRoot> = parent of installer/. */
113
+ function repoRoot() {
114
+ return path.resolve(installerPkgRoot(), "..");
115
+ }
116
+ /** The ordered candidate bundle dirs for an agent (spec 03 §3.1 table). */
117
+ function bundleCandidates(agent, opts) {
118
+ // Explicit --source is exclusive: the packaged / in-repo locations are NOT consulted (spec §3.1).
119
+ if (opts.source)
120
+ return [path.resolve(opts.source, agent)];
121
+ const candidates = [];
122
+ candidates.push(path.join(installerPkgRoot(), "adapters", agent)); // packaged copy (D7)
123
+ candidates.push(path.join(repoRoot(), "adapters", agent)); // in-repo dev (C-3)
124
+ return candidates;
125
+ }
126
+ /** The most actionable expected path to name in a SOURCE_MISSING error (REQ-OBS-02). */
127
+ function primaryExpectedPath(agent, opts) {
128
+ return opts.source
129
+ ? path.resolve(opts.source, agent)
130
+ : path.join(repoRoot(), "adapters", agent);
131
+ }
132
+ /** common + per-agent required paths (from BUNDLE_REQUIRED_PATHS, 00 §6). */
133
+ function requiredPathsFor(agent) {
134
+ const perAgent = BUNDLE_REQUIRED_PATHS.perAgent[agent] ?? [];
135
+ return [...BUNDLE_REQUIRED_PATHS.common, ...perAgent];
136
+ }
137
+ /** Construct the SOURCE_INVALID error naming the offending path (REQ-OBS-02). */
138
+ function invalid(agent, badPath, why) {
139
+ return err({
140
+ code: "SOURCE_INVALID",
141
+ agent,
142
+ path: badPath,
143
+ message: `source bundle for agent "${agent}" is invalid: ${why} (at ${badPath}).`,
144
+ remedy: "re-run the adapters build to regenerate a complete bundle",
145
+ });
146
+ }
147
+ /** True iff `p` exists and is a directory. */
148
+ function isDirectory(p) {
149
+ try {
150
+ return fs.statSync(p).isDirectory();
151
+ }
152
+ catch {
153
+ return false;
154
+ }
155
+ }
156
+ /** True iff directory `p` has at least one entry (used for the non-empty `skills/` check). */
157
+ function hasEntries(p) {
158
+ try {
159
+ return fs.readdirSync(p).length > 0;
160
+ }
161
+ catch {
162
+ return false;
163
+ }
164
+ }
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Shared type system, error hierarchy, and constants for the cross-agent installer.
3
+ *
4
+ * This module is the foundation: every other module imports the types, constants, and
5
+ * `Result` defined here and does not redefine them (spec 00-core-definitions). Named
6
+ * exports only; `node:` built-ins only where applicable; zero runtime dependencies.
7
+ */
8
+ /**
9
+ * The five coding agents this installer targets (REQ-DET-01). Order is the canonical
10
+ * iteration order used by detection, planning, and reporting so output is deterministic.
11
+ */
12
+ export declare const AGENT_IDS: readonly ["claude", "codex", "copilot", "cursor", "gemini"];
13
+ /** A supported coding-agent identifier. */
14
+ export type AgentId = (typeof AGENT_IDS)[number];
15
+ /**
16
+ * Install scope (REQ-FLAG-02). `"project"` (default) installs into the current project's
17
+ * agent dir (e.g. `./.claude/skills/feature-forge/`); `"global"` installs into the
18
+ * user-level dir (e.g. `~/.claude/skills/feature-forge/`).
19
+ */
20
+ export type Scope = "project" | "global";
21
+ /**
22
+ * Materialization mode (REQ-FLAG-03). `"copy"` is the default and the only mode on Windows;
23
+ * `"symlink"` links the whole namespace dir to the source bundle (opt-in via `--symlink`).
24
+ */
25
+ export type Mode = "copy" | "symlink";
26
+ /** The four invocable subcommands (aliases resolved before this type, spec 07). */
27
+ export type Subcommand = "install" | "update" | "uninstall" | "list";
28
+ /** Process exit codes (REQ-OBS-01/03, spec 07). */
29
+ export declare const EXIT: {
30
+ readonly SUCCESS: 0;
31
+ readonly FAILURE: 1;
32
+ readonly USAGE: 2;
33
+ };
34
+ export type ExitCode = (typeof EXIT)[keyof typeof EXIT];
35
+ /** Manifest schema version; bumped only on a breaking manifest-shape change. */
36
+ export declare const SCHEMA_VERSION: 1;
37
+ /** The single namespace directory name written inside each agent's install location [D5]. */
38
+ export declare const FEATURE_FORGE_NS: "feature-forge";
39
+ /** Filename prefix for the hidden parent-sibling manifest, completed by the scope (§3, spec 05). */
40
+ export declare const MANIFEST_PREFIX: ".feature-forge.";
41
+ /**
42
+ * One row of the static per-agent detection map (REQ-DET-01, REQ-DET-05). Adding a new
43
+ * agent is exactly adding one entry to `AGENT_TARGETS` — no logic change (REQ-SCALE-01).
44
+ *
45
+ * The on-disk destination for an agent under a given scope is derived, not stored:
46
+ * <scopeRoot>/<configDirName>/<installSubdir>/<FEATURE_FORGE_NS>/
47
+ * where scopeRoot is the resolved home (global) or cwd (project).
48
+ */
49
+ export interface AgentTarget {
50
+ /** Stable agent identifier. */
51
+ readonly id: AgentId;
52
+ /**
53
+ * Basename of the agent's config directory, probed for detection (REQ-DET-02),
54
+ * e.g. ".claude", ".codex", ".cursor". Detection is `stat` on this dir, never a subprocess.
55
+ */
56
+ readonly configDirName: string;
57
+ /**
58
+ * Sub-path under the config dir that holds the namespaced install dir, e.g.
59
+ * "skills" (claude/codex/copilot), "rules" (cursor), "extensions" (gemini).
60
+ */
61
+ readonly installSubdir: string;
62
+ /**
63
+ * Informational: the skill-file form this agent's bundle uses — "SKILL.md" (claude),
64
+ * "<name>.md" (codex/copilot/gemini), "<name>.mdc" (cursor). The installer copies the
65
+ * bundle verbatim (REQ-SCALE-02) and does not parse skill files, so this is documentation.
66
+ */
67
+ readonly skillFileForm: string;
68
+ /**
69
+ * Confidence in this row's paths. "confirmed" = source-verified (claude). "best-known"
70
+ * = the TQ-1 paths (codex/copilot/cursor/gemini) to re-verify against each agent's current
71
+ * docs at implementation (REQ-SCALE-01 — isolated, localized correction).
72
+ */
73
+ readonly confidence: "confirmed" | "best-known";
74
+ }
75
+ /** Options for path resolution, injectable so tests never touch the real `~` (spec 02, spec 08). */
76
+ export interface ResolveOpts {
77
+ /** Home dir for global scope. Default: `os.homedir()`. */
78
+ readonly home?: string;
79
+ /** Working dir for project scope. Default: `process.cwd()`. */
80
+ readonly cwd?: string;
81
+ /** Active scope. Default: `"project"`. */
82
+ readonly scope?: Scope;
83
+ }
84
+ /**
85
+ * One agent's detection outcome (REQ-DET-02/04). Returned by `detectAgent`/`detectAgents`
86
+ * (spec 02) and the data half of the `agent-detection-map` surface (REQ-DET-05).
87
+ */
88
+ export interface DetectionResult {
89
+ readonly agent: AgentId;
90
+ /** True iff the agent's config dir is present (primary signal, REQ-DET-02). */
91
+ readonly detected: boolean;
92
+ /** Every config dir probed — named verbatim in the zero-detection report (REQ-DET-04). */
93
+ readonly configDirsProbed: string[];
94
+ /** Secondary, advisory only: whether the agent's CLI is on PATH (never the detection signal). */
95
+ readonly cliOnPath?: boolean;
96
+ /** Resolved absolute install destination for the active scope (the `feature-forge/` namespace dir). */
97
+ readonly destination: string;
98
+ }
99
+ /** One file recorded in the manifest inventory (REQ-SAFE-01). */
100
+ export interface ManifestFile {
101
+ /** Path relative to the manifest's `destination`. */
102
+ readonly path: string;
103
+ /** SHA-256 of the written bytes. Omitted for symlink mode (no per-file copy exists). */
104
+ readonly sha256?: string;
105
+ }
106
+ /**
107
+ * The persisted per-install manifest (REQ-SAFE-01/03), written as the hidden parent-sibling
108
+ * `<installSubdir>/.feature-forge.<scope>.json` (spec 05). It is the sole record `list`/`update`/
109
+ * `uninstall` use to tell installer-written content from user content and to detect drift.
110
+ */
111
+ export interface InstallManifest {
112
+ readonly schemaVersion: typeof SCHEMA_VERSION;
113
+ readonly agent: AgentId;
114
+ readonly scope: Scope;
115
+ readonly mode: Mode;
116
+ /** Absolute path of the `feature-forge/` namespace dir this manifest governs. */
117
+ readonly destination: string;
118
+ /**
119
+ * Bundle version coordinate. **`null` today** — the consumed `adapters/` bundles carry no
120
+ * version (no plugin.json / version header; gemini's `gemini-extension.json` version is a
121
+ * `0.0.0` placeholder). Recording a real value is deferred to the generator under OQ-A/IR-1;
122
+ * C-3 forbids the installer reading outside `adapters/` to synthesize one.
123
+ */
124
+ readonly featureForgeVersion: string | null;
125
+ /** SHA-256 over the source bundle's canonical (sorted-path) file set — drift anchor (OQ-4, spec 03). */
126
+ readonly sourceHash: string;
127
+ /** Pinned rauf coordinate recorded at install, e.g. "rauf@0.6.0"; `null` if `--skip-rauf` (spec 06). */
128
+ readonly raufPin: string | null;
129
+ /** ISO-8601 timestamps. */
130
+ readonly installedAt: string;
131
+ readonly updatedAt: string;
132
+ /** Installed skill ids (the bundle's `skills/*` dir names). */
133
+ readonly skills: string[];
134
+ /** Per-file inventory (copy mode); `sha256` omitted in symlink mode. */
135
+ readonly files: ManifestFile[];
136
+ /** Symlink mode only: the source bundle the namespace dir links to (REQ-SAFE-02). */
137
+ readonly link?: {
138
+ readonly target: string;
139
+ };
140
+ }
141
+ /**
142
+ * The per-file action the planner assigns by diffing source ⇆ destination ⇆ manifest (spec 04).
143
+ * - "create": destination file absent → will be written.
144
+ * - "overwrite": clean prior file whose source bytes changed → will be refreshed.
145
+ * - "skip-modified": destination locally modified (≠ recorded hash AND ≠ what we'd write)
146
+ * → left untouched and reported, unless `--force` (REQ-IDEM-02, REQ-FLAG-04).
147
+ * - "unchanged": destination matches source → no write (REQ-IDEM-01).
148
+ * - "remove": manifest records it but canon no longer has it → removed by `update` (REQ-OPS-02).
149
+ */
150
+ export type FileActionKind = "create" | "overwrite" | "skip-modified" | "unchanged" | "remove";
151
+ /** A single planned file action (REQ-OPS-05). */
152
+ export interface FileAction {
153
+ /** Path relative to the agent's install destination. */
154
+ readonly relpath: string;
155
+ readonly action: FileActionKind;
156
+ }
157
+ /**
158
+ * One agent's complete plan (REQ-OPS-05). `--dry-run` prints exactly this; a real run hands
159
+ * the *same* `PlannedAction` to `apply` (spec 04), guaranteeing "dry-run = real run".
160
+ */
161
+ export interface PlannedAction {
162
+ readonly agent: AgentId;
163
+ readonly scope: Scope;
164
+ readonly mode: Mode;
165
+ readonly files: FileAction[];
166
+ /** Surfaced in the plan/report for visibility (spec 06); not a file action. */
167
+ readonly raufPin?: string | null;
168
+ }
169
+ /** One agent's outcome in a run summary (REQ-OBS-01/03). */
170
+ export interface AgentReport {
171
+ readonly agent: AgentId;
172
+ readonly detected: boolean;
173
+ /** False iff this agent's operation failed (others still proceed — REQ-OBS-03). */
174
+ readonly ok: boolean;
175
+ /** The actions performed (or planned, under `--dry-run`). */
176
+ readonly actions: FileAction[];
177
+ /** Present iff `ok` is false. */
178
+ readonly error?: InstallerError;
179
+ readonly raufPin?: string | null;
180
+ }
181
+ /** The whole-run summary, rendered human-readable or as `--json` (REQ-OBS-01, REQ-DET-05). */
182
+ export interface RunReport {
183
+ readonly subcommand: Subcommand;
184
+ readonly scope: Scope;
185
+ readonly mode: Mode;
186
+ readonly dryRun: boolean;
187
+ readonly agents: AgentReport[];
188
+ /** EXIT.SUCCESS unless any agent failed (FAILURE) or args were invalid (USAGE). */
189
+ readonly exitCode: ExitCode;
190
+ /**
191
+ * Run-level rauf preflight failure (spec 07 §3.2): set when the install/update rauf
192
+ * resolvability check failed. Skills still install (each `AgentReport.ok` stays true) but the
193
+ * run `exitCode` is FAILURE and the renderer surfaces this message. Absent on success.
194
+ *
195
+ * This is the sanctioned run-level field spec 07 §3.2 permits in lieu of the `attachRaufError`
196
+ * hook (see cli.ts run-report assembly). It is part of the `--json` machine surface
197
+ * (REQ-DET-05): consumers reading `renderReport(report, { json: true })` see it verbatim.
198
+ */
199
+ readonly raufError?: InstallerError;
200
+ }
201
+ /**
202
+ * The static detection map (REQ-DET-01, REQ-SCALE-01). Keyed by AgentId; iteration order
203
+ * follows `AGENT_IDS`. Paths for non-claude agents are "best-known" (TQ-1) — re-verify each
204
+ * against the agent's current config-dir/skills-dir convention at implementation (OQ-B).
205
+ *
206
+ * Verified ground truth: every bundle has `skills/` (11 skills), `references/`,
207
+ * `scripts/forge-root.sh`, `agents/`; gemini adds a root `gemini-extension.json`,
208
+ * codex adds `agents/openai.yaml`, cursor uses `.mdc` files.
209
+ */
210
+ export declare const AGENT_TARGETS: Readonly<Record<AgentId, AgentTarget>>;
211
+ /**
212
+ * Minimal integrity check (REQ-OPS-06, spec 03): a located bundle is valid iff `skills/` is a
213
+ * non-empty dir, `scripts/forge-root.sh` exists, and — for gemini only — `gemini-extension.json`
214
+ * exists at the bundle root. Defined here as data so the check is a localized table read.
215
+ */
216
+ export declare const BUNDLE_REQUIRED_PATHS: {
217
+ /** Required of every agent bundle. */
218
+ readonly common: readonly ["skills", "scripts/forge-root.sh"];
219
+ /** Additional per-agent requirements. */
220
+ readonly perAgent: Partial<Record<AgentId, readonly string[]>>;
221
+ };
222
+ /**
223
+ * Stable error codes. Each maps to an actionable message form (REQ-OBS-02) and, at the CLI
224
+ * boundary, an exit code (spec 07): USAGE → EXIT.USAGE (2); everything else → EXIT.FAILURE (1).
225
+ */
226
+ export type ErrorCode = "USAGE" | "SOURCE_MISSING" | "SOURCE_INVALID" | "LOCALLY_MODIFIED" | "WRITE_DENIED" | "PATH_ESCAPE" | "RAUF_UNRESOLVABLE" | "MANIFEST_CORRUPT" | "UNEXPECTED";
227
+ /**
228
+ * A structured, actionable installer error (REQ-OBS-02). `message` must name the agent, the
229
+ * path, and the remedy where applicable; `remedy` optionally carries the suggested fix verbatim.
230
+ */
231
+ export interface InstallerError {
232
+ readonly code: ErrorCode;
233
+ readonly message: string;
234
+ readonly agent?: AgentId;
235
+ readonly path?: string;
236
+ readonly remedy?: string;
237
+ }
238
+ /** Result<T,E> — success carries a value; failure carries a structured error. */
239
+ export type Result<T, E = InstallerError> = {
240
+ readonly ok: true;
241
+ readonly value: T;
242
+ } | {
243
+ readonly ok: false;
244
+ readonly error: E;
245
+ };
246
+ /** Success constructor. */
247
+ export declare const ok: <T>(value: T) => Result<T, never>;
248
+ /** Failure constructor. */
249
+ export declare const err: <E>(error: E) => Result<never, E>;
250
+ /**
251
+ * Parsed, normalized CLI flags (REQ-FLAG-01..05, REQ-DIST-02). Produced by `cli.ts` from
252
+ * `node:util.parseArgs` (spec 07). `agent` undefined ⇒ all detected agents (REQ-DET-03).
253
+ */
254
+ export interface CliFlags {
255
+ readonly agent?: AgentId;
256
+ readonly global: boolean;
257
+ readonly symlink: boolean;
258
+ readonly force: boolean;
259
+ readonly dryRun: boolean;
260
+ readonly yes: boolean;
261
+ readonly json: boolean;
262
+ readonly skipRauf: boolean;
263
+ readonly source?: string;
264
+ }