opkg 0.6.1 → 0.7.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 (487) hide show
  1. package/README.md +109 -186
  2. package/assets/openpackage_ascii_dark.png +0 -0
  3. package/assets/openpackage_ascii_light.png +0 -0
  4. package/dist/commands/add.js +34 -10
  5. package/dist/commands/add.js.map +1 -1
  6. package/dist/commands/apply.js +16 -0
  7. package/dist/commands/apply.js.map +1 -0
  8. package/dist/commands/delete.js +1 -1
  9. package/dist/commands/delete.js.map +1 -1
  10. package/dist/commands/install.js +177 -8
  11. package/dist/commands/install.js.map +1 -1
  12. package/dist/commands/list.js +2 -2
  13. package/dist/commands/list.js.map +1 -1
  14. package/dist/commands/login.js +1 -1
  15. package/dist/commands/login.js.map +1 -1
  16. package/dist/commands/logout.js +1 -1
  17. package/dist/commands/logout.js.map +1 -1
  18. package/dist/commands/new.js +125 -0
  19. package/dist/commands/new.js.map +1 -0
  20. package/dist/commands/pack.js +7 -13
  21. package/dist/commands/pack.js.map +1 -1
  22. package/dist/commands/pull.js +1 -1
  23. package/dist/commands/pull.js.map +1 -1
  24. package/dist/commands/push.js +1 -1
  25. package/dist/commands/push.js.map +1 -1
  26. package/dist/commands/remove.js +63 -0
  27. package/dist/commands/remove.js.map +1 -0
  28. package/dist/commands/save.js +11 -17
  29. package/dist/commands/save.js.map +1 -1
  30. package/dist/commands/set.js +33 -0
  31. package/dist/commands/set.js.map +1 -0
  32. package/dist/commands/show.js +16 -94
  33. package/dist/commands/show.js.map +1 -1
  34. package/dist/commands/status.js +26 -701
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/commands/uninstall.js +14 -427
  37. package/dist/commands/uninstall.js.map +1 -1
  38. package/dist/constants/index.js +72 -16
  39. package/dist/constants/index.js.map +1 -1
  40. package/dist/core/add/add-conflict-handler.js +1 -8
  41. package/dist/core/add/add-conflict-handler.js.map +1 -1
  42. package/dist/core/add/add-pipeline.js +12 -10
  43. package/dist/core/add/add-pipeline.js.map +1 -1
  44. package/dist/core/add/add-to-source-pipeline.js +123 -0
  45. package/dist/core/add/add-to-source-pipeline.js.map +1 -0
  46. package/dist/core/add/package-index-updater.js +77 -78
  47. package/dist/core/add/package-index-updater.js.map +1 -1
  48. package/dist/core/add/platform-path-transformer.js +6 -4
  49. package/dist/core/add/platform-path-transformer.js.map +1 -1
  50. package/dist/core/add/source-collector.js +2 -3
  51. package/dist/core/add/source-collector.js.map +1 -1
  52. package/dist/core/apply/apply-pipeline.js +110 -0
  53. package/dist/core/apply/apply-pipeline.js.map +1 -0
  54. package/dist/core/dependency-resolver.js +263 -21
  55. package/dist/core/dependency-resolver.js.map +1 -1
  56. package/dist/core/discovery/file-discovery.js +1 -2
  57. package/dist/core/discovery/file-discovery.js.map +1 -1
  58. package/dist/core/discovery/platform-files-discovery.js +33 -18
  59. package/dist/core/discovery/platform-files-discovery.js.map +1 -1
  60. package/dist/core/flows/flow-executor.js +974 -0
  61. package/dist/core/flows/flow-executor.js.map +1 -0
  62. package/dist/core/flows/flow-inverter.js +442 -0
  63. package/dist/core/flows/flow-inverter.js.map +1 -0
  64. package/dist/core/flows/flow-key-extractor.js +101 -0
  65. package/dist/core/flows/flow-key-extractor.js.map +1 -0
  66. package/dist/core/flows/flow-key-mapper.js +382 -0
  67. package/dist/core/flows/flow-key-mapper.js.map +1 -0
  68. package/dist/core/flows/flow-transforms.js +632 -0
  69. package/dist/core/flows/flow-transforms.js.map +1 -0
  70. package/dist/core/flows/map-pipeline/context.js +73 -0
  71. package/dist/core/flows/map-pipeline/context.js.map +1 -0
  72. package/dist/core/flows/map-pipeline/index.js +156 -0
  73. package/dist/core/flows/map-pipeline/index.js.map +1 -0
  74. package/dist/core/flows/map-pipeline/operations/copy.js +104 -0
  75. package/dist/core/flows/map-pipeline/operations/copy.js.map +1 -0
  76. package/dist/core/flows/map-pipeline/operations/pipe.js +70 -0
  77. package/dist/core/flows/map-pipeline/operations/pipe.js.map +1 -0
  78. package/dist/core/flows/map-pipeline/operations/rename.js +102 -0
  79. package/dist/core/flows/map-pipeline/operations/rename.js.map +1 -0
  80. package/dist/core/flows/map-pipeline/operations/set.js +50 -0
  81. package/dist/core/flows/map-pipeline/operations/set.js.map +1 -0
  82. package/dist/core/flows/map-pipeline/operations/switch.js +79 -0
  83. package/dist/core/flows/map-pipeline/operations/switch.js.map +1 -0
  84. package/dist/core/flows/map-pipeline/operations/transform.js +543 -0
  85. package/dist/core/flows/map-pipeline/operations/transform.js.map +1 -0
  86. package/dist/core/flows/map-pipeline/operations/unset.js +65 -0
  87. package/dist/core/flows/map-pipeline/operations/unset.js.map +1 -0
  88. package/dist/core/flows/map-pipeline/types.js +8 -0
  89. package/dist/core/flows/map-pipeline/types.js.map +1 -0
  90. package/dist/core/flows/map-pipeline/utils.js +278 -0
  91. package/dist/core/flows/map-pipeline/utils.js.map +1 -0
  92. package/dist/core/flows/platform-converter.js +328 -0
  93. package/dist/core/flows/platform-converter.js.map +1 -0
  94. package/dist/core/flows/source-resolver.js +192 -0
  95. package/dist/core/flows/source-resolver.js.map +1 -0
  96. package/dist/core/flows/toml-domain-transforms.js +23 -0
  97. package/dist/core/flows/toml-domain-transforms.js.map +1 -0
  98. package/dist/core/install/bulk-install-pipeline.js +68 -7
  99. package/dist/core/install/bulk-install-pipeline.js.map +1 -1
  100. package/dist/core/install/canonical-plan.js +3 -3
  101. package/dist/core/install/canonical-plan.js.map +1 -1
  102. package/dist/core/install/dry-run.js +3 -3
  103. package/dist/core/install/dry-run.js.map +1 -1
  104. package/dist/core/install/flow-based-installer.js +1158 -0
  105. package/dist/core/install/flow-based-installer.js.map +1 -0
  106. package/dist/core/install/flow-workspace-tracker.js +111 -0
  107. package/dist/core/install/flow-workspace-tracker.js.map +1 -0
  108. package/dist/core/install/format-detector.js +228 -0
  109. package/dist/core/install/format-detector.js.map +1 -0
  110. package/dist/core/install/git-package-loader.js +20 -0
  111. package/dist/core/install/git-package-loader.js.map +1 -0
  112. package/dist/core/install/install-errors.js +1 -1
  113. package/dist/core/install/install-errors.js.map +1 -1
  114. package/dist/core/install/install-flow.js +34 -14
  115. package/dist/core/install/install-flow.js.map +1 -1
  116. package/dist/core/install/install-pipeline.js +52 -17
  117. package/dist/core/install/install-pipeline.js.map +1 -1
  118. package/dist/core/install/install-reporting.js +26 -8
  119. package/dist/core/install/install-reporting.js.map +1 -1
  120. package/dist/core/install/local-source-resolution.js +103 -0
  121. package/dist/core/install/local-source-resolution.js.map +1 -0
  122. package/dist/core/install/marketplace-handler.js +221 -0
  123. package/dist/core/install/marketplace-handler.js.map +1 -0
  124. package/dist/core/install/path-install-pipeline.js +241 -0
  125. package/dist/core/install/path-install-pipeline.js.map +1 -0
  126. package/dist/core/install/path-package-loader.js +116 -0
  127. package/dist/core/install/path-package-loader.js.map +1 -0
  128. package/dist/core/install/plugin-detector.js +72 -0
  129. package/dist/core/install/plugin-detector.js.map +1 -0
  130. package/dist/core/install/plugin-to-universal-converter.js +218 -0
  131. package/dist/core/install/plugin-to-universal-converter.js.map +1 -0
  132. package/dist/core/install/plugin-transformer.js +191 -0
  133. package/dist/core/install/plugin-transformer.js.map +1 -0
  134. package/dist/core/install/version-selection.js +1 -1
  135. package/dist/core/install/version-selection.js.map +1 -1
  136. package/dist/core/openpackage.js +40 -22
  137. package/dist/core/openpackage.js.map +1 -1
  138. package/dist/core/pack/pack-output.js +62 -0
  139. package/dist/core/pack/pack-output.js.map +1 -0
  140. package/dist/core/pack/pack-pipeline.js +186 -0
  141. package/dist/core/pack/pack-pipeline.js.map +1 -0
  142. package/dist/core/package-context.js +45 -70
  143. package/dist/core/package-context.js.map +1 -1
  144. package/dist/core/package-creation.js +203 -0
  145. package/dist/core/package-creation.js.map +1 -0
  146. package/dist/core/package.js +20 -6
  147. package/dist/core/package.js.map +1 -1
  148. package/dist/core/platforms.js +665 -209
  149. package/dist/core/platforms.js.map +1 -1
  150. package/dist/core/push/push-context.js +1 -1
  151. package/dist/core/push/push-context.js.map +1 -1
  152. package/dist/core/push/push-upload.js +2 -2
  153. package/dist/core/push/push-upload.js.map +1 -1
  154. package/dist/core/registry.js +6 -6
  155. package/dist/core/registry.js.map +1 -1
  156. package/dist/core/remote-pull.js +2 -2
  157. package/dist/core/remote-pull.js.map +1 -1
  158. package/dist/core/remove/removal-collector.js +52 -0
  159. package/dist/core/remove/removal-collector.js.map +1 -0
  160. package/dist/core/remove/removal-confirmation.js +39 -0
  161. package/dist/core/remove/removal-confirmation.js.map +1 -0
  162. package/dist/core/remove/remove-from-source-pipeline.js +173 -0
  163. package/dist/core/remove/remove-from-source-pipeline.js.map +1 -0
  164. package/dist/core/save/constants.js +3 -3
  165. package/dist/core/save/constants.js.map +1 -1
  166. package/dist/core/save/flow-based-saver.js +270 -0
  167. package/dist/core/save/flow-based-saver.js.map +1 -0
  168. package/dist/core/save/name-resolution.js +1 -1
  169. package/dist/core/save/name-resolution.js.map +1 -1
  170. package/dist/core/save/package-yml-generator.js +4 -5
  171. package/dist/core/save/package-yml-generator.js.map +1 -1
  172. package/dist/core/save/save-candidate-builder.js +215 -0
  173. package/dist/core/save/save-candidate-builder.js.map +1 -0
  174. package/dist/core/save/save-candidate-loader.js +12 -11
  175. package/dist/core/save/save-candidate-loader.js.map +1 -1
  176. package/dist/core/save/save-conflict-analyzer.js +150 -0
  177. package/dist/core/save/save-conflict-analyzer.js.map +1 -0
  178. package/dist/core/save/save-conflict-resolution.js +28 -14
  179. package/dist/core/save/save-conflict-resolution.js.map +1 -1
  180. package/dist/core/save/save-conflict-resolver.js +31 -275
  181. package/dist/core/save/save-conflict-resolver.js.map +1 -1
  182. package/dist/core/save/save-group-builder.js +52 -0
  183. package/dist/core/save/save-group-builder.js.map +1 -0
  184. package/dist/core/save/save-interactive-resolver.js +190 -0
  185. package/dist/core/save/save-interactive-resolver.js.map +1 -0
  186. package/dist/core/save/save-pipeline.js +58 -34
  187. package/dist/core/save/save-pipeline.js.map +1 -1
  188. package/dist/core/save/save-platform-handler.js +53 -0
  189. package/dist/core/save/save-platform-handler.js.map +1 -0
  190. package/dist/core/save/save-resolution-executor.js +145 -0
  191. package/dist/core/save/save-resolution-executor.js.map +1 -0
  192. package/dist/core/save/save-result-reporter.js +167 -0
  193. package/dist/core/save/save-result-reporter.js.map +1 -0
  194. package/dist/core/save/save-to-source-pipeline.js +154 -0
  195. package/dist/core/save/save-to-source-pipeline.js.map +1 -0
  196. package/dist/core/save/save-versioning.js +4 -4
  197. package/dist/core/save/save-versioning.js.map +1 -1
  198. package/dist/core/save/save-write-coordinator.js +204 -0
  199. package/dist/core/save/save-write-coordinator.js.map +1 -0
  200. package/dist/core/save/save-yml-resolution.js +28 -216
  201. package/dist/core/save/save-yml-resolution.js.map +1 -1
  202. package/dist/core/save/workspace-rename.js +7 -8
  203. package/dist/core/save/workspace-rename.js.map +1 -1
  204. package/dist/core/set/set-output.js +72 -0
  205. package/dist/core/set/set-output.js.map +1 -0
  206. package/dist/core/set/set-pipeline.js +361 -0
  207. package/dist/core/set/set-pipeline.js.map +1 -0
  208. package/dist/core/set/set-types.js +5 -0
  209. package/dist/core/set/set-types.js.map +1 -0
  210. package/dist/core/show/package-resolver.js +257 -0
  211. package/dist/core/show/package-resolver.js.map +1 -0
  212. package/dist/core/show/scope-discovery.js +165 -0
  213. package/dist/core/show/scope-discovery.js.map +1 -0
  214. package/dist/core/show/show-output.js +168 -0
  215. package/dist/core/show/show-output.js.map +1 -0
  216. package/dist/core/show/show-pipeline.js +113 -0
  217. package/dist/core/show/show-pipeline.js.map +1 -0
  218. package/dist/core/show/show-types.js +5 -0
  219. package/dist/core/show/show-types.js.map +1 -0
  220. package/dist/core/source-resolution/dependency-graph.js +104 -0
  221. package/dist/core/source-resolution/dependency-graph.js.map +1 -0
  222. package/dist/core/source-resolution/resolve-mutable-source.js +109 -0
  223. package/dist/core/source-resolution/resolve-mutable-source.js.map +1 -0
  224. package/dist/core/source-resolution/resolve-package-source.js +29 -0
  225. package/dist/core/source-resolution/resolve-package-source.js.map +1 -0
  226. package/dist/core/source-resolution/resolve-registry-version.js +35 -0
  227. package/dist/core/source-resolution/resolve-registry-version.js.map +1 -0
  228. package/dist/core/source-resolution/types.js.map +1 -0
  229. package/dist/core/status/status-file-discovery.js +23 -12
  230. package/dist/core/status/status-file-discovery.js.map +1 -1
  231. package/dist/core/status/status-pipeline.js +134 -0
  232. package/dist/core/status/status-pipeline.js.map +1 -0
  233. package/dist/core/sync/platform-sync-summary.js +27 -0
  234. package/dist/core/sync/platform-sync-summary.js.map +1 -0
  235. package/dist/core/uninstall/flow-aware-uninstaller.js +189 -0
  236. package/dist/core/uninstall/flow-aware-uninstaller.js.map +1 -0
  237. package/dist/core/uninstall/uninstall-file-discovery.js +11 -6
  238. package/dist/core/uninstall/uninstall-file-discovery.js.map +1 -1
  239. package/dist/core/uninstall/uninstall-pipeline.js +141 -0
  240. package/dist/core/uninstall/uninstall-pipeline.js.map +1 -0
  241. package/dist/core/universal-patterns.js +64 -0
  242. package/dist/core/universal-patterns.js.map +1 -0
  243. package/dist/index.js +99 -6
  244. package/dist/index.js.map +1 -1
  245. package/dist/types/flows.js +8 -0
  246. package/dist/types/flows.js.map +1 -0
  247. package/dist/types/index.js +3 -0
  248. package/dist/types/index.js.map +1 -1
  249. package/dist/types/platform-flows.js +8 -0
  250. package/dist/types/platform-flows.js.map +1 -0
  251. package/dist/types/workspace-index.js +6 -0
  252. package/dist/types/workspace-index.js.map +1 -0
  253. package/dist/utils/custom-path-resolution.js +160 -0
  254. package/dist/utils/custom-path-resolution.js.map +1 -0
  255. package/dist/utils/dependency-coverage.js +1 -1
  256. package/dist/utils/dependency-coverage.js.map +1 -1
  257. package/dist/utils/file-processing.js +1 -1
  258. package/dist/utils/flow-index-installer.js +209 -0
  259. package/dist/utils/flow-index-installer.js.map +1 -0
  260. package/dist/utils/formatters.js +47 -1
  261. package/dist/utils/formatters.js.map +1 -1
  262. package/dist/utils/fs.js +17 -0
  263. package/dist/utils/fs.js.map +1 -1
  264. package/dist/utils/git-clone-registry.js +88 -0
  265. package/dist/utils/git-clone-registry.js.map +1 -0
  266. package/dist/utils/git-clone.js +69 -0
  267. package/dist/utils/git-clone.js.map +1 -0
  268. package/dist/utils/git-spec.js +96 -0
  269. package/dist/utils/git-spec.js.map +1 -0
  270. package/dist/utils/http-client.js +7 -0
  271. package/dist/utils/http-client.js.map +1 -1
  272. package/dist/utils/index-based-installer.js +356 -163
  273. package/dist/utils/index-based-installer.js.map +1 -1
  274. package/dist/utils/install-conflict-handler.js +2 -2
  275. package/dist/utils/install-conflict-handler.js.map +1 -1
  276. package/dist/utils/install-file-discovery.js +18 -13
  277. package/dist/utils/install-file-discovery.js.map +1 -1
  278. package/dist/utils/install-helpers.js +43 -20
  279. package/dist/utils/install-helpers.js.map +1 -1
  280. package/dist/utils/jsonc.js +23 -1
  281. package/dist/utils/jsonc.js.map +1 -1
  282. package/dist/utils/manifest-paths.js +1 -1
  283. package/dist/utils/manifest-paths.js.map +1 -1
  284. package/dist/utils/markdown-frontmatter.js +46 -0
  285. package/dist/utils/markdown-frontmatter.js.map +1 -1
  286. package/dist/utils/package-copy.js +5 -103
  287. package/dist/utils/package-copy.js.map +1 -1
  288. package/dist/utils/package-filters.js +9 -105
  289. package/dist/utils/package-filters.js.map +1 -1
  290. package/dist/utils/package-index-yml.js +27 -6
  291. package/dist/utils/package-index-yml.js.map +1 -1
  292. package/dist/utils/package-input.js +98 -0
  293. package/dist/utils/package-input.js.map +1 -0
  294. package/dist/utils/package-management.js +80 -28
  295. package/dist/utils/package-management.js.map +1 -1
  296. package/dist/utils/package-name-resolution.js +327 -0
  297. package/dist/utils/package-name-resolution.js.map +1 -0
  298. package/dist/utils/package-name.js +18 -16
  299. package/dist/utils/package-name.js.map +1 -1
  300. package/dist/utils/package-versioning.js +2 -33
  301. package/dist/utils/package-versioning.js.map +1 -1
  302. package/dist/utils/package-yml.js +19 -28
  303. package/dist/utils/package-yml.js.map +1 -1
  304. package/dist/utils/path-resolution.js +102 -0
  305. package/dist/utils/path-resolution.js.map +1 -0
  306. package/dist/utils/paths.js +6 -6
  307. package/dist/utils/paths.js.map +1 -1
  308. package/dist/utils/platform-file.js +36 -24
  309. package/dist/utils/platform-file.js.map +1 -1
  310. package/dist/utils/platform-mapper.js +222 -68
  311. package/dist/utils/platform-mapper.js.map +1 -1
  312. package/dist/utils/platform-root-files.js +44 -0
  313. package/dist/utils/platform-root-files.js.map +1 -0
  314. package/dist/utils/platform-utils.js +35 -54
  315. package/dist/utils/platform-utils.js.map +1 -1
  316. package/dist/utils/platform-yaml-merge.js +20 -140
  317. package/dist/utils/platform-yaml-merge.js.map +1 -1
  318. package/dist/utils/prompts.js +92 -7
  319. package/dist/utils/prompts.js.map +1 -1
  320. package/dist/utils/registry-entry-filter.js +50 -27
  321. package/dist/utils/registry-entry-filter.js.map +1 -1
  322. package/dist/utils/registry-paths.js +5 -4
  323. package/dist/utils/registry-paths.js.map +1 -1
  324. package/dist/utils/scope-resolution.js +156 -0
  325. package/dist/utils/scope-resolution.js.map +1 -0
  326. package/dist/utils/source-mutability.js +15 -0
  327. package/dist/utils/source-mutability.js.map +1 -0
  328. package/dist/utils/tarball.js +29 -4
  329. package/dist/utils/tarball.js.map +1 -1
  330. package/dist/utils/version-ranges.js +1 -32
  331. package/dist/utils/version-ranges.js.map +1 -1
  332. package/dist/utils/workspace-index-helpers.js +28 -0
  333. package/dist/utils/workspace-index-helpers.js.map +1 -0
  334. package/dist/utils/workspace-index-ownership.js +100 -0
  335. package/dist/utils/workspace-index-ownership.js.map +1 -0
  336. package/dist/utils/workspace-index-yml.js +173 -0
  337. package/dist/utils/workspace-index-yml.js.map +1 -0
  338. package/examples/custom-subdirs-platform.jsonc +157 -0
  339. package/package.json +7 -2
  340. package/platforms.jsonc +531 -84
  341. package/schemas/map-pipeline-v1.json +256 -0
  342. package/schemas/platforms-v1.json +400 -0
  343. package/specs/README.md +88 -0
  344. package/specs/add/README.md +166 -0
  345. package/specs/agents-claude.md +570 -0
  346. package/specs/agents-opencode.md +622 -0
  347. package/specs/apply/README.md +21 -0
  348. package/specs/apply/apply-behavior.md +58 -0
  349. package/specs/apply/apply-command.md +51 -0
  350. package/specs/apply/conflicts.md +41 -0
  351. package/specs/apply/index-effects.md +81 -0
  352. package/specs/architecture.md +107 -0
  353. package/specs/auth/README.md +17 -0
  354. package/specs/auth/auth-http-contract.md +25 -0
  355. package/specs/auth/cli/credentials.md +39 -0
  356. package/specs/auth/cli/login.md +32 -0
  357. package/specs/auth/cli/logout.md +16 -0
  358. package/specs/claude-mcp.md +1065 -0
  359. package/specs/claude-plugins-marketplace.md +363 -0
  360. package/specs/claude-plugins.md +413 -0
  361. package/specs/cli-options.md +52 -0
  362. package/specs/codex-mcp.md +114 -0
  363. package/specs/commands-overview.md +175 -0
  364. package/specs/directory-layout.md +95 -0
  365. package/specs/install/README.md +12 -4
  366. package/specs/install/git-sources.md +230 -0
  367. package/specs/install/install-behavior.md +483 -73
  368. package/specs/install/package-yml-canonical.md +67 -35
  369. package/specs/install/version-resolution.md +69 -115
  370. package/specs/new/README.md +769 -0
  371. package/specs/new/SUMMARY.md +310 -0
  372. package/specs/new/scope-behavior.md +793 -0
  373. package/specs/pack/README.md +77 -0
  374. package/specs/pack/package-name-resolution.md +330 -0
  375. package/specs/package/README.md +18 -17
  376. package/specs/package/nested-packages-and-parent-packages.md +32 -31
  377. package/specs/package/package-index-yml.md +95 -101
  378. package/specs/package/package-root-layout.md +64 -46
  379. package/specs/package/registry-payload-and-copy.md +50 -44
  380. package/specs/package/universal-content.md +33 -56
  381. package/specs/package-sources.md +248 -0
  382. package/specs/platforms/README.md +52 -0
  383. package/specs/platforms/configuration.md +571 -0
  384. package/specs/platforms/detection.md +552 -0
  385. package/specs/platforms/directory-layout.md +599 -0
  386. package/specs/platforms/examples.md +1146 -0
  387. package/specs/platforms/flow-reference.md +1240 -0
  388. package/specs/platforms/flows.md +1488 -0
  389. package/specs/platforms/map-pipeline.md +801 -0
  390. package/specs/platforms/overview.md +349 -0
  391. package/specs/platforms/specification.md +700 -0
  392. package/specs/platforms/troubleshooting.md +697 -0
  393. package/specs/platforms/universal-converter.md +520 -0
  394. package/specs/push/README.md +1 -0
  395. package/specs/push/push-behavior.md +11 -3
  396. package/specs/push/push-remote-upload.md +1 -1
  397. package/specs/push/push-scoping.md +1 -1
  398. package/specs/push/push-version-selection.md +1 -1
  399. package/specs/registry.md +111 -0
  400. package/specs/remove/README.md +257 -0
  401. package/specs/save/README.md +21 -17
  402. package/specs/save/save-conflict-resolution.md +205 -83
  403. package/specs/save/save-file-discovery.md +6 -4
  404. package/specs/save/save-frontmatter-overrides.md +11 -15
  405. package/specs/save/save-modes-inputs.md +9 -39
  406. package/specs/save/save-naming-scoping.md +4 -4
  407. package/specs/save/save-package-detection.md +13 -13
  408. package/specs/save/save-registry-sync.md +16 -106
  409. package/specs/save/save-versioning.md +80 -0
  410. package/specs/scope-management.md +92 -0
  411. package/specs/set/README.md +520 -0
  412. package/specs/set/set-behavior.md +563 -0
  413. package/specs/show/README.md +483 -0
  414. package/specs/show/show-remote.md +494 -0
  415. package/specs/status/README.md +38 -0
  416. package/specs/uninstall/README.md +231 -0
  417. package/dist/commands/duplicate.js +0 -69
  418. package/dist/commands/duplicate.js.map +0 -1
  419. package/dist/commands/init.js +0 -117
  420. package/dist/commands/init.js.map +0 -1
  421. package/dist/commands/prune.js +0 -357
  422. package/dist/commands/prune.js.map +0 -1
  423. package/dist/commands/tui.js +0 -61
  424. package/dist/commands/tui.js.map +0 -1
  425. package/dist/core/install/index.js +0 -3
  426. package/dist/core/install/index.js.map +0 -1
  427. package/dist/core/push/push-single-file.js +0 -56
  428. package/dist/core/push/push-single-file.js.map +0 -1
  429. package/dist/core/save/package-detection.js +0 -147
  430. package/dist/core/save/package-detection.js.map +0 -1
  431. package/dist/core/save/save-single-file.js +0 -124
  432. package/dist/core/save/save-single-file.js.map +0 -1
  433. package/dist/core/token-store.js +0 -73
  434. package/dist/core/token-store.js.map +0 -1
  435. package/dist/tui/app.js +0 -95
  436. package/dist/tui/app.js.map +0 -1
  437. package/dist/tui/components/package-list.js +0 -73
  438. package/dist/tui/components/package-list.js.map +0 -1
  439. package/dist/tui/controller.js +0 -365
  440. package/dist/tui/controller.js.map +0 -1
  441. package/dist/tui/index.js +0 -12
  442. package/dist/tui/index.js.map +0 -1
  443. package/dist/tui/services/file-index.js +0 -64
  444. package/dist/tui/services/file-index.js.map +0 -1
  445. package/dist/tui/services/packages.js +0 -18
  446. package/dist/tui/services/packages.js.map +0 -1
  447. package/dist/tui/services/save.js +0 -21
  448. package/dist/tui/services/save.js.map +0 -1
  449. package/dist/tui/state/app-state.js +0 -15
  450. package/dist/tui/state/app-state.js.map +0 -1
  451. package/dist/tui/state.js +0 -17
  452. package/dist/tui/state.js.map +0 -1
  453. package/dist/tui/types.js.map +0 -1
  454. package/dist/tui/views/add-file-modal.js +0 -129
  455. package/dist/tui/views/add-file-modal.js.map +0 -1
  456. package/dist/tui/views/file-preview.js +0 -44
  457. package/dist/tui/views/file-preview.js.map +0 -1
  458. package/dist/tui/views/list-packages.js +0 -73
  459. package/dist/tui/views/list-packages.js.map +0 -1
  460. package/dist/tui/views/main-menu.js +0 -29
  461. package/dist/tui/views/main-menu.js.map +0 -1
  462. package/dist/tui/views/manage-view.js +0 -81
  463. package/dist/tui/views/manage-view.js.map +0 -1
  464. package/dist/tui/views/package-hub.js +0 -120
  465. package/dist/tui/views/package-hub.js.map +0 -1
  466. package/dist/tui/views/placeholder.js +0 -24
  467. package/dist/tui/views/placeholder.js.map +0 -1
  468. package/dist/utils/bun-bootstrap.js +0 -72
  469. package/dist/utils/bun-bootstrap.js.map +0 -1
  470. package/dist/utils/entity-id.js +0 -19
  471. package/dist/utils/entity-id.js.map +0 -1
  472. package/dist/utils/package-local-files.js +0 -5
  473. package/dist/utils/package-local-files.js.map +0 -1
  474. package/dist/utils/path-matching.js +0 -74
  475. package/dist/utils/path-matching.js.map +0 -1
  476. package/dist/utils/root-file-operations.js +0 -39
  477. package/dist/utils/root-file-operations.js.map +0 -1
  478. package/dist/utils/root-file-transformer.js +0 -27
  479. package/dist/utils/root-file-transformer.js.map +0 -1
  480. package/dist/utils/yaml-frontmatter.js +0 -25
  481. package/dist/utils/yaml-frontmatter.js.map +0 -1
  482. package/specs/auth/auth-device-flow.md +0 -70
  483. package/specs/login/login-device-flow.md +0 -70
  484. package/specs/platforms.md +0 -193
  485. package/specs/save-pack-versioning.md +0 -224
  486. package/specs/save-pack.md +0 -68
  487. /package/dist/{tui → core/source-resolution}/types.js +0 -0
@@ -0,0 +1,974 @@
1
+ /**
2
+ * Flow Executor
3
+ *
4
+ * Executes flows through a multi-stage pipeline:
5
+ * 1. Load source file and parse format
6
+ * 2. Extract JSONPath (if specified)
7
+ * 3. Pick/omit keys
8
+ * 4. Map keys (with transforms)
9
+ * 5. Apply pipe transforms
10
+ * 6. Embed in target structure
11
+ * 7. Merge with existing target (priority-based)
12
+ * 8. Write to target file
13
+ */
14
+ import fsSync from 'fs';
15
+ import path from 'path';
16
+ import yaml from 'js-yaml';
17
+ import * as TOML from 'smol-toml';
18
+ import { parse as parseJsonc } from 'jsonc-parser';
19
+ import { JSONPath } from 'jsonpath-plus';
20
+ import * as fsUtils from '../../utils/fs.js';
21
+ import { mergePackageContentIntoRootFile } from '../../utils/root-file-merger.js';
22
+ import { logger } from '../../utils/logger.js';
23
+ import { defaultTransformRegistry, } from './flow-transforms.js';
24
+ import { mergeInlinePlatformOverride } from '../../utils/platform-yaml-merge.js';
25
+ import { getNestedValue, setNestedValue, deleteNestedValue } from './flow-key-mapper.js';
26
+ import { extractAllKeys } from './flow-key-extractor.js';
27
+ import { applyMapPipeline, createMapContext, validateMapPipeline } from './map-pipeline/index.js';
28
+ import { SourcePatternResolver } from './source-resolver.js';
29
+ /**
30
+ * Default flow executor implementation
31
+ */
32
+ export class DefaultFlowExecutor {
33
+ constructor(transformRegistry) {
34
+ this.transformRegistry = transformRegistry || defaultTransformRegistry;
35
+ this.sourceResolver = new SourcePatternResolver();
36
+ }
37
+ /**
38
+ * Execute a single flow (now supports glob patterns)
39
+ */
40
+ async executeFlow(flow, context) {
41
+ const startTime = Date.now();
42
+ try {
43
+ // Check if this is a multi-target flow
44
+ if (typeof flow.to !== 'string') {
45
+ const results = await this.executeMultiTarget(flow, context);
46
+ // Aggregate results
47
+ return this.aggregateResults(results, startTime);
48
+ }
49
+ // Validate flow
50
+ const validation = this.validateFlow(flow);
51
+ if (!validation.valid) {
52
+ return {
53
+ source: this.normalizeFromPattern(flow.from),
54
+ target: flow.to,
55
+ success: false,
56
+ transformed: false,
57
+ error: new Error(`Invalid flow: ${validation.errors.map(e => e.message).join(', ')}`),
58
+ executionTime: Date.now() - startTime,
59
+ };
60
+ }
61
+ // Evaluate conditions
62
+ if (flow.when && !this.evaluateCondition(flow.when, context)) {
63
+ const normalized = this.normalizeFromPattern(flow.from);
64
+ logger.debug(`Flow skipped due to condition: ${normalized} -> ${flow.to}`);
65
+ return {
66
+ source: normalized,
67
+ target: flow.to,
68
+ success: true,
69
+ transformed: false,
70
+ warnings: ['Flow skipped due to condition'],
71
+ executionTime: Date.now() - startTime,
72
+ };
73
+ }
74
+ // Resolve source paths (may return multiple files for glob patterns)
75
+ const resolution = await this.resolveSourcePattern(flow.from, context);
76
+ const sourcePaths = resolution.paths;
77
+ const resolutionWarnings = resolution.warnings;
78
+ // If no files matched, return success with no files processed
79
+ if (sourcePaths.length === 0) {
80
+ return {
81
+ source: this.normalizeFromPattern(flow.from),
82
+ target: flow.to,
83
+ success: true,
84
+ transformed: false,
85
+ warnings: resolutionWarnings.length > 0 ? resolutionWarnings : ['No files matched pattern'],
86
+ executionTime: Date.now() - startTime,
87
+ };
88
+ }
89
+ // Execute pipeline for each matched file
90
+ const results = [];
91
+ const firstFromPattern = this.getFirstPattern(flow.from);
92
+ for (const sourcePath of sourcePaths) {
93
+ const targetPath = this.resolveTargetFromGlob(sourcePath, firstFromPattern, flow.to, context);
94
+ const result = await this.executePipeline(flow, sourcePath, targetPath, context);
95
+ results.push({
96
+ ...result,
97
+ executionTime: Date.now() - startTime,
98
+ });
99
+ }
100
+ // Add resolution warnings to first result if any
101
+ if (resolutionWarnings.length > 0 && results.length > 0) {
102
+ results[0].warnings = [
103
+ ...(results[0].warnings || []),
104
+ ...resolutionWarnings,
105
+ ];
106
+ }
107
+ // If single file, return single result
108
+ if (results.length === 1) {
109
+ return results[0];
110
+ }
111
+ // Aggregate multiple results
112
+ return this.aggregateResults(results, startTime);
113
+ }
114
+ catch (error) {
115
+ return {
116
+ source: this.normalizeFromPattern(flow.from),
117
+ target: typeof flow.to === 'string' ? flow.to : Object.keys(flow.to).join(', '),
118
+ success: false,
119
+ transformed: false,
120
+ error: error instanceof Error ? error : new Error(String(error)),
121
+ executionTime: Date.now() - startTime,
122
+ };
123
+ }
124
+ }
125
+ /**
126
+ * Execute multiple flows
127
+ */
128
+ async executeFlows(flows, context) {
129
+ const results = [];
130
+ for (const flow of flows) {
131
+ const result = await this.executeFlow(flow, context);
132
+ results.push(result);
133
+ }
134
+ return results;
135
+ }
136
+ /**
137
+ * Execute a multi-target flow
138
+ */
139
+ async executeMultiTarget(flow, context) {
140
+ if (typeof flow.to === 'string') {
141
+ throw new Error('Flow is not a multi-target flow');
142
+ }
143
+ const multiTarget = flow.to;
144
+ const normalizedFrom = this.normalizeFromPattern(flow.from);
145
+ // Resolve source paths (may be multiple files with glob)
146
+ const resolution = await this.resolveSourcePattern(flow.from, context);
147
+ const sourcePaths = resolution.paths;
148
+ // If no files matched
149
+ if (sourcePaths.length === 0) {
150
+ return Object.keys(multiTarget).map(target => ({
151
+ source: normalizedFrom,
152
+ target,
153
+ success: true,
154
+ transformed: false,
155
+ warnings: resolution.warnings.length > 0 ? resolution.warnings : ['No files matched pattern'],
156
+ }));
157
+ }
158
+ // Execute each source file
159
+ const allResults = [];
160
+ const firstFromPattern = this.getFirstPattern(flow.from);
161
+ for (const sourcePath of sourcePaths) {
162
+ // Load and parse source once
163
+ const sourceContent = await this.loadSourceFile(sourcePath, context);
164
+ // Execute each target
165
+ for (const [targetPath, targetFlow] of Object.entries(multiTarget)) {
166
+ const startTime = Date.now();
167
+ try {
168
+ // Merge target flow with base flow
169
+ const mergedFlow = {
170
+ ...flow,
171
+ ...targetFlow,
172
+ from: flow.from,
173
+ to: targetPath,
174
+ };
175
+ // Evaluate conditions
176
+ if (mergedFlow.when && !this.evaluateCondition(mergedFlow.when, context)) {
177
+ logger.debug(`Multi-target flow skipped due to condition: ${normalizedFrom} -> ${targetPath}`);
178
+ allResults.push({
179
+ source: normalizedFrom,
180
+ target: targetPath,
181
+ success: true,
182
+ transformed: false,
183
+ warnings: ['Flow skipped due to condition'],
184
+ executionTime: Date.now() - startTime,
185
+ });
186
+ continue;
187
+ }
188
+ const resolvedTargetPath = this.resolveTargetFromGlob(sourcePath, firstFromPattern, targetPath, context);
189
+ // Execute pipeline with pre-loaded content
190
+ const result = await this.executePipelineWithContent(mergedFlow, sourceContent, sourcePath, resolvedTargetPath, context);
191
+ allResults.push({
192
+ ...result,
193
+ executionTime: Date.now() - startTime,
194
+ });
195
+ }
196
+ catch (error) {
197
+ allResults.push({
198
+ source: normalizedFrom,
199
+ target: targetPath,
200
+ success: false,
201
+ transformed: false,
202
+ error: error instanceof Error ? error : new Error(String(error)),
203
+ executionTime: Date.now() - startTime,
204
+ });
205
+ }
206
+ }
207
+ }
208
+ return allResults;
209
+ }
210
+ /**
211
+ * Validate a flow configuration
212
+ */
213
+ validateFlow(flow) {
214
+ const errors = [];
215
+ // Check required fields
216
+ if (!flow.from) {
217
+ errors.push({ message: 'Flow missing required field "from"', code: 'MISSING_FROM' });
218
+ }
219
+ if (!flow.to) {
220
+ errors.push({ message: 'Flow missing required field "to"', code: 'MISSING_TO' });
221
+ }
222
+ // Validate pick/omit
223
+ if (flow.pick && flow.omit) {
224
+ errors.push({ message: 'Flow cannot have both "pick" and "omit"', code: 'CONFLICTING_FILTERS' });
225
+ }
226
+ // Validate merge strategy
227
+ if (flow.merge && !['deep', 'shallow', 'replace', 'composite'].includes(flow.merge)) {
228
+ errors.push({
229
+ message: `Invalid merge strategy: ${flow.merge}. Must be one of: deep, shallow, replace, composite`,
230
+ code: 'INVALID_MERGE',
231
+ });
232
+ }
233
+ // Validate JSONPath expression
234
+ if (flow.path) {
235
+ try {
236
+ // Try to validate JSONPath syntax
237
+ JSONPath({ path: flow.path, json: {} });
238
+ }
239
+ catch (error) {
240
+ errors.push({
241
+ message: `Invalid JSONPath expression: ${flow.path}`,
242
+ code: 'INVALID_JSONPATH',
243
+ });
244
+ }
245
+ }
246
+ // Validate map pipeline
247
+ if (flow.map) {
248
+ const mapPipelineValidation = validateMapPipeline(flow.map);
249
+ if (!mapPipelineValidation.valid) {
250
+ for (const error of mapPipelineValidation.errors) {
251
+ errors.push({
252
+ message: error,
253
+ code: 'INVALID_MAP_PIPELINE',
254
+ });
255
+ }
256
+ }
257
+ }
258
+ return {
259
+ valid: errors.length === 0,
260
+ errors,
261
+ warnings: [],
262
+ };
263
+ }
264
+ /**
265
+ * Execute the transformation pipeline
266
+ */
267
+ async executePipeline(flow, sourcePath, targetPath, context) {
268
+ // Step 1: Load source file
269
+ const sourceContent = await this.loadSourceFile(sourcePath, context);
270
+ return this.executePipelineWithContent(flow, sourceContent, sourcePath, targetPath, context);
271
+ }
272
+ /**
273
+ * Execute pipeline with pre-loaded content (for multi-target flows)
274
+ */
275
+ async executePipelineWithContent(flow, sourceContent, sourcePath, targetPath, context) {
276
+ const warnings = [];
277
+ const conflicts = [];
278
+ let data = sourceContent.data;
279
+ let transformed = false;
280
+ try {
281
+ // Step 2: Extract JSONPath (if specified)
282
+ if (flow.path) {
283
+ data = this.extractJSONPath(data, flow.path);
284
+ transformed = true;
285
+ }
286
+ // Step 3: Pick/omit keys
287
+ if (flow.pick) {
288
+ data = this.pickKeys(data, flow.pick);
289
+ transformed = true;
290
+ }
291
+ else if (flow.omit) {
292
+ data = this.omitKeys(data, flow.omit);
293
+ transformed = true;
294
+ }
295
+ // Step 4: Apply map pipeline
296
+ // Split into schema operations and pipe operations
297
+ // Schema ops are applied BEFORE merge, pipe ops are applied AFTER merge
298
+ let contributedKeys;
299
+ let schemaOps = [];
300
+ let pipeOps = [];
301
+ if (flow.map) {
302
+ // Separate schema operations from pipe operations
303
+ for (const op of flow.map) {
304
+ if ('$pipe' in op) {
305
+ pipeOps.push(op);
306
+ }
307
+ else {
308
+ schemaOps.push(op);
309
+ }
310
+ }
311
+ // Apply schema operations first (before merge)
312
+ if (schemaOps.length > 0) {
313
+ const mapContext = createMapContext({
314
+ filename: path.basename(sourcePath, path.extname(sourcePath)),
315
+ dirname: path.basename(path.dirname(sourcePath)),
316
+ path: path.relative(context.packageRoot, sourcePath),
317
+ ext: path.extname(sourcePath),
318
+ });
319
+ // For markdown files, apply to frontmatter
320
+ if (data && typeof data === 'object' && 'frontmatter' in data) {
321
+ data.frontmatter = applyMapPipeline(data.frontmatter, schemaOps, mapContext, this.transformRegistry);
322
+ }
323
+ else {
324
+ // Apply to entire document
325
+ data = applyMapPipeline(data, schemaOps, mapContext, this.transformRegistry);
326
+ }
327
+ transformed = true;
328
+ }
329
+ }
330
+ // Track keys AFTER schema transforms but BEFORE merge and pipe transforms
331
+ // This represents the structured data this package contributes
332
+ const shouldTrackKeys = Boolean(flow.merge) &&
333
+ flow.merge !== 'replace' &&
334
+ flow.merge !== 'composite';
335
+ if (shouldTrackKeys && typeof data === 'object' && data !== null) {
336
+ // Extract from frontmatter if it's a markdown file
337
+ const dataToExtract = (data && 'frontmatter' in data) ? data.frontmatter : data;
338
+ if (typeof dataToExtract === 'object' && dataToExtract !== null) {
339
+ contributedKeys = extractAllKeys(dataToExtract);
340
+ }
341
+ }
342
+ const targetExists = await fsUtils.exists(targetPath);
343
+ // Step 6: Embed in target structure
344
+ if (flow.embed) {
345
+ data = this.embedContent(data, flow.embed);
346
+ transformed = true;
347
+ }
348
+ // Step 7: Merge with existing target (if needed)
349
+ if (targetExists) {
350
+ const targetContent = await this.loadSourceFile(targetPath, context);
351
+ // Special handling for composite merge - works with raw text
352
+ if (flow.merge === 'composite') {
353
+ // Use raw content for composite merge
354
+ const sourceRaw = sourceContent.raw;
355
+ const targetRaw = targetContent.raw;
356
+ data = mergePackageContentIntoRootFile(targetRaw, context.packageName, sourceRaw);
357
+ transformed = true;
358
+ }
359
+ else {
360
+ // Normal merge for other strategies
361
+ const mergeResult = this.mergeContent(data, targetContent.data, flow.merge || 'replace', context);
362
+ data = mergeResult.data;
363
+ conflicts.push(...mergeResult.conflicts);
364
+ if (mergeResult.conflicts.length > 0) {
365
+ transformed = true;
366
+ }
367
+ }
368
+ }
369
+ // Step 7.5: Apply pipe operations AFTER merge (format conversions)
370
+ // These operations may convert the data to a string format (e.g., json-to-toml)
371
+ if (pipeOps.length > 0) {
372
+ const mapContext = createMapContext({
373
+ filename: path.basename(sourcePath, path.extname(sourcePath)),
374
+ dirname: path.basename(path.dirname(sourcePath)),
375
+ path: path.relative(context.packageRoot, sourcePath),
376
+ ext: path.extname(sourcePath),
377
+ });
378
+ // For markdown files, apply to frontmatter
379
+ if (data && typeof data === 'object' && 'frontmatter' in data) {
380
+ data.frontmatter = applyMapPipeline(data.frontmatter, pipeOps, mapContext, this.transformRegistry);
381
+ }
382
+ else {
383
+ // Apply to entire document
384
+ data = applyMapPipeline(data, pipeOps, mapContext, this.transformRegistry);
385
+ }
386
+ transformed = true;
387
+ }
388
+ // Step 8: Write to target file
389
+ if (!context.dryRun) {
390
+ await this.writeTargetFile(targetPath, data, sourceContent.format);
391
+ }
392
+ return {
393
+ source: sourcePath,
394
+ target: targetPath,
395
+ success: true,
396
+ transformed,
397
+ keys: contributedKeys,
398
+ merge: flow.merge,
399
+ warnings: warnings.length > 0 ? warnings : undefined,
400
+ conflicts: conflicts.length > 0 ? conflicts : undefined,
401
+ pipeline: this.getPipeline(flow),
402
+ };
403
+ }
404
+ catch (error) {
405
+ return {
406
+ source: sourcePath,
407
+ target: targetPath,
408
+ success: false,
409
+ transformed,
410
+ error: error instanceof Error ? error : new Error(String(error)),
411
+ warnings: warnings.length > 0 ? warnings : undefined,
412
+ };
413
+ }
414
+ }
415
+ /**
416
+ * Load and parse source file
417
+ */
418
+ async loadSourceFile(filePath, context) {
419
+ let raw = await fsUtils.readTextFile(filePath);
420
+ const format = this.detectFormat(filePath, raw);
421
+ // Apply platform-specific frontmatter overrides for markdown files during install
422
+ if ((format === 'markdown' || format === 'md') && context?.platform && context?.direction === 'install') {
423
+ raw = mergeInlinePlatformOverride(raw, context.platform, context.workspaceRoot);
424
+ }
425
+ const data = this.parseSourceContent(raw, format);
426
+ return { data, format, raw };
427
+ }
428
+ /**
429
+ * Write transformed content to target file
430
+ */
431
+ async writeTargetFile(filePath, content, sourceFormat) {
432
+ await fsUtils.ensureDir(path.dirname(filePath));
433
+ // Detect target format from file extension
434
+ const targetFormat = this.detectFormat(filePath, '');
435
+ const serialized = this.serializeTargetContent(content, targetFormat);
436
+ await fsUtils.writeTextFile(filePath, serialized);
437
+ }
438
+ /**
439
+ * Parse source content based on format
440
+ */
441
+ parseSourceContent(content, format) {
442
+ try {
443
+ switch (format) {
444
+ case 'json':
445
+ case 'jsonc':
446
+ // Remove comments for JSONC
447
+ const cleaned = content;
448
+ if (format === 'jsonc') {
449
+ const parsed = parseJsonc(cleaned);
450
+ if (parsed === undefined) {
451
+ throw new Error('jsonc-parser returned undefined');
452
+ }
453
+ return parsed;
454
+ }
455
+ return JSON.parse(cleaned);
456
+ case 'yaml':
457
+ case 'yml':
458
+ return yaml.load(content);
459
+ case 'toml':
460
+ try {
461
+ return TOML.parse(content);
462
+ }
463
+ catch (error) {
464
+ throw new Error(`TOML parse error: ${error instanceof Error ? error.message : String(error)}`);
465
+ }
466
+ case 'markdown':
467
+ case 'md':
468
+ return this.parseMarkdown(content);
469
+ case 'text':
470
+ case 'txt':
471
+ default:
472
+ return content;
473
+ }
474
+ }
475
+ catch (error) {
476
+ throw new Error(`Failed to parse ${format} content: ${error instanceof Error ? error.message : String(error)}`);
477
+ }
478
+ }
479
+ /**
480
+ * Serialize content to target format
481
+ */
482
+ serializeTargetContent(content, format) {
483
+ try {
484
+ switch (format) {
485
+ case 'json':
486
+ case 'jsonc':
487
+ return JSON.stringify(content, null, 2);
488
+ case 'yaml':
489
+ case 'yml':
490
+ return yaml.dump(content, { indent: 2, lineWidth: -1 });
491
+ case 'toml':
492
+ // If a pipeline already produced TOML text (e.g. via domain transforms),
493
+ // don't stringify again.
494
+ if (typeof content === 'string') {
495
+ return content;
496
+ }
497
+ try {
498
+ // Serialize to TOML
499
+ let toml = TOML.stringify(content);
500
+ // Apply inline table formatting for Codex MCP configs
501
+ if (content && typeof content === 'object' && content.mcp_servers) {
502
+ toml = this.applyCodexTomlFormatting(toml);
503
+ }
504
+ return toml;
505
+ }
506
+ catch (error) {
507
+ throw new Error(`TOML stringify error: ${error instanceof Error ? error.message : String(error)}`);
508
+ }
509
+ case 'markdown':
510
+ case 'md':
511
+ return this.serializeMarkdown(content);
512
+ case 'text':
513
+ case 'txt':
514
+ default:
515
+ return typeof content === 'string' ? content : JSON.stringify(content, null, 2);
516
+ }
517
+ }
518
+ catch (error) {
519
+ throw new Error(`Failed to serialize ${format} content: ${error instanceof Error ? error.message : String(error)}`);
520
+ }
521
+ }
522
+ /**
523
+ * Detect format from file extension or content
524
+ */
525
+ detectFormat(filePath, content) {
526
+ const ext = path.extname(filePath).toLowerCase();
527
+ const extMap = {
528
+ '.json': 'json',
529
+ '.jsonc': 'jsonc',
530
+ '.yaml': 'yaml',
531
+ '.yml': 'yml',
532
+ '.toml': 'toml',
533
+ '.md': 'markdown',
534
+ '.markdown': 'markdown',
535
+ '.txt': 'text',
536
+ };
537
+ if (extMap[ext]) {
538
+ return extMap[ext];
539
+ }
540
+ // Try to detect from content
541
+ if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
542
+ return content.includes('//') || content.includes('/*') ? 'jsonc' : 'json';
543
+ }
544
+ if (content.includes('---\n') || content.includes('\n---\n')) {
545
+ return 'markdown';
546
+ }
547
+ return 'text';
548
+ }
549
+ /**
550
+ * Extract data using JSONPath
551
+ */
552
+ extractJSONPath(data, jsonPath) {
553
+ try {
554
+ const result = JSONPath({ path: jsonPath, json: data });
555
+ return result.length === 1 ? result[0] : result;
556
+ }
557
+ catch (error) {
558
+ throw new Error(`JSONPath extraction failed: ${error instanceof Error ? error.message : String(error)}`);
559
+ }
560
+ }
561
+ /**
562
+ * Pick specified keys
563
+ */
564
+ pickKeys(data, keys) {
565
+ if (typeof data !== 'object' || data === null) {
566
+ return data;
567
+ }
568
+ const result = Array.isArray(data) ? [] : {};
569
+ for (const key of keys) {
570
+ if (key.includes('.')) {
571
+ // Handle nested keys
572
+ this.setNestedValue(result, key, this.getNestedValue(data, key));
573
+ }
574
+ else if (key in data) {
575
+ result[key] = data[key];
576
+ }
577
+ }
578
+ return result;
579
+ }
580
+ /**
581
+ * Omit specified keys
582
+ */
583
+ omitKeys(data, keys) {
584
+ if (typeof data !== 'object' || data === null) {
585
+ return data;
586
+ }
587
+ const result = Array.isArray(data) ? [...data] : { ...data };
588
+ for (const key of keys) {
589
+ if (key.includes('.')) {
590
+ // Handle nested keys
591
+ this.deleteNestedValue(result, key);
592
+ }
593
+ else {
594
+ delete result[key];
595
+ }
596
+ }
597
+ return result;
598
+ }
599
+ /**
600
+ * Apply pipe transforms
601
+ */
602
+ async applyPipeTransforms(data, transforms, context) {
603
+ let result = data;
604
+ for (const transformSpec of transforms) {
605
+ try {
606
+ // Parse transform specification
607
+ // Format: "transform-name" or "transform-name(option1=value1,option2=value2)"
608
+ const { name, options } = this.parseTransformSpec(transformSpec);
609
+ logger.debug(`Applying transform: ${name}`, options);
610
+ // Execute transform
611
+ result = this.transformRegistry.execute(name, result, options);
612
+ }
613
+ catch (error) {
614
+ throw new Error(`Transform '${transformSpec}' failed: ${error instanceof Error ? error.message : String(error)}`);
615
+ }
616
+ }
617
+ return result;
618
+ }
619
+ /**
620
+ * Parse transform specification
621
+ * Examples: "trim", "number", "pick-keys(keys=[a,b,c])"
622
+ */
623
+ parseTransformSpec(spec) {
624
+ const match = spec.match(/^([a-z-]+)(?:\((.+)\))?$/);
625
+ if (!match) {
626
+ throw new Error(`Invalid transform specification: ${spec}`);
627
+ }
628
+ const [, name, optionsStr] = match;
629
+ if (!optionsStr) {
630
+ return { name };
631
+ }
632
+ // Parse options (simple key=value format)
633
+ const options = {};
634
+ const pairs = optionsStr.split(',').map(s => s.trim());
635
+ for (const pair of pairs) {
636
+ const [key, value] = pair.split('=').map(s => s.trim());
637
+ // Parse value type
638
+ if (value.startsWith('[') && value.endsWith(']')) {
639
+ // Array
640
+ options[key] = value.slice(1, -1).split(',').map(s => s.trim());
641
+ }
642
+ else if (value === 'true' || value === 'false') {
643
+ // Boolean
644
+ options[key] = value === 'true';
645
+ }
646
+ else if (!isNaN(Number(value))) {
647
+ // Number
648
+ options[key] = Number(value);
649
+ }
650
+ else {
651
+ // String
652
+ options[key] = value;
653
+ }
654
+ }
655
+ return { name, options };
656
+ }
657
+ /**
658
+ * Embed content under a key
659
+ */
660
+ embedContent(data, key) {
661
+ return { [key]: data };
662
+ }
663
+ /**
664
+ * Merge content with priority-based conflict resolution
665
+ */
666
+ mergeContent(source, target, strategy, context) {
667
+ const conflicts = [];
668
+ let merged;
669
+ switch (strategy) {
670
+ case 'replace':
671
+ merged = source;
672
+ break;
673
+ case 'shallow':
674
+ merged = { ...target, ...source };
675
+ break;
676
+ case 'deep':
677
+ merged = this.deepMerge(target, source, conflicts, context);
678
+ break;
679
+ case 'composite':
680
+ // Composite merge is handled earlier in the pipeline (Step 7)
681
+ // This case should not be reached
682
+ merged = source;
683
+ break;
684
+ default:
685
+ merged = source;
686
+ }
687
+ return { data: merged, conflicts };
688
+ }
689
+ /**
690
+ * Deep merge two objects
691
+ */
692
+ deepMerge(target, source, conflicts, context, keyPath = '') {
693
+ if (typeof source !== 'object' || source === null) {
694
+ return source;
695
+ }
696
+ if (typeof target !== 'object' || target === null) {
697
+ return source;
698
+ }
699
+ if (Array.isArray(source) && Array.isArray(target)) {
700
+ // Merge arrays
701
+ return [...target, ...source];
702
+ }
703
+ const result = { ...target };
704
+ for (const key of Object.keys(source)) {
705
+ const currentPath = keyPath ? `${keyPath}.${key}` : key;
706
+ if (!(key in target)) {
707
+ result[key] = source[key];
708
+ }
709
+ else if (typeof source[key] === 'object' && typeof target[key] === 'object') {
710
+ result[key] = this.deepMerge(target[key], source[key], conflicts, context, currentPath);
711
+ }
712
+ else if (source[key] !== target[key]) {
713
+ // Conflict detected
714
+ conflicts.push({
715
+ path: currentPath,
716
+ winner: context.packageName,
717
+ losers: ['existing'],
718
+ type: 'value',
719
+ resolution: 'last-writer-wins',
720
+ });
721
+ result[key] = source[key];
722
+ }
723
+ }
724
+ return result;
725
+ }
726
+ /**
727
+ * Evaluate condition
728
+ */
729
+ /**
730
+ * Composite merge using comment delimiters to preserve multiple package contributions
731
+ * Each package's content is wrapped in HTML comment markers with package name
732
+ */
733
+ evaluateCondition(condition, context) {
734
+ if (condition.and) {
735
+ return condition.and.every((c) => this.evaluateCondition(c, context));
736
+ }
737
+ if (condition.or) {
738
+ return condition.or.some((c) => this.evaluateCondition(c, context));
739
+ }
740
+ if (condition.not) {
741
+ return !this.evaluateCondition(condition.not, context);
742
+ }
743
+ if (condition.exists) {
744
+ const testPath = path.join(context.workspaceRoot, condition.exists);
745
+ // Use existsSync for synchronous condition evaluation
746
+ return fsSync.existsSync(testPath);
747
+ }
748
+ if (condition.platform) {
749
+ return context.platform === condition.platform;
750
+ }
751
+ return true;
752
+ }
753
+ /**
754
+ * Resolve pattern with glob support and priority-based array matching
755
+ * Returns resolved file paths (glob patterns return multiple files)
756
+ */
757
+ async resolveSourcePattern(pattern, context) {
758
+ const result = await this.sourceResolver.resolve(pattern, {
759
+ baseDir: context.packageRoot,
760
+ logWarnings: true,
761
+ });
762
+ return {
763
+ paths: result.paths,
764
+ warnings: result.warnings,
765
+ };
766
+ }
767
+ /**
768
+ * Resolve target path from source path and patterns
769
+ * Handles single-level (*) and recursive (**) globs
770
+ */
771
+ resolveTargetFromGlob(sourcePath, fromPattern, toPattern, context) {
772
+ // Get relative path from package root
773
+ const relativePath = path.relative(context.packageRoot, sourcePath);
774
+ // If 'to' pattern has glob, map the structure
775
+ if (toPattern.includes('*')) {
776
+ // Handle ** recursive patterns
777
+ if (fromPattern.includes('**') && toPattern.includes('**')) {
778
+ // Extract the base directories before **
779
+ const fromParts = fromPattern.split('**');
780
+ const toParts = toPattern.split('**');
781
+ const fromBase = fromParts[0].replace(/\/$/, '');
782
+ const toBase = toParts[0].replace(/\/$/, '');
783
+ // Get the file pattern after **
784
+ const fromSuffix = fromParts[1] || '';
785
+ const toSuffix = toParts[1] || '';
786
+ // Extract the relative path after the base directory
787
+ let relativeSubpath = relativePath;
788
+ if (fromBase) {
789
+ relativeSubpath = relativePath.startsWith(fromBase + '/')
790
+ ? relativePath.slice(fromBase.length + 1)
791
+ : relativePath;
792
+ }
793
+ // Handle extension mapping if suffixes specify extensions
794
+ // e.g., /**/*.md -> /**/*.mdc
795
+ if (fromSuffix && toSuffix) {
796
+ const fromExt = fromSuffix.replace(/^\/?\*+/, '');
797
+ const toExt = toSuffix.replace(/^\/?\*+/, '');
798
+ if (fromExt && toExt && fromExt !== toExt) {
799
+ relativeSubpath = relativeSubpath.replace(new RegExp(fromExt.replace('.', '\\.') + '$'), toExt);
800
+ }
801
+ }
802
+ // Build target path
803
+ const targetPath = toBase ? path.join(toBase, relativeSubpath) : relativeSubpath;
804
+ return path.join(context.workspaceRoot, targetPath);
805
+ }
806
+ // Handle single-level * patterns
807
+ const sourceFileName = path.basename(sourcePath);
808
+ const sourceExt = path.extname(sourcePath);
809
+ const sourceBase = path.basename(sourcePath, sourceExt);
810
+ const toParts = toPattern.split('*');
811
+ const toPrefix = toParts[0];
812
+ const toSuffix = toParts[1] || '';
813
+ const targetExt = toSuffix.startsWith('.') ? toSuffix : (sourceExt + toSuffix);
814
+ const targetFileName = sourceBase + targetExt;
815
+ const resolvedTo = toPrefix + targetFileName;
816
+ return path.join(context.workspaceRoot, resolvedTo);
817
+ }
818
+ // No glob in target - use as-is
819
+ return path.join(context.workspaceRoot, toPattern);
820
+ }
821
+ /**
822
+ * Get pipeline description
823
+ */
824
+ getPipeline(flow) {
825
+ const pipeline = ['load'];
826
+ if (flow.path)
827
+ pipeline.push('extract');
828
+ if (flow.pick)
829
+ pipeline.push('pick');
830
+ if (flow.omit)
831
+ pipeline.push('omit');
832
+ if (flow.map)
833
+ pipeline.push('map');
834
+ if (flow.embed)
835
+ pipeline.push('embed');
836
+ if (flow.merge)
837
+ pipeline.push(`merge:${flow.merge}`);
838
+ pipeline.push('write');
839
+ return pipeline;
840
+ }
841
+ /**
842
+ * Normalize from pattern for display in results
843
+ * Converts array to comma-separated string
844
+ */
845
+ normalizeFromPattern(pattern) {
846
+ return Array.isArray(pattern) ? pattern.join(', ') : pattern;
847
+ }
848
+ /**
849
+ * Get the first pattern from a pattern or array of patterns
850
+ * Used for path resolution when multiple sources are specified
851
+ */
852
+ getFirstPattern(pattern) {
853
+ return Array.isArray(pattern) ? pattern[0] : pattern;
854
+ }
855
+ /**
856
+ * Aggregate multi-target results
857
+ */
858
+ aggregateResults(results, startTime) {
859
+ const successful = results.filter(r => r.success);
860
+ const failed = results.filter(r => !r.success);
861
+ if (failed.length > 0) {
862
+ return {
863
+ source: results[0]?.source || '',
864
+ target: results.map(r => r.target).join(', '),
865
+ success: false,
866
+ transformed: results.some(r => r.transformed),
867
+ error: failed[0]?.error,
868
+ warnings: results.flatMap(r => r.warnings || []),
869
+ executionTime: Date.now() - startTime,
870
+ };
871
+ }
872
+ return {
873
+ source: results[0]?.source || '',
874
+ target: results.flatMap(r => typeof r.target === 'string' ? [r.target] : r.target),
875
+ success: true,
876
+ transformed: results.some(r => r.transformed),
877
+ warnings: results.flatMap(r => r.warnings || []),
878
+ conflicts: results.flatMap(r => r.conflicts || []),
879
+ executionTime: Date.now() - startTime,
880
+ };
881
+ }
882
+ /**
883
+ * Parse markdown with frontmatter
884
+ */
885
+ parseMarkdown(content) {
886
+ const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
887
+ const match = content.match(frontmatterRegex);
888
+ if (match) {
889
+ const frontmatter = yaml.load(match[1]);
890
+ const body = match[2];
891
+ return { frontmatter, body };
892
+ }
893
+ return { body: content };
894
+ }
895
+ /**
896
+ * Serialize markdown with frontmatter
897
+ */
898
+ serializeMarkdown(content) {
899
+ if (typeof content === 'string') {
900
+ return content;
901
+ }
902
+ if (content.frontmatter) {
903
+ const frontmatterStr = yaml.dump(content.frontmatter, { indent: 2 });
904
+ return `---\n${frontmatterStr}---\n${content.body || ''}`;
905
+ }
906
+ return content.body || '';
907
+ }
908
+ /**
909
+ * Strip JSON comments
910
+ */
911
+ stripJSONComments(content) {
912
+ // Remove single-line comments
913
+ let result = content.replace(/\/\/.*$/gm, '');
914
+ // Remove multi-line comments
915
+ result = result.replace(/\/\*[\s\S]*?\*\//g, '');
916
+ return result;
917
+ }
918
+ /**
919
+ * Get nested value using dot notation (delegates to key mapper)
920
+ */
921
+ getNestedValue(obj, path) {
922
+ return getNestedValue(obj, path);
923
+ }
924
+ /**
925
+ * Set nested value using dot notation (delegates to key mapper)
926
+ */
927
+ setNestedValue(obj, path, value) {
928
+ setNestedValue(obj, path, value);
929
+ }
930
+ /**
931
+ * Delete nested value using dot notation (delegates to key mapper)
932
+ */
933
+ deleteNestedValue(obj, path) {
934
+ deleteNestedValue(obj, path);
935
+ }
936
+ /**
937
+ * Apply Codex-specific TOML formatting
938
+ * Converts nested table sections to inline format for http_headers and env_http_headers
939
+ */
940
+ applyCodexTomlFormatting(toml) {
941
+ const inlineKeys = ['http_headers', 'env_http_headers'];
942
+ let result = toml;
943
+ for (const key of inlineKeys) {
944
+ // Pattern to match nested table sections for the key
945
+ const pattern = new RegExp(`\\[([\\w-]+(?:\\.[\\w-]+|\\."[^"]+")*)?\\.${key}\\]\\s*\\n([\\s\\S]*?)(?=\\n\\[|\\n*$)`, 'g');
946
+ result = result.replace(pattern, (match, parentPath, content) => {
947
+ const pairs = [];
948
+ const lines = content.trim().split('\n');
949
+ for (const line of lines) {
950
+ const trimmed = line.trim();
951
+ if (!trimmed || trimmed.startsWith('#'))
952
+ continue;
953
+ const kvMatch = trimmed.match(/^([\w-]+)\s*=\s*(.+)$/);
954
+ if (kvMatch) {
955
+ const [, k, v] = kvMatch;
956
+ pairs.push(`"${k}" = ${v}`);
957
+ }
958
+ }
959
+ if (pairs.length === 0)
960
+ return match;
961
+ const inlineTable = `{ ${pairs.join(', ')} }`;
962
+ return `${key} = ${inlineTable}`;
963
+ });
964
+ }
965
+ return result;
966
+ }
967
+ }
968
+ /**
969
+ * Create a flow executor instance
970
+ */
971
+ export function createFlowExecutor() {
972
+ return new DefaultFlowExecutor();
973
+ }
974
+ //# sourceMappingURL=flow-executor.js.map