wingfoil 0.2.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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +334 -0
  3. package/dist/cli/error.d.ts +11 -0
  4. package/dist/cli/error.d.ts.map +1 -0
  5. package/dist/cli/error.js +27 -0
  6. package/dist/cli/error.js.map +1 -0
  7. package/dist/cli/exit.d.ts +19 -0
  8. package/dist/cli/exit.d.ts.map +1 -0
  9. package/dist/cli/exit.js +14 -0
  10. package/dist/cli/exit.js.map +1 -0
  11. package/dist/cli/index.d.ts +28 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +36 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init-command.d.ts +34 -0
  16. package/dist/cli/init-command.d.ts.map +1 -0
  17. package/dist/cli/init-command.js +100 -0
  18. package/dist/cli/init-command.js.map +1 -0
  19. package/dist/cli/mcp-command.d.ts +24 -0
  20. package/dist/cli/mcp-command.d.ts.map +1 -0
  21. package/dist/cli/mcp-command.js +42 -0
  22. package/dist/cli/mcp-command.js.map +1 -0
  23. package/dist/cli/output.d.ts +13 -0
  24. package/dist/cli/output.d.ts.map +1 -0
  25. package/dist/cli/output.js +28 -0
  26. package/dist/cli/output.js.map +1 -0
  27. package/dist/cli/program.d.ts +12 -0
  28. package/dist/cli/program.d.ts.map +1 -0
  29. package/dist/cli/program.js +279 -0
  30. package/dist/cli/program.js.map +1 -0
  31. package/dist/cli/registrar.d.ts +69 -0
  32. package/dist/cli/registrar.d.ts.map +1 -0
  33. package/dist/cli/registrar.js +82 -0
  34. package/dist/cli/registrar.js.map +1 -0
  35. package/dist/cli.d.ts +3 -0
  36. package/dist/cli.d.ts.map +1 -0
  37. package/dist/cli.js +45 -0
  38. package/dist/cli.js.map +1 -0
  39. package/dist/core/approval-authority.d.ts +83 -0
  40. package/dist/core/approval-authority.d.ts.map +1 -0
  41. package/dist/core/approval-authority.js +112 -0
  42. package/dist/core/approval-authority.js.map +1 -0
  43. package/dist/core/builtin-asset.d.ts +71 -0
  44. package/dist/core/builtin-asset.d.ts.map +1 -0
  45. package/dist/core/builtin-asset.js +137 -0
  46. package/dist/core/builtin-asset.js.map +1 -0
  47. package/dist/core/builtin-integrity.d.ts +32 -0
  48. package/dist/core/builtin-integrity.d.ts.map +1 -0
  49. package/dist/core/builtin-integrity.js +148 -0
  50. package/dist/core/builtin-integrity.js.map +1 -0
  51. package/dist/core/confinement.d.ts +57 -0
  52. package/dist/core/confinement.d.ts.map +1 -0
  53. package/dist/core/confinement.js +108 -0
  54. package/dist/core/confinement.js.map +1 -0
  55. package/dist/core/context.d.ts +187 -0
  56. package/dist/core/context.d.ts.map +1 -0
  57. package/dist/core/context.js +192 -0
  58. package/dist/core/context.js.map +1 -0
  59. package/dist/core/directive-assign.d.ts +131 -0
  60. package/dist/core/directive-assign.d.ts.map +1 -0
  61. package/dist/core/directive-assign.js +352 -0
  62. package/dist/core/directive-assign.js.map +1 -0
  63. package/dist/core/directives-list.d.ts +80 -0
  64. package/dist/core/directives-list.d.ts.map +1 -0
  65. package/dist/core/directives-list.js +148 -0
  66. package/dist/core/directives-list.js.map +1 -0
  67. package/dist/core/exit-code.d.ts +85 -0
  68. package/dist/core/exit-code.d.ts.map +1 -0
  69. package/dist/core/exit-code.js +163 -0
  70. package/dist/core/exit-code.js.map +1 -0
  71. package/dist/core/git-identity.d.ts +32 -0
  72. package/dist/core/git-identity.d.ts.map +1 -0
  73. package/dist/core/git-identity.js +72 -0
  74. package/dist/core/git-identity.js.map +1 -0
  75. package/dist/core/index.d.ts +388 -0
  76. package/dist/core/index.d.ts.map +1 -0
  77. package/dist/core/index.js +1331 -0
  78. package/dist/core/index.js.map +1 -0
  79. package/dist/core/init.d.ts +78 -0
  80. package/dist/core/init.d.ts.map +1 -0
  81. package/dist/core/init.js +167 -0
  82. package/dist/core/init.js.map +1 -0
  83. package/dist/core/loaders.d.ts +174 -0
  84. package/dist/core/loaders.d.ts.map +1 -0
  85. package/dist/core/loaders.js +364 -0
  86. package/dist/core/loaders.js.map +1 -0
  87. package/dist/core/memory-add-type.d.ts +43 -0
  88. package/dist/core/memory-add-type.d.ts.map +1 -0
  89. package/dist/core/memory-add-type.js +151 -0
  90. package/dist/core/memory-add-type.js.map +1 -0
  91. package/dist/core/memory-transition.d.ts +111 -0
  92. package/dist/core/memory-transition.d.ts.map +1 -0
  93. package/dist/core/memory-transition.js +269 -0
  94. package/dist/core/memory-transition.js.map +1 -0
  95. package/dist/core/registry.d.ts +189 -0
  96. package/dist/core/registry.d.ts.map +1 -0
  97. package/dist/core/registry.js +66 -0
  98. package/dist/core/registry.js.map +1 -0
  99. package/dist/core/relevance.d.ts +86 -0
  100. package/dist/core/relevance.d.ts.map +1 -0
  101. package/dist/core/relevance.js +258 -0
  102. package/dist/core/relevance.js.map +1 -0
  103. package/dist/core/require-reason.d.ts +27 -0
  104. package/dist/core/require-reason.d.ts.map +1 -0
  105. package/dist/core/require-reason.js +95 -0
  106. package/dist/core/require-reason.js.map +1 -0
  107. package/dist/core/types.d.ts +45 -0
  108. package/dist/core/types.d.ts.map +1 -0
  109. package/dist/core/types.js +22 -0
  110. package/dist/core/types.js.map +1 -0
  111. package/dist/core/usage-error.d.ts +20 -0
  112. package/dist/core/usage-error.d.ts.map +1 -0
  113. package/dist/core/usage-error.js +29 -0
  114. package/dist/core/usage-error.js.map +1 -0
  115. package/dist/core/write-guard.d.ts +99 -0
  116. package/dist/core/write-guard.d.ts.map +1 -0
  117. package/dist/core/write-guard.js +247 -0
  118. package/dist/core/write-guard.js.map +1 -0
  119. package/dist/directives/create.d.ts +47 -0
  120. package/dist/directives/create.d.ts.map +1 -0
  121. package/dist/directives/create.js +82 -0
  122. package/dist/directives/create.js.map +1 -0
  123. package/dist/directives/index.d.ts +20 -0
  124. package/dist/directives/index.d.ts.map +1 -0
  125. package/dist/directives/index.js +32 -0
  126. package/dist/directives/index.js.map +1 -0
  127. package/dist/directives/roles-edit.d.ts +53 -0
  128. package/dist/directives/roles-edit.d.ts.map +1 -0
  129. package/dist/directives/roles-edit.js +286 -0
  130. package/dist/directives/roles-edit.js.map +1 -0
  131. package/dist/directives/schema.d.ts +65 -0
  132. package/dist/directives/schema.d.ts.map +1 -0
  133. package/dist/directives/schema.js +68 -0
  134. package/dist/directives/schema.js.map +1 -0
  135. package/dist/dna/edit.d.ts +61 -0
  136. package/dist/dna/edit.d.ts.map +1 -0
  137. package/dist/dna/edit.js +429 -0
  138. package/dist/dna/edit.js.map +1 -0
  139. package/dist/dna/index.d.ts +24 -0
  140. package/dist/dna/index.d.ts.map +1 -0
  141. package/dist/dna/index.js +59 -0
  142. package/dist/dna/index.js.map +1 -0
  143. package/dist/dna/mutate.d.ts +74 -0
  144. package/dist/dna/mutate.d.ts.map +1 -0
  145. package/dist/dna/mutate.js +318 -0
  146. package/dist/dna/mutate.js.map +1 -0
  147. package/dist/dna/path.d.ts +175 -0
  148. package/dist/dna/path.d.ts.map +1 -0
  149. package/dist/dna/path.js +289 -0
  150. package/dist/dna/path.js.map +1 -0
  151. package/dist/dna/roles.d.ts +57 -0
  152. package/dist/dna/roles.d.ts.map +1 -0
  153. package/dist/dna/roles.js +50 -0
  154. package/dist/dna/roles.js.map +1 -0
  155. package/dist/dna/schema.d.ts +172 -0
  156. package/dist/dna/schema.d.ts.map +1 -0
  157. package/dist/dna/schema.js +182 -0
  158. package/dist/dna/schema.js.map +1 -0
  159. package/dist/dna/set.d.ts +145 -0
  160. package/dist/dna/set.d.ts.map +1 -0
  161. package/dist/dna/set.js +428 -0
  162. package/dist/dna/set.js.map +1 -0
  163. package/dist/mcp/dna-resource.d.ts +16 -0
  164. package/dist/mcp/dna-resource.d.ts.map +1 -0
  165. package/dist/mcp/dna-resource.js +56 -0
  166. package/dist/mcp/dna-resource.js.map +1 -0
  167. package/dist/mcp/index.d.ts +49 -0
  168. package/dist/mcp/index.d.ts.map +1 -0
  169. package/dist/mcp/index.js +60 -0
  170. package/dist/mcp/index.js.map +1 -0
  171. package/dist/mcp/memory-resource.d.ts +17 -0
  172. package/dist/mcp/memory-resource.d.ts.map +1 -0
  173. package/dist/mcp/memory-resource.js +133 -0
  174. package/dist/mcp/memory-resource.js.map +1 -0
  175. package/dist/mcp/prompt.d.ts +48 -0
  176. package/dist/mcp/prompt.d.ts.map +1 -0
  177. package/dist/mcp/prompt.js +176 -0
  178. package/dist/mcp/prompt.js.map +1 -0
  179. package/dist/mcp/read-only.d.ts +42 -0
  180. package/dist/mcp/read-only.d.ts.map +1 -0
  181. package/dist/mcp/read-only.js +93 -0
  182. package/dist/mcp/read-only.js.map +1 -0
  183. package/dist/mcp/registrar.d.ts +54 -0
  184. package/dist/mcp/registrar.d.ts.map +1 -0
  185. package/dist/mcp/registrar.js +72 -0
  186. package/dist/mcp/registrar.js.map +1 -0
  187. package/dist/mcp/server.d.ts +58 -0
  188. package/dist/mcp/server.d.ts.map +1 -0
  189. package/dist/mcp/server.js +64 -0
  190. package/dist/mcp/server.js.map +1 -0
  191. package/dist/mcp/workflow-resource.d.ts +17 -0
  192. package/dist/mcp/workflow-resource.d.ts.map +1 -0
  193. package/dist/mcp/workflow-resource.js +63 -0
  194. package/dist/mcp/workflow-resource.js.map +1 -0
  195. package/dist/memory/add.d.ts +50 -0
  196. package/dist/memory/add.d.ts.map +1 -0
  197. package/dist/memory/add.js +131 -0
  198. package/dist/memory/add.js.map +1 -0
  199. package/dist/memory/audit.d.ts +177 -0
  200. package/dist/memory/audit.d.ts.map +1 -0
  201. package/dist/memory/audit.js +287 -0
  202. package/dist/memory/audit.js.map +1 -0
  203. package/dist/memory/commit-message.d.ts +146 -0
  204. package/dist/memory/commit-message.d.ts.map +1 -0
  205. package/dist/memory/commit-message.js +203 -0
  206. package/dist/memory/commit-message.js.map +1 -0
  207. package/dist/memory/entry.d.ts +56 -0
  208. package/dist/memory/entry.d.ts.map +1 -0
  209. package/dist/memory/entry.js +28 -0
  210. package/dist/memory/entry.js.map +1 -0
  211. package/dist/memory/frontmatter-edit.d.ts +68 -0
  212. package/dist/memory/frontmatter-edit.d.ts.map +1 -0
  213. package/dist/memory/frontmatter-edit.js +333 -0
  214. package/dist/memory/frontmatter-edit.js.map +1 -0
  215. package/dist/memory/git-log.d.ts +32 -0
  216. package/dist/memory/git-log.d.ts.map +1 -0
  217. package/dist/memory/git-log.js +100 -0
  218. package/dist/memory/git-log.js.map +1 -0
  219. package/dist/memory/history.d.ts +142 -0
  220. package/dist/memory/history.d.ts.map +1 -0
  221. package/dist/memory/history.js +250 -0
  222. package/dist/memory/history.js.map +1 -0
  223. package/dist/memory/index.d.ts +58 -0
  224. package/dist/memory/index.d.ts.map +1 -0
  225. package/dist/memory/index.js +107 -0
  226. package/dist/memory/index.js.map +1 -0
  227. package/dist/memory/query.d.ts +150 -0
  228. package/dist/memory/query.d.ts.map +1 -0
  229. package/dist/memory/query.js +307 -0
  230. package/dist/memory/query.js.map +1 -0
  231. package/dist/memory/reject.d.ts +11 -0
  232. package/dist/memory/reject.d.ts.map +1 -0
  233. package/dist/memory/reject.js +32 -0
  234. package/dist/memory/reject.js.map +1 -0
  235. package/dist/memory/schema.d.ts +104 -0
  236. package/dist/memory/schema.d.ts.map +1 -0
  237. package/dist/memory/schema.js +161 -0
  238. package/dist/memory/schema.js.map +1 -0
  239. package/dist/memory/state-machine.d.ts +166 -0
  240. package/dist/memory/state-machine.d.ts.map +1 -0
  241. package/dist/memory/state-machine.js +431 -0
  242. package/dist/memory/state-machine.js.map +1 -0
  243. package/dist/memory/submit.d.ts +11 -0
  244. package/dist/memory/submit.d.ts.map +1 -0
  245. package/dist/memory/submit.js +44 -0
  246. package/dist/memory/submit.js.map +1 -0
  247. package/dist/storage/builtin-directives.d.ts +54 -0
  248. package/dist/storage/builtin-directives.d.ts.map +1 -0
  249. package/dist/storage/builtin-directives.js +152 -0
  250. package/dist/storage/builtin-directives.js.map +1 -0
  251. package/dist/storage/commit.d.ts +92 -0
  252. package/dist/storage/commit.d.ts.map +1 -0
  253. package/dist/storage/commit.js +201 -0
  254. package/dist/storage/commit.js.map +1 -0
  255. package/dist/storage/confinement.d.ts +93 -0
  256. package/dist/storage/confinement.d.ts.map +1 -0
  257. package/dist/storage/confinement.js +164 -0
  258. package/dist/storage/confinement.js.map +1 -0
  259. package/dist/storage/document.d.ts +27 -0
  260. package/dist/storage/document.d.ts.map +1 -0
  261. package/dist/storage/document.js +52 -0
  262. package/dist/storage/document.js.map +1 -0
  263. package/dist/storage/errors.d.ts +36 -0
  264. package/dist/storage/errors.d.ts.map +1 -0
  265. package/dist/storage/errors.js +46 -0
  266. package/dist/storage/errors.js.map +1 -0
  267. package/dist/storage/frontmatter.d.ts +27 -0
  268. package/dist/storage/frontmatter.d.ts.map +1 -0
  269. package/dist/storage/frontmatter.js +37 -0
  270. package/dist/storage/frontmatter.js.map +1 -0
  271. package/dist/storage/git-root.d.ts +17 -0
  272. package/dist/storage/git-root.d.ts.map +1 -0
  273. package/dist/storage/git-root.js +49 -0
  274. package/dist/storage/git-root.js.map +1 -0
  275. package/dist/storage/index.d.ts +29 -0
  276. package/dist/storage/index.d.ts.map +1 -0
  277. package/dist/storage/index.js +66 -0
  278. package/dist/storage/index.js.map +1 -0
  279. package/dist/storage/init-state.d.ts +9 -0
  280. package/dist/storage/init-state.d.ts.map +1 -0
  281. package/dist/storage/init-state.js +24 -0
  282. package/dist/storage/init-state.js.map +1 -0
  283. package/dist/storage/layout.d.ts +51 -0
  284. package/dist/storage/layout.d.ts.map +1 -0
  285. package/dist/storage/layout.js +88 -0
  286. package/dist/storage/layout.js.map +1 -0
  287. package/dist/storage/memory-path.d.ts +72 -0
  288. package/dist/storage/memory-path.d.ts.map +1 -0
  289. package/dist/storage/memory-path.js +127 -0
  290. package/dist/storage/memory-path.js.map +1 -0
  291. package/dist/storage/snapshot.d.ts +16 -0
  292. package/dist/storage/snapshot.d.ts.map +1 -0
  293. package/dist/storage/snapshot.js +72 -0
  294. package/dist/storage/snapshot.js.map +1 -0
  295. package/dist/storage/templates.d.ts +88 -0
  296. package/dist/storage/templates.d.ts.map +1 -0
  297. package/dist/storage/templates.js +422 -0
  298. package/dist/storage/templates.js.map +1 -0
  299. package/dist/validation/error-mapper.d.ts +16 -0
  300. package/dist/validation/error-mapper.d.ts.map +1 -0
  301. package/dist/validation/error-mapper.js +18 -0
  302. package/dist/validation/error-mapper.js.map +1 -0
  303. package/dist/validation/errors.d.ts +69 -0
  304. package/dist/validation/errors.d.ts.map +1 -0
  305. package/dist/validation/errors.js +69 -0
  306. package/dist/validation/errors.js.map +1 -0
  307. package/dist/validation/id.d.ts +16 -0
  308. package/dist/validation/id.d.ts.map +1 -0
  309. package/dist/validation/id.js +177 -0
  310. package/dist/validation/id.js.map +1 -0
  311. package/dist/validation/index.d.ts +18 -0
  312. package/dist/validation/index.d.ts.map +1 -0
  313. package/dist/validation/index.js +37 -0
  314. package/dist/validation/index.js.map +1 -0
  315. package/dist/validation/secret-scan.d.ts +129 -0
  316. package/dist/validation/secret-scan.d.ts.map +1 -0
  317. package/dist/validation/secret-scan.js +367 -0
  318. package/dist/validation/secret-scan.js.map +1 -0
  319. package/dist/validation/two-pass.d.ts +24 -0
  320. package/dist/validation/two-pass.d.ts.map +1 -0
  321. package/dist/validation/two-pass.js +24 -0
  322. package/dist/validation/two-pass.js.map +1 -0
  323. package/dist/validation/warning.d.ts +31 -0
  324. package/dist/validation/warning.d.ts.map +1 -0
  325. package/dist/validation/warning.js +81 -0
  326. package/dist/validation/warning.js.map +1 -0
  327. package/dist/validation/yaml.d.ts +9 -0
  328. package/dist/validation/yaml.d.ts.map +1 -0
  329. package/dist/validation/yaml.js +30 -0
  330. package/dist/validation/yaml.js.map +1 -0
  331. package/dist/workflow/index.d.ts +10 -0
  332. package/dist/workflow/index.d.ts.map +1 -0
  333. package/dist/workflow/index.js +16 -0
  334. package/dist/workflow/index.js.map +1 -0
  335. package/dist/workflow/schema.d.ts +89 -0
  336. package/dist/workflow/schema.d.ts.map +1 -0
  337. package/dist/workflow/schema.js +73 -0
  338. package/dist/workflow/schema.js.map +1 -0
  339. package/package.json +78 -0
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCliCommands = buildCliCommands;
4
+ exports.listRegisteredCliCommands = listRegisteredCliCommands;
5
+ const registry_1 = require("../core/registry");
6
+ const exit_code_1 = require("../core/exit-code");
7
+ const error_1 = require("./error");
8
+ const exit_1 = require("./exit");
9
+ const output_1 = require("./output");
10
+ /**
11
+ * Derive one `CliCommand` per operation in `modules`, in `enumerateOperations`'s deterministic
12
+ * order (REQ-SYS-07). The verb is derived via `deriveVerb` (spec-006 §5).
13
+ */
14
+ function buildCliCommands(modules, options) {
15
+ return (0, registry_1.enumerateOperations)(modules).map(({ module, operation }) => {
16
+ const verb = (0, registry_1.deriveVerb)(module.name, operation.name);
17
+ return {
18
+ noun: module.name,
19
+ verb,
20
+ mutates: operation.mutates,
21
+ flags: operation.flags,
22
+ options: operation.options,
23
+ run: async (formatValue, positionals, flags, optionValues) => {
24
+ if (!(0, output_1.isValidFormat)(formatValue)) {
25
+ (0, exit_1.exitWith)(2, `error: invalid --format value "${formatValue}", expected one of: console, json, yaml`);
26
+ return;
27
+ }
28
+ const format = formatValue;
29
+ let result;
30
+ try {
31
+ // `resolveRoot()` / `buildParams()` run INSIDE the try so an ambient failure — e.g. a
32
+ // `StorageError` from resolving the git root outside a WingFoil project — is rendered
33
+ // through the spec-005 §1 single-exit path (emitError + exitWith), never escaping as an
34
+ // uncaught throw that a top-level handler would stack-dump (bug-002-cli-error-stack-dump).
35
+ const params = options.buildParams({
36
+ moduleName: module.name,
37
+ operationName: operation.name,
38
+ root: options.resolveRoot(),
39
+ // `positional` stays the first positional (single-positional read ops read it); `positionals`
40
+ // is the full list a multi-input op (`dnaSet`) reads (task-025-implement-dna-set).
41
+ positional: positionals?.[0],
42
+ positionals,
43
+ flags,
44
+ // Value-bearing `--{name} <value>` options a data-mutating op reads (task-020's `memoryAdd`
45
+ // reads `type`/`title`/`tags`). Undefined for every op declaring none.
46
+ options: optionValues,
47
+ });
48
+ result = await operation.fn(params);
49
+ }
50
+ catch (error) {
51
+ // Core owns exit-code selection for a thrown error too (spec-008 Consequences): a UsageError
52
+ // (a malformed argument, e.g. `dna set`'s invalid key path) surfaces as exit 2, everything
53
+ // else as 1 — never a bare crash (bug-002-cli-error-stack-dump). task-025-implement-dna-set.
54
+ const { reason, exitCode } = (0, exit_code_1.exitCodeForThrow)(error);
55
+ (0, error_1.emitError)(reason, { format });
56
+ (0, exit_1.exitWith)(exitCode);
57
+ return;
58
+ }
59
+ // Render the outcome, then terminate through the single exit function with the code core
60
+ // selects for this result (`0` success / `1` logic error) — the CLI does not re-decide the
61
+ // `0`/`1` mapping (task-012, spec-005 §1). `2` (usage error) is handled above, pre-core.
62
+ if (result.ok) {
63
+ process.stdout.write((0, output_1.renderSuccess)(result.value, format));
64
+ }
65
+ else {
66
+ (0, error_1.emitError)(result.error.message, { format });
67
+ }
68
+ (0, exit_1.exitWith)((0, exit_code_1.exitCodeForResult)(result));
69
+ },
70
+ };
71
+ });
72
+ }
73
+ /**
74
+ * Every `"{noun} {verb}"` command derived from `commands`, sorted (REQ-SYS-07) — the CLI-side
75
+ * enumeration the REQ-SYS-05 parity test (`test/core/parity.test.ts`) diffs against the MCP Tool
76
+ * list. A flat, no-verb command (`verb === ''`, task-028-implement-paths-category) renders as the
77
+ * bare noun (`"paths"`), not `"paths "` with a trailing space.
78
+ */
79
+ function listRegisteredCliCommands(commands) {
80
+ return commands.map((command) => (command.verb ? `${command.noun} ${command.verb}` : command.noun)).sort();
81
+ }
82
+ //# sourceMappingURL=registrar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registrar.js","sourceRoot":"","sources":["../../src/cli/registrar.ts"],"names":[],"mappings":";;AA2EA,4CA+DC;AAQD,8DAEC;AAlID,+CAAmE;AAEnE,iDAAwE;AAExE,mCAAoC;AACpC,iCAAkC;AAClC,qCAAwD;AA+CxD;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,OAA8B,EAAE,OAA6B;IAC5F,OAAO,IAAA,8BAAmB,EAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE;QAChE,MAAM,IAAI,GAAG,IAAA,qBAAU,EAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACrD,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI;YACJ,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,GAAG,EAAE,KAAK,EACR,WAAmB,EACnB,WAA+B,EAC/B,KAAyC,EACzC,YAA+C,EAC/C,EAAE;gBACF,IAAI,CAAC,IAAA,sBAAa,EAAC,WAAW,CAAC,EAAE,CAAC;oBAChC,IAAA,eAAQ,EAAC,CAAC,EAAE,kCAAkC,WAAW,yCAAyC,CAAC,CAAC;oBACpG,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,WAAW,CAAC;gBAE3B,IAAI,MAA2B,CAAC;gBAChC,IAAI,CAAC;oBACH,sFAAsF;oBACtF,sFAAsF;oBACtF,wFAAwF;oBACxF,2FAA2F;oBAC3F,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;wBACjC,UAAU,EAAE,MAAM,CAAC,IAAI;wBACvB,aAAa,EAAE,SAAS,CAAC,IAAI;wBAC7B,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE;wBAC3B,8FAA8F;wBAC9F,mFAAmF;wBACnF,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;wBAC5B,WAAW;wBACX,KAAK;wBACL,4FAA4F;wBAC5F,uEAAuE;wBACvE,OAAO,EAAE,YAAY;qBACtB,CAAC,CAAC;oBACH,MAAM,GAAG,MAAM,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;gBACtC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,6FAA6F;oBAC7F,2FAA2F;oBAC3F,6FAA6F;oBAC7F,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,4BAAgB,EAAC,KAAK,CAAC,CAAC;oBACrD,IAAA,iBAAS,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;oBAC9B,IAAA,eAAQ,EAAC,QAAQ,CAAC,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,yFAAyF;gBACzF,2FAA2F;gBAC3F,yFAAyF;gBACzF,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;oBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAA,sBAAa,EAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC5D,CAAC;qBAAM,CAAC;oBACN,IAAA,iBAAS,EAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC9C,CAAC;gBACD,IAAA,eAAQ,EAAC,IAAA,6BAAiB,EAAC,MAAM,CAAC,CAAC,CAAC;YACtC,CAAC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,yBAAyB,CAAC,QAA+B;IACvE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7G,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /**
5
+ * The `wingfoil` CLI executable — task-007-npm-distribution (REQ-SYS-09). This is the `bin`
6
+ * entrypoint `package.json`'s `"bin"` field maps to (compiled to `dist/cli.js`); `npm install -g
7
+ * wingfoil` places a shim on `PATH` that runs this file directly (npm chmods it executable at link
8
+ * time — the `#!/usr/bin/env node` shebang above is what makes the shim itself runnable).
9
+ *
10
+ * It wires the real `commander` program built by `./cli/program.ts` (`buildProgram`, task-006) onto
11
+ * a real process invocation, exactly like `test/cli/fixtures/cli-harness.cjs` drives it against a
12
+ * fixture root in tests — the difference here is `resolveRoot` resolves the *real* project root via
13
+ * git-root detection (`./storage/git-root.ts`, spec-011-storage-layout) instead of a fixed fixture
14
+ * path, and the real `process.argv` is parsed instead of a synthetic one.
15
+ *
16
+ * `resolveRoot` is only invoked lazily, once per dispatched command (see `./cli/registrar.ts`'s
17
+ * `run`) — `--help` (Commander built-in) and `--version` (registered by `buildProgram` via
18
+ * `.version()`, see `./cli/program.ts` — bug-001-cli-version-flag) are both handled by `commander`
19
+ * before any command handler runs, so `wingfoil --help` / `wingfoil --version` exit `0`
20
+ * (spec-005-cli-command-contract §1, spec-008-cli-grammar §1) even outside a git repository; only an
21
+ * actual `<noun> <verb>` invocation needs a resolvable git root.
22
+ */
23
+ const program_1 = require("./cli/program");
24
+ const core_1 = require("./core");
25
+ const git_root_1 = require("./storage/git-root");
26
+ (0, program_1.buildProgram)(core_1.CORE_MODULES, {
27
+ resolveRoot: () => (0, git_root_1.resolveProjectRoot)(process.cwd()),
28
+ // task-026's single bare `positional` (`ctx.positional`, read by `dnaShowFn`/`pathsFn` as
29
+ // section/category), task-025's full `positionals` list (read by `dnaSetFn` as `<key> <value>`),
30
+ // task-028's additive `flags` spread (e.g. `paths`'s `--list` -> `list: true`), plus task-020's
31
+ // value-bearing `options` record (read by `memoryAddFn` as `type`/`title`/`tags`). A command with no
32
+ // positional/flags/options is unaffected.
33
+ buildParams: (ctx) => ({ root: ctx.root, positional: ctx.positional, positionals: ctx.positionals, options: ctx.options, ...(ctx.flags ?? {}) }),
34
+ })
35
+ .then((program) => program.parseAsync(process.argv))
36
+ .catch((error) => {
37
+ // Last-resort handler for anything that escapes the per-command spec-005 exit path. Emit only the
38
+ // message as a single `error:` line — never `error.stack`, which would leak a stack trace and
39
+ // absolute internal paths from a published CLI (bug-002-cli-error-stack-dump). The normal
40
+ // no-git-root path is already handled cleanly inside the registrar; this guards the unexpected.
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ process.stderr.write(`error: ${message}\n`);
43
+ process.exit(1);
44
+ });
45
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AACA;;;;;;;;;;;;;;;;;;GAkBG;AACH,2CAA6C;AAC7C,iCAAsC;AACtC,iDAAwD;AAExD,IAAA,sBAAY,EAAC,mBAAY,EAAE;IACzB,WAAW,EAAE,GAAG,EAAE,CAAC,IAAA,6BAAkB,EAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACpD,0FAA0F;IAC1F,iGAAiG;IACjG,gGAAgG;IAChG,qGAAqG;IACrG,0CAA0C;IAC1C,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;CACjJ,CAAC;KACC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KACnD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IACxB,kGAAkG;IAClG,8FAA8F;IAC9F,0FAA0F;IAC1F,gGAAgG;IAChG,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Role-based approval authority (REQ-SEC-03, adr-006-git-identity-role-based-authz —
3
+ * task-040-role-based-approval-authority).
4
+ *
5
+ * adr-006's second point fixes approval authority as role-based, never bound to a named person: a
6
+ * `.wingfoil/dna.yaml` `team.members[]` entry holds one or more `roles` from the canonical
7
+ * `team.roles` catalogue (`src/dna/schema.ts`'s `Team`), and only a member holding the `approver`
8
+ * role may approve a Memory document — uniformly across every type. There is no per-type approver
9
+ * role anywhere in `memory.yaml`/`dna.yaml`: every `approval: { by_role: ... }` step across
10
+ * `.wingfoil/workflows/custom/*.yaml` already gates on the single `approver` role, so this module
11
+ * does the same rather than inventing a per-type mapping no spec defines.
12
+ *
13
+ * "Who is asking" is resolved the same way `requireGitIdentity` (REQ-SEC-01) already establishes
14
+ * attribution: the live git identity configured at the project root (`readGitIdentity`,
15
+ * `./git-identity.ts`) — git identity is WingFoil's sole attribution mechanism (adr-001/adr-006), so
16
+ * authority is checked against the exact same "who" the resulting approval commit will record. This
17
+ * is why `team.agents` is deliberately NOT consulted here: an AI agent has no git identity of its own
18
+ * distinct from whichever human account runs the command, so there is no code-level signal this
19
+ * git-identity-keyed check could use to single an agent out. Structurally preventing agents from
20
+ * holding approval authority is instead a `dna.yaml` fact (`team.agents[].approval_authority: false`)
21
+ * enforced by process/governance (CLAUDE.md §4/§8: "AI agents ... never hold approval authority"), not
22
+ * by this predicate.
23
+ *
24
+ * **"Who is authorized" is read from the COMMITTED `dna.yaml`, never from the working tree**
25
+ * (task-090, `bug-079-uncommitted-dna-yaml-grants-approval-authority`). The two halves of the check
26
+ * therefore take deliberately different baselines, and each takes the only one it can:
27
+ *
28
+ * - *who is asking* — the live git config, which is a local setting git never commits, and which is
29
+ * also exactly what the resulting commit will record as its author;
30
+ * - *who may approve* — `HEAD`'s `.wingfoil/dna.yaml`, because an approval is evidence and evidence
31
+ * is worth what an independent reader can re-derive from the artefact of record. An uncommitted
32
+ * grant used to be enough to put `Approver: … (approver)` into a permanent commit that the
33
+ * repository's own record, at that very commit, contradicted.
34
+ */
35
+ import type { DnaYaml } from '../dna/schema';
36
+ import { type CoreResult } from './types';
37
+ /** The one role that carries approval authority, uniformly across every Memory type (adr-006). */
38
+ export declare const APPROVER_ROLE: "approver";
39
+ /**
40
+ * The roles held by the `dna.yaml` `team.members[]` entry whose `email` matches `email`
41
+ * (case-insensitively — git identities are free-form and casing is not semantically meaningful),
42
+ * or `[]` when `email` is empty or matches no member. Pure lookup — no git/filesystem access; callers
43
+ * needing the live git identity go through {@link requireApprovalAuthority} or `readGitIdentity`
44
+ * (`./git-identity.ts`) themselves.
45
+ */
46
+ export declare function resolveMemberRoles(dna: DnaYaml, email: string): readonly string[];
47
+ /** Whether the `dna.yaml` team member matching `email` holds the {@link APPROVER_ROLE}. */
48
+ export declare function hasApproverRole(dna: DnaYaml, email: string): boolean;
49
+ /**
50
+ * Verify the principal identified by the live git identity at `root` holds the `approver` role — **as
51
+ * the committed `.wingfoil/dna.yaml` records it** — before an approval (`memory.approve`, P1.7) or a
52
+ * rejection (`memory.reject`, P1.8) proceeds. Returns a `CoreResult.error` (code `VALIDATION` — a
53
+ * failed authorization precondition, mapped to exit `1` by `exitCodeForError`, mirroring
54
+ * `requireGitIdentity`'s own precondition shape; `spec-005` §1 reserves `2` for a malformed
55
+ * *invocation*, which none of these refusals is) carrying the exact REQ-SEC-03 fit-criterion message
56
+ * `user not authorized to approve type '<type>'` when the git-configured email holds no `approver`
57
+ * role at `HEAD` (including when it is unset, or matches no `team.members` entry at all); otherwise
58
+ * `ok`. `typeName` is the Memory element type being approved (e.g. `task`, `adr`) — interpolated into
59
+ * the refusal message only, it plays no role in the authority check itself (§ module doc comment: the
60
+ * `approver` role is uniform across types).
61
+ *
62
+ * Three refusals, all fail-closed, all before anything is written:
63
+ *
64
+ * 1. **No authority at `HEAD`** — REQ-SEC-03's message verbatim. When the *working tree* would have
65
+ * granted it, a second sentence names the baseline, so a user whose editor shows them as an
66
+ * approver is told why the tool disagrees instead of being left to argue with the file on their
67
+ * screen. That diagnostic is the half of option (b) — refusing while `dna.yaml` is modified —
68
+ * worth keeping; the refusal itself remains a statement about the repository.
69
+ * 2. **No committed `dna.yaml` at all** — an untracked file, or a repository with no commits: there
70
+ * is no record to read, so no authority can be established.
71
+ * 3. **A committed `dna.yaml` that does not parse or validate** — the baseline is unreadable, which
72
+ * is not the same as "grants nothing", but leads to the same refusal.
73
+ *
74
+ * This function takes `root`, not a `DnaYaml`: a caller cannot hand it a parsed working-tree
75
+ * configuration even by accident, which is what makes the committed baseline a property of the read
76
+ * itself rather than of a precondition someone must remember to run (task-090, AC2).
77
+ *
78
+ * Callers run this AFTER `requireGitIdentity` (REQ-SEC-01) in the same mutating-op pre-flight
79
+ * sequence `dnaSetFn`/`memoryAddFn` (`src/core/index.ts`) establish — an unconfigured identity should
80
+ * surface as REQ-SEC-01's own message, not this one.
81
+ */
82
+ export declare function requireApprovalAuthority(root: string, typeName: string): CoreResult<void>;
83
+ //# sourceMappingURL=approval-authority.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-authority.d.ts","sourceRoot":"","sources":["../../src/core/approval-authority.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAK7C,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3D,kGAAkG;AAClG,eAAO,MAAM,aAAa,EAAG,UAAmB,CAAC;AAEjD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAKjF;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAmCzF"}
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.APPROVER_ROLE = void 0;
4
+ exports.resolveMemberRoles = resolveMemberRoles;
5
+ exports.hasApproverRole = hasApproverRole;
6
+ exports.requireApprovalAuthority = requireApprovalAuthority;
7
+ const validation_1 = require("../validation");
8
+ const git_identity_1 = require("./git-identity");
9
+ const loaders_1 = require("./loaders");
10
+ const types_1 = require("./types");
11
+ /** The one role that carries approval authority, uniformly across every Memory type (adr-006). */
12
+ exports.APPROVER_ROLE = 'approver';
13
+ /**
14
+ * The roles held by the `dna.yaml` `team.members[]` entry whose `email` matches `email`
15
+ * (case-insensitively — git identities are free-form and casing is not semantically meaningful),
16
+ * or `[]` when `email` is empty or matches no member. Pure lookup — no git/filesystem access; callers
17
+ * needing the live git identity go through {@link requireApprovalAuthority} or `readGitIdentity`
18
+ * (`./git-identity.ts`) themselves.
19
+ */
20
+ function resolveMemberRoles(dna, email) {
21
+ if (email.length === 0)
22
+ return [];
23
+ const normalized = email.toLowerCase();
24
+ const member = dna.team.members.find((candidate) => candidate.email?.toLowerCase() === normalized);
25
+ return member?.roles ?? [];
26
+ }
27
+ /** Whether the `dna.yaml` team member matching `email` holds the {@link APPROVER_ROLE}. */
28
+ function hasApproverRole(dna, email) {
29
+ return resolveMemberRoles(dna, email).includes(exports.APPROVER_ROLE);
30
+ }
31
+ /**
32
+ * Whether the working tree's `dna.yaml` grants `email` the `approver` role — a **diagnostic only**,
33
+ * never a decision. It answers one question: is the user looking at a file that says something the
34
+ * repository has not recorded? Any failure to read or parse it is simply "no" (`false`): an
35
+ * unreadable working-tree file must never change the outcome of a check whose answer comes from
36
+ * `HEAD`.
37
+ */
38
+ function workingTreeWouldGrant(root, email) {
39
+ try {
40
+ return hasApproverRole((0, loaders_1.loadDnaYaml)(root), email);
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ /**
47
+ * Verify the principal identified by the live git identity at `root` holds the `approver` role — **as
48
+ * the committed `.wingfoil/dna.yaml` records it** — before an approval (`memory.approve`, P1.7) or a
49
+ * rejection (`memory.reject`, P1.8) proceeds. Returns a `CoreResult.error` (code `VALIDATION` — a
50
+ * failed authorization precondition, mapped to exit `1` by `exitCodeForError`, mirroring
51
+ * `requireGitIdentity`'s own precondition shape; `spec-005` §1 reserves `2` for a malformed
52
+ * *invocation*, which none of these refusals is) carrying the exact REQ-SEC-03 fit-criterion message
53
+ * `user not authorized to approve type '<type>'` when the git-configured email holds no `approver`
54
+ * role at `HEAD` (including when it is unset, or matches no `team.members` entry at all); otherwise
55
+ * `ok`. `typeName` is the Memory element type being approved (e.g. `task`, `adr`) — interpolated into
56
+ * the refusal message only, it plays no role in the authority check itself (§ module doc comment: the
57
+ * `approver` role is uniform across types).
58
+ *
59
+ * Three refusals, all fail-closed, all before anything is written:
60
+ *
61
+ * 1. **No authority at `HEAD`** — REQ-SEC-03's message verbatim. When the *working tree* would have
62
+ * granted it, a second sentence names the baseline, so a user whose editor shows them as an
63
+ * approver is told why the tool disagrees instead of being left to argue with the file on their
64
+ * screen. That diagnostic is the half of option (b) — refusing while `dna.yaml` is modified —
65
+ * worth keeping; the refusal itself remains a statement about the repository.
66
+ * 2. **No committed `dna.yaml` at all** — an untracked file, or a repository with no commits: there
67
+ * is no record to read, so no authority can be established.
68
+ * 3. **A committed `dna.yaml` that does not parse or validate** — the baseline is unreadable, which
69
+ * is not the same as "grants nothing", but leads to the same refusal.
70
+ *
71
+ * This function takes `root`, not a `DnaYaml`: a caller cannot hand it a parsed working-tree
72
+ * configuration even by accident, which is what makes the committed baseline a property of the read
73
+ * itself rather than of a precondition someone must remember to run (task-090, AC2).
74
+ *
75
+ * Callers run this AFTER `requireGitIdentity` (REQ-SEC-01) in the same mutating-op pre-flight
76
+ * sequence `dnaSetFn`/`memoryAddFn` (`src/core/index.ts`) establish — an unconfigured identity should
77
+ * surface as REQ-SEC-01's own message, not this one.
78
+ */
79
+ function requireApprovalAuthority(root, typeName) {
80
+ const { email } = (0, git_identity_1.readGitIdentity)(root);
81
+ let committed;
82
+ try {
83
+ committed = (0, loaders_1.loadDnaYamlAtHead)(root);
84
+ }
85
+ catch (error) {
86
+ if (!(error instanceof validation_1.ValidationError))
87
+ throw error;
88
+ return (0, types_1.coreErr)({
89
+ code: 'VALIDATION',
90
+ message: `cannot resolve approval authority: the committed '${loaders_1.DNA_YAML_PATH}' (at HEAD) is not readable as DNA: ` +
91
+ `${error.message}. Approval authority is read from the committed configuration, so the committed file ` +
92
+ 'must be valid; fix it and commit the fix, then retry.',
93
+ details: { issues: error.issues },
94
+ });
95
+ }
96
+ if (committed === null) {
97
+ return (0, types_1.coreErr)({
98
+ code: 'VALIDATION',
99
+ message: `cannot resolve approval authority: '${loaders_1.DNA_YAML_PATH}' is not committed at HEAD. Approval authority is a ` +
100
+ 'property of the repository, not of a working tree (adr-006), so an approval can only rest on roles some ' +
101
+ `commit records; commit '${loaders_1.DNA_YAML_PATH}' first, then retry.`,
102
+ });
103
+ }
104
+ if (hasApproverRole(committed, email))
105
+ return (0, types_1.coreOk)(undefined);
106
+ const uncommittedGrant = workingTreeWouldGrant(root, email)
107
+ ? ` — the working tree's '${loaders_1.DNA_YAML_PATH}' grants it, but that change is not committed, and approval authority ` +
108
+ `is read from the committed configuration (adr-006); commit '${loaders_1.DNA_YAML_PATH}' first, then retry`
109
+ : '';
110
+ return (0, types_1.coreErr)({ code: 'VALIDATION', message: `user not authorized to approve type '${typeName}'${uncommittedGrant}` });
111
+ }
112
+ //# sourceMappingURL=approval-authority.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-authority.js","sourceRoot":"","sources":["../../src/core/approval-authority.ts"],"names":[],"mappings":";;;AAmDA,gDAKC;AAGD,0CAEC;AAkDD,4DAmCC;AA/GD,8CAAgD;AAEhD,iDAAiD;AACjD,uCAA0E;AAC1E,mCAA2D;AAE3D,kGAAkG;AACrF,QAAA,aAAa,GAAG,UAAmB,CAAC;AAEjD;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,GAAY,EAAE,KAAa;IAC5D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC;IACnG,OAAO,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,2FAA2F;AAC3F,SAAgB,eAAe,CAAC,GAAY,EAAE,KAAa;IACzD,OAAO,kBAAkB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,qBAAa,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,qBAAqB,CAAC,IAAY,EAAE,KAAa;IACxD,IAAI,CAAC;QACH,OAAO,eAAe,CAAC,IAAA,qBAAW,EAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,SAAgB,wBAAwB,CAAC,IAAY,EAAE,QAAgB;IACrE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAA,8BAAe,EAAC,IAAI,CAAC,CAAC;IAExC,IAAI,SAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,SAAS,GAAG,IAAA,2BAAiB,EAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,4BAAe,CAAC;YAAE,MAAM,KAAK,CAAC;QACrD,OAAO,IAAA,eAAO,EAAC;YACb,IAAI,EAAE,YAAY;YAClB,OAAO,EACL,qDAAqD,uBAAa,sCAAsC;gBACxG,GAAG,KAAK,CAAC,OAAO,uFAAuF;gBACvG,uDAAuD;YACzD,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;SAClC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,IAAA,eAAO,EAAC;YACb,IAAI,EAAE,YAAY;YAClB,OAAO,EACL,uCAAuC,uBAAa,sDAAsD;gBAC1G,0GAA0G;gBAC1G,2BAA2B,uBAAa,sBAAsB;SACjE,CAAC,CAAC;IACL,CAAC;IAED,IAAI,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC;QAAE,OAAO,IAAA,cAAM,EAAO,SAAS,CAAC,CAAC;IAEtE,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC;QACzD,CAAC,CAAC,0BAA0B,uBAAa,wEAAwE;YAC/G,+DAA+D,uBAAa,qBAAqB;QACnG,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,IAAA,eAAO,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,wCAAwC,QAAQ,IAAI,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAC1H,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Immutable built-in assets pre-flight check (REQ-SEC-07 — task-042-immutable-builtin-assets).
3
+ *
4
+ * **The rule is REQ-SEC-07's, the layout is spec-011's.** REQ-SEC-07 states that built-in directives
5
+ * and built-in workflow templates cannot be removed; `spec-011-storage-layout` supplies the structure
6
+ * that rule is expressed over — the `directives/{built-in,custom}/` and `workflows/{built-in,custom}/`
7
+ * split, where `built-in/` is "reserved for the official … templates shipped by the `wingfoil` npm
8
+ * package" (empty today, `.gitkeep` only) and `custom/` holds every asset that exists right now. Only
9
+ * the removability rule comes from REQ-SEC-07; spec-011 never speaks about removal or protection.
10
+ *
11
+ * Classification is **structural** (which subdirectory holds the file), not the directive
12
+ * frontmatter's `kind:` field — and that is the one point spec-011 does settle: the six P3.8 template
13
+ * stand-ins live under `custom/` while each is "authored with `kind: custom`, `ref: [P3.8]`", so
14
+ * `kind:` demonstrably does not track the subdirectory and cannot be the discriminator. Location is
15
+ * the only discriminator spec-011 defines.
16
+ *
17
+ * **This check is an ALLOW-LIST and fails CLOSED.** It returns "removable" only for a path it
18
+ * positively recognises as `{directives|workflows}/custom/…`; every other shape — a built-in path, an
19
+ * unknown shape, an empty string, a `.`/`..` traversal, an absolute path, a doubled separator, a
20
+ * different pillar's tree — is refused. A deny-list on the literal segment `built-in` was the
21
+ * first-pass defect: `src/core/loaders.ts` builds a directive's stored path with the **platform**
22
+ * separator (`join('directives', relativePath)`), so on Windows a built-in arrives as
23
+ * `directives\built-in\testing.md`, which a forward-slash-only split classifies as *not* built-in and
24
+ * would therefore have allowed `directive remove` to delete. Both separators are accepted as
25
+ * separators here for exactly that reason.
26
+ *
27
+ * `.`/`..` segments are **refused, not resolved**: resolving them textually is not equivalent to
28
+ * resolving them on a filesystem (a symlinked `custom/` aliasing `built-in/` defeats any string-level
29
+ * normalisation), so an ambiguous shape is rejected rather than guessed at. Callers pass the
30
+ * `.wingfoil`-relative path of an asset they already located; that path is never ambiguous.
31
+ *
32
+ * This lives in `src/core` (not `src/directives`/`src/workflow`) because REQ-SEC-07 crosscuts both
33
+ * pillars identically and both the CLI and MCP surfaces must enforce it the same way (REQ-SYS-05) —
34
+ * the same reasoning `requireGitIdentity` (REQ-SEC-01, task-014) already established for a
35
+ * cross-cutting mutating-op pre-flight. Neither `directive remove` nor `workflow remove` exists yet in
36
+ * `CORE_MODULES` (spec-006 §3 reserves both as future `mutates: true` operations); per the same
37
+ * precedent (task-014, task-016), this task delivers the shared, fully-tested primitive now and defers
38
+ * wiring it into the actual mutating op to the dependent task (task-052-directive-remove for
39
+ * `directive.remove`; a future task for `workflow.remove`).
40
+ *
41
+ * **Scope:** this module delivers REQ-SEC-07's *first* clause only. The second — "removal of a
42
+ * still-referenced custom asset is rejected naming the referrer" — is deliberately not implemented
43
+ * here; per `dl-030-req-sec-07-referenced-asset-ownership` (`ready`) the directive half belongs to
44
+ * `task-052-directive-remove` and the workflow half (P4.9) is carried to the next `release-planning`
45
+ * run. `requireCustomAsset` is therefore the FIRST of two checks a remove op runs, never the only one.
46
+ */
47
+ import { type CoreResult } from './types';
48
+ /** The two pillars REQ-SEC-07 protects — `built-in/` is immutable in both. */
49
+ export type AssetKind = 'directive' | 'workflow';
50
+ /**
51
+ * The allow-list: true only when `relativePath` is positively recognised as a removable `custom/`
52
+ * asset of this `kind` — e.g. `directives/custom/legacy-rule.md`, `workflows/custom/arch-review.yaml`,
53
+ * or a file nested deeper under `custom/`. Either separator is accepted (see the module doc-comment on
54
+ * `loaders.ts`'s platform `join`). A file merely *named* `built-in-notes.md` under `custom/` is
55
+ * removable — it is the `custom` **segment** that decides, never a substring. Everything else is
56
+ * false, including a `kind` mismatch: a workflow path handed to `'directive'` is not removable through
57
+ * the directive surface. Pure predicate — no filesystem access, no wall-clock/randomness (REQ-SYS-07).
58
+ */
59
+ export declare function isRemovableCustomAssetPath(kind: AssetKind, relativePath: string): boolean;
60
+ /**
61
+ * REQ-SEC-07 pre-flight: allow a removal only for a recognised `custom/` asset of `kind`. Mirrors
62
+ * `requireGitIdentity`'s shape exactly (`CoreResult<void>`, pure, safe to call before any read/write)
63
+ * — intended as the FIRST check `directiveRemove`/`workflowRemove` run, before any reference check,
64
+ * so a protected or unrecognised path is rejected on structure alone. Returns a `VALIDATION`
65
+ * `CoreResult.error` (a failed precondition, like the sibling pre-flights `requireGitIdentity` and
66
+ * `requireApprovalAuthority`; exit `1` via `exitCodeForError`) carrying REQ-SEC-07's exact fit-criterion
67
+ * wording for a declared built-in, or a generic `cannot remove '<path>': …` refusal — shaped after
68
+ * P3.3's own `cannot remove '<name>': …` message — for any other unrecognised shape; otherwise `ok`.
69
+ */
70
+ export declare function requireCustomAsset(kind: AssetKind, relativePath: string): CoreResult<void>;
71
+ //# sourceMappingURL=builtin-asset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-asset.d.ts","sourceRoot":"","sources":["../../src/core/builtin-asset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAiDjD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAEzF;AAcD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAQ1F"}
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isRemovableCustomAssetPath = isRemovableCustomAssetPath;
4
+ exports.requireCustomAsset = requireCustomAsset;
5
+ /**
6
+ * Immutable built-in assets pre-flight check (REQ-SEC-07 — task-042-immutable-builtin-assets).
7
+ *
8
+ * **The rule is REQ-SEC-07's, the layout is spec-011's.** REQ-SEC-07 states that built-in directives
9
+ * and built-in workflow templates cannot be removed; `spec-011-storage-layout` supplies the structure
10
+ * that rule is expressed over — the `directives/{built-in,custom}/` and `workflows/{built-in,custom}/`
11
+ * split, where `built-in/` is "reserved for the official … templates shipped by the `wingfoil` npm
12
+ * package" (empty today, `.gitkeep` only) and `custom/` holds every asset that exists right now. Only
13
+ * the removability rule comes from REQ-SEC-07; spec-011 never speaks about removal or protection.
14
+ *
15
+ * Classification is **structural** (which subdirectory holds the file), not the directive
16
+ * frontmatter's `kind:` field — and that is the one point spec-011 does settle: the six P3.8 template
17
+ * stand-ins live under `custom/` while each is "authored with `kind: custom`, `ref: [P3.8]`", so
18
+ * `kind:` demonstrably does not track the subdirectory and cannot be the discriminator. Location is
19
+ * the only discriminator spec-011 defines.
20
+ *
21
+ * **This check is an ALLOW-LIST and fails CLOSED.** It returns "removable" only for a path it
22
+ * positively recognises as `{directives|workflows}/custom/…`; every other shape — a built-in path, an
23
+ * unknown shape, an empty string, a `.`/`..` traversal, an absolute path, a doubled separator, a
24
+ * different pillar's tree — is refused. A deny-list on the literal segment `built-in` was the
25
+ * first-pass defect: `src/core/loaders.ts` builds a directive's stored path with the **platform**
26
+ * separator (`join('directives', relativePath)`), so on Windows a built-in arrives as
27
+ * `directives\built-in\testing.md`, which a forward-slash-only split classifies as *not* built-in and
28
+ * would therefore have allowed `directive remove` to delete. Both separators are accepted as
29
+ * separators here for exactly that reason.
30
+ *
31
+ * `.`/`..` segments are **refused, not resolved**: resolving them textually is not equivalent to
32
+ * resolving them on a filesystem (a symlinked `custom/` aliasing `built-in/` defeats any string-level
33
+ * normalisation), so an ambiguous shape is rejected rather than guessed at. Callers pass the
34
+ * `.wingfoil`-relative path of an asset they already located; that path is never ambiguous.
35
+ *
36
+ * This lives in `src/core` (not `src/directives`/`src/workflow`) because REQ-SEC-07 crosscuts both
37
+ * pillars identically and both the CLI and MCP surfaces must enforce it the same way (REQ-SYS-05) —
38
+ * the same reasoning `requireGitIdentity` (REQ-SEC-01, task-014) already established for a
39
+ * cross-cutting mutating-op pre-flight. Neither `directive remove` nor `workflow remove` exists yet in
40
+ * `CORE_MODULES` (spec-006 §3 reserves both as future `mutates: true` operations); per the same
41
+ * precedent (task-014, task-016), this task delivers the shared, fully-tested primitive now and defers
42
+ * wiring it into the actual mutating op to the dependent task (task-052-directive-remove for
43
+ * `directive.remove`; a future task for `workflow.remove`).
44
+ *
45
+ * **Scope:** this module delivers REQ-SEC-07's *first* clause only. The second — "removal of a
46
+ * still-referenced custom asset is rejected naming the referrer" — is deliberately not implemented
47
+ * here; per `dl-030-req-sec-07-referenced-asset-ownership` (`ready`) the directive half belongs to
48
+ * `task-052-directive-remove` and the workflow half (P4.9) is carried to the next `release-planning`
49
+ * run. `requireCustomAsset` is therefore the FIRST of two checks a remove op runs, never the only one.
50
+ */
51
+ const types_1 = require("./types");
52
+ /** The `.wingfoil/` top-level directory that holds each pillar's assets (spec-011 layout). */
53
+ const ASSET_ROOT_SEGMENT = {
54
+ directive: 'directives',
55
+ workflow: 'workflows',
56
+ };
57
+ /** The spec-011 subdirectory holding removable assets — the ONE segment this allow-list accepts. */
58
+ const CUSTOM_SEGMENT = 'custom';
59
+ /** The spec-011 subdirectory reserved for official, npm-shipped templates. */
60
+ const BUILT_IN_SEGMENT = 'built-in';
61
+ /**
62
+ * Exact refusal wording required by REQ-SEC-07's fit criterion — **do not reword**: the BDD scenarios
63
+ * "Error - removing a built-in directive" (`p3-directives/P3.3-directive-remove.feature`) and
64
+ * "Error - removing a built-in workflow template" (`p4-workflow/P4.9-workflow-remove.feature`) assert
65
+ * these strings verbatim. Held in one named constant for the same reason `git-identity.ts` holds
66
+ * `IDENTITY_ERROR`: a fit-criterion string must be greppable against the `.feature` file that pins it,
67
+ * not reconstructed at each use site.
68
+ */
69
+ const BUILT_IN_REMOVAL_ERROR = {
70
+ directive: 'built-in directives cannot be removed',
71
+ workflow: 'built-in workflows cannot be removed',
72
+ };
73
+ /**
74
+ * Split a stored asset path into segments on **either** separator, without collapsing runs — so a
75
+ * doubled separator yields an empty segment and an absolute path yields an empty leading segment,
76
+ * both of which the shape check below then rejects.
77
+ */
78
+ function segmentsOf(relativePath) {
79
+ return relativePath.split(/[\\/]/);
80
+ }
81
+ /**
82
+ * True when `relativePath` is exactly the shape `{kind's root}/{subdirectory}/…/<file>`: at least
83
+ * three segments, no empty/`.`/`..` segment, the expected pillar root first and `subdirectory`
84
+ * second. Case-sensitive on purpose — a differently-cased segment is not recognised, and therefore
85
+ * (for the `custom` allow-list) refused rather than trusted on a case-insensitive filesystem.
86
+ */
87
+ function isPillarAssetPath(kind, relativePath, subdirectory) {
88
+ const segments = segmentsOf(relativePath);
89
+ if (segments.length < 3)
90
+ return false;
91
+ if (!segments.every((segment) => segment !== '' && segment !== '.' && segment !== '..'))
92
+ return false;
93
+ return segments[0] === ASSET_ROOT_SEGMENT[kind] && segments[1] === subdirectory;
94
+ }
95
+ /**
96
+ * The allow-list: true only when `relativePath` is positively recognised as a removable `custom/`
97
+ * asset of this `kind` — e.g. `directives/custom/legacy-rule.md`, `workflows/custom/arch-review.yaml`,
98
+ * or a file nested deeper under `custom/`. Either separator is accepted (see the module doc-comment on
99
+ * `loaders.ts`'s platform `join`). A file merely *named* `built-in-notes.md` under `custom/` is
100
+ * removable — it is the `custom` **segment** that decides, never a substring. Everything else is
101
+ * false, including a `kind` mismatch: a workflow path handed to `'directive'` is not removable through
102
+ * the directive surface. Pure predicate — no filesystem access, no wall-clock/randomness (REQ-SYS-07).
103
+ */
104
+ function isRemovableCustomAssetPath(kind, relativePath) {
105
+ return isPillarAssetPath(kind, relativePath, CUSTOM_SEGMENT);
106
+ }
107
+ /**
108
+ * True when `relativePath` positively declares itself a `built-in/` asset of this `kind`. Used
109
+ * **only** to choose the refusal wording, never to decide whether to refuse — that decision belongs
110
+ * entirely to {@link isRemovableCustomAssetPath}, so this predicate cannot reintroduce a fail-open
111
+ * path. A shape that is neither clearly `custom/` nor clearly `built-in/` (a traversal, an odd
112
+ * casing) is still refused; it just gets the generic wording instead of REQ-SEC-07's, because
113
+ * asserting "this is a built-in" about a path we did not recognise would be a claim we cannot make.
114
+ */
115
+ function declaresBuiltInPath(kind, relativePath) {
116
+ return isPillarAssetPath(kind, relativePath, BUILT_IN_SEGMENT);
117
+ }
118
+ /**
119
+ * REQ-SEC-07 pre-flight: allow a removal only for a recognised `custom/` asset of `kind`. Mirrors
120
+ * `requireGitIdentity`'s shape exactly (`CoreResult<void>`, pure, safe to call before any read/write)
121
+ * — intended as the FIRST check `directiveRemove`/`workflowRemove` run, before any reference check,
122
+ * so a protected or unrecognised path is rejected on structure alone. Returns a `VALIDATION`
123
+ * `CoreResult.error` (a failed precondition, like the sibling pre-flights `requireGitIdentity` and
124
+ * `requireApprovalAuthority`; exit `1` via `exitCodeForError`) carrying REQ-SEC-07's exact fit-criterion
125
+ * wording for a declared built-in, or a generic `cannot remove '<path>': …` refusal — shaped after
126
+ * P3.3's own `cannot remove '<name>': …` message — for any other unrecognised shape; otherwise `ok`.
127
+ */
128
+ function requireCustomAsset(kind, relativePath) {
129
+ if (isRemovableCustomAssetPath(kind, relativePath)) {
130
+ return (0, types_1.coreOk)(undefined);
131
+ }
132
+ const message = declaresBuiltInPath(kind, relativePath)
133
+ ? BUILT_IN_REMOVAL_ERROR[kind]
134
+ : `cannot remove '${relativePath}': not a ${kind} under '${ASSET_ROOT_SEGMENT[kind]}/${CUSTOM_SEGMENT}/'`;
135
+ return (0, types_1.coreErr)({ code: 'VALIDATION', message });
136
+ }
137
+ //# sourceMappingURL=builtin-asset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-asset.js","sourceRoot":"","sources":["../../src/core/builtin-asset.ts"],"names":[],"mappings":";;AA2GA,gEAEC;AAwBD,gDAQC;AA7ID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,mCAA2D;AAK3D,8FAA8F;AAC9F,MAAM,kBAAkB,GAA8B;IACpD,SAAS,EAAE,YAAY;IACvB,QAAQ,EAAE,WAAW;CACtB,CAAC;AAEF,oGAAoG;AACpG,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEhC,8EAA8E;AAC9E,MAAM,gBAAgB,GAAG,UAAU,CAAC;AAEpC;;;;;;;GAOG;AACH,MAAM,sBAAsB,GAA8B;IACxD,SAAS,EAAE,uCAAuC;IAClD,QAAQ,EAAE,sCAAsC;CACjD,CAAC;AAEF;;;;GAIG;AACH,SAAS,UAAU,CAAC,YAAoB;IACtC,OAAO,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,IAAe,EAAE,YAAoB,EAAE,YAAoB;IACpF,MAAM,QAAQ,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IAC1C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACtG,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC;AAClF,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,0BAA0B,CAAC,IAAe,EAAE,YAAoB;IAC9E,OAAO,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,mBAAmB,CAAC,IAAe,EAAE,YAAoB;IAChE,OAAO,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,kBAAkB,CAAC,IAAe,EAAE,YAAoB;IACtE,IAAI,0BAA0B,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC;QACnD,OAAO,IAAA,cAAM,EAAO,SAAS,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,EAAE,YAAY,CAAC;QACrD,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC;QAC9B,CAAC,CAAC,kBAAkB,YAAY,YAAY,IAAI,WAAW,kBAAkB,CAAC,IAAI,CAAC,IAAI,cAAc,IAAI,CAAC;IAC5G,OAAO,IAAA,eAAO,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type BuiltinTemplateKind, type BuiltinTemplateSource } from '../storage';
2
+ export type { BuiltinTemplateKind, BuiltinTemplateSource } from '../storage';
3
+ /**
4
+ * The first built-in template source {@link verifyBuiltinTemplates} found invalid: its `name`/`kind`
5
+ * (identifying which source failed) plus the exact, already-formatted abort `message` — either the
6
+ * P3.8 directive wording or the P4.17 workflow wording, per REQ-SEC-10's fit criterion.
7
+ */
8
+ export interface BuiltinIntegrityFailure {
9
+ readonly name: string;
10
+ readonly kind: BuiltinTemplateKind;
11
+ readonly message: string;
12
+ }
13
+ /**
14
+ * Schema-check every `sources` entry, in list order (REQ-SYS-07: deterministic, no unordered
15
+ * iteration), and return the FIRST one that fails — or `null` when every source is valid (including
16
+ * the trivial, always-passing case of an empty list, which is what `src/storage/templates.ts`'s
17
+ * `builtinTemplateSources` derives from the P1.1 minimal skeleton, whose built-in directory holds only
18
+ * a `.gitkeep`; `templateScaffold` yields the six P3.8 built-in directives since
19
+ * `task-057-builtin-directive-templates`).
20
+ *
21
+ * FAILS CLOSED on an unrecognized `kind`: a source whose kind has no {@link INTEGRITY_POLICY} entry is
22
+ * reported as a failure ({@link unknownKindMessage}) rather than skipped or thrown on — it cannot be
23
+ * checked, so it must not be installed.
24
+ *
25
+ * Callers — `initWingfoilProject` AND `initWingfoilStorage`, both in `src/core/init.ts`; every
26
+ * `initStorage` write path there is one — MUST run this before writing any file: REQ-SEC-10's fit
27
+ * criterion requires the abort to happen "before writing partial assets". This function itself never
28
+ * writes or reads from disk, so calling it before the scaffold write is sufficient to satisfy that
29
+ * ordering.
30
+ */
31
+ export declare function verifyBuiltinTemplates(sources: readonly BuiltinTemplateSource[]): BuiltinIntegrityFailure | null;
32
+ //# sourceMappingURL=builtin-integrity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-integrity.d.ts","sourceRoot":"","sources":["../../src/core/builtin-integrity.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAItG,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE7E;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAuFD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACxC,uBAAuB,GAAG,IAAI,CAehC"}