opkg 0.5.0 → 0.6.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 (397) hide show
  1. package/README.md +82 -21
  2. package/dist/commands/add.js +11 -276
  3. package/dist/commands/add.js.map +1 -1
  4. package/dist/commands/duplicate.js +5 -2
  5. package/dist/commands/duplicate.js.map +1 -1
  6. package/dist/commands/init.js +76 -145
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/install.js +30 -671
  9. package/dist/commands/install.js.map +1 -1
  10. package/dist/commands/list.js +9 -3
  11. package/dist/commands/list.js.map +1 -1
  12. package/dist/commands/login.js +58 -0
  13. package/dist/commands/login.js.map +1 -0
  14. package/dist/commands/logout.js +28 -0
  15. package/dist/commands/logout.js.map +1 -0
  16. package/dist/commands/pack.js +10 -137
  17. package/dist/commands/pack.js.map +1 -1
  18. package/dist/commands/pull.js +5 -299
  19. package/dist/commands/pull.js.map +1 -1
  20. package/dist/commands/push.js +5 -239
  21. package/dist/commands/push.js.map +1 -1
  22. package/dist/commands/save.js +29 -168
  23. package/dist/commands/save.js.map +1 -1
  24. package/dist/commands/show.js +18 -5
  25. package/dist/commands/show.js.map +1 -1
  26. package/dist/commands/status.js +21 -8
  27. package/dist/commands/status.js.map +1 -1
  28. package/dist/commands/tui.js +61 -0
  29. package/dist/commands/tui.js.map +1 -0
  30. package/dist/commands/uninstall.js +5 -5
  31. package/dist/commands/uninstall.js.map +1 -1
  32. package/dist/constants/index.js +19 -45
  33. package/dist/constants/index.js.map +1 -1
  34. package/dist/constants/workspace.js +9 -0
  35. package/dist/constants/workspace.js.map +1 -0
  36. package/dist/core/add/add-conflict-handler.js +68 -0
  37. package/dist/core/add/add-conflict-handler.js.map +1 -0
  38. package/dist/core/add/add-pipeline.js +137 -0
  39. package/dist/core/add/add-pipeline.js.map +1 -0
  40. package/dist/core/add/package-index-updater.js +66 -36
  41. package/dist/core/add/package-index-updater.js.map +1 -1
  42. package/dist/core/add/platform-path-transformer.js +47 -0
  43. package/dist/core/add/platform-path-transformer.js.map +1 -0
  44. package/dist/core/add/source-collector.js +57 -0
  45. package/dist/core/add/source-collector.js.map +1 -0
  46. package/dist/core/api-keys.js +6 -2
  47. package/dist/core/api-keys.js.map +1 -1
  48. package/dist/core/auth.js +25 -40
  49. package/dist/core/auth.js.map +1 -1
  50. package/dist/core/config.js +11 -3
  51. package/dist/core/config.js.map +1 -1
  52. package/dist/core/dependency-resolver.js +24 -11
  53. package/dist/core/dependency-resolver.js.map +1 -1
  54. package/dist/core/device-auth.js +81 -0
  55. package/dist/core/device-auth.js.map +1 -0
  56. package/dist/core/directory.js +15 -9
  57. package/dist/core/directory.js.map +1 -1
  58. package/dist/core/discovery/file-discovery.js +55 -54
  59. package/dist/core/discovery/file-discovery.js.map +1 -1
  60. package/dist/core/discovery/platform-files-discovery.js +32 -17
  61. package/dist/core/discovery/platform-files-discovery.js.map +1 -1
  62. package/dist/core/install/bulk-install-pipeline.js +200 -0
  63. package/dist/core/install/bulk-install-pipeline.js.map +1 -0
  64. package/dist/core/install/canonical-plan.js +129 -0
  65. package/dist/core/install/canonical-plan.js.map +1 -0
  66. package/dist/core/install/download-keys.js +2 -2
  67. package/dist/core/install/download-keys.js.map +1 -1
  68. package/dist/core/install/dry-run.js +2 -2
  69. package/dist/core/install/dry-run.js.map +1 -1
  70. package/dist/core/install/index.js +3 -0
  71. package/dist/core/install/index.js.map +1 -0
  72. package/dist/core/install/install-errors.js +41 -0
  73. package/dist/core/install/install-errors.js.map +1 -0
  74. package/dist/core/install/install-flow.js +8 -9
  75. package/dist/core/install/install-flow.js.map +1 -1
  76. package/dist/core/install/install-pipeline.js +296 -0
  77. package/dist/core/install/install-pipeline.js.map +1 -0
  78. package/dist/core/install/install-reporting.js +99 -0
  79. package/dist/core/install/install-reporting.js.map +1 -0
  80. package/dist/core/install/platform-resolution.js +6 -6
  81. package/dist/core/install/platform-resolution.js.map +1 -1
  82. package/dist/core/install/remote-flow.js +84 -7
  83. package/dist/core/install/remote-flow.js.map +1 -1
  84. package/dist/core/install/version-selection.js +25 -8
  85. package/dist/core/install/version-selection.js.map +1 -1
  86. package/dist/core/openpackage.js +22 -14
  87. package/dist/core/openpackage.js.map +1 -1
  88. package/dist/core/package-context.js +246 -0
  89. package/dist/core/package-context.js.map +1 -0
  90. package/dist/core/package.js +73 -9
  91. package/dist/core/package.js.map +1 -1
  92. package/dist/core/platforms.js +126 -217
  93. package/dist/core/platforms.js.map +1 -1
  94. package/dist/core/profiles.js +60 -3
  95. package/dist/core/profiles.js.map +1 -1
  96. package/dist/core/pull/pull-errors.js +62 -0
  97. package/dist/core/pull/pull-errors.js.map +1 -0
  98. package/dist/core/pull/pull-options.js +2 -0
  99. package/dist/core/pull/pull-options.js.map +1 -0
  100. package/dist/core/pull/pull-output.js +50 -0
  101. package/dist/core/pull/pull-output.js.map +1 -0
  102. package/dist/core/pull/pull-pipeline.js +141 -0
  103. package/dist/core/pull/pull-pipeline.js.map +1 -0
  104. package/dist/core/pull/pull-strategies.js +103 -0
  105. package/dist/core/pull/pull-strategies.js.map +1 -0
  106. package/dist/core/pull/pull-types.js +2 -0
  107. package/dist/core/pull/pull-types.js.map +1 -0
  108. package/dist/core/push/push-context.js +95 -0
  109. package/dist/core/push/push-context.js.map +1 -0
  110. package/dist/core/push/push-errors.js +133 -0
  111. package/dist/core/push/push-errors.js.map +1 -0
  112. package/dist/core/push/push-output.js +44 -0
  113. package/dist/core/push/push-output.js.map +1 -0
  114. package/dist/core/push/push-pipeline.js +74 -0
  115. package/dist/core/push/push-pipeline.js.map +1 -0
  116. package/dist/core/push/push-single-file.js +56 -0
  117. package/dist/core/push/push-single-file.js.map +1 -0
  118. package/dist/core/push/push-types.js +2 -0
  119. package/dist/core/push/push-types.js.map +1 -0
  120. package/dist/core/push/push-upload.js +68 -0
  121. package/dist/core/push/push-upload.js.map +1 -0
  122. package/dist/core/registry/registry-rename.js +2 -1
  123. package/dist/core/registry/registry-rename.js.map +1 -1
  124. package/dist/core/registry.js +14 -5
  125. package/dist/core/registry.js.map +1 -1
  126. package/dist/core/remote-pull.js +165 -36
  127. package/dist/core/remote-pull.js.map +1 -1
  128. package/dist/core/save/constants.js +4 -0
  129. package/dist/core/save/constants.js.map +1 -1
  130. package/dist/core/save/name-resolution.js +31 -0
  131. package/dist/core/save/name-resolution.js.map +1 -0
  132. package/dist/core/save/package-detection.js +147 -0
  133. package/dist/core/save/package-detection.js.map +1 -0
  134. package/dist/core/save/package-saver.js +82 -43
  135. package/dist/core/save/package-saver.js.map +1 -1
  136. package/dist/core/save/package-yml-generator.js +51 -71
  137. package/dist/core/save/package-yml-generator.js.map +1 -1
  138. package/dist/core/save/root-save-candidates.js.map +1 -1
  139. package/dist/core/save/save-candidate-loader.js +89 -0
  140. package/dist/core/save/save-candidate-loader.js.map +1 -0
  141. package/dist/core/save/save-conflict-resolution.js +72 -410
  142. package/dist/core/save/save-conflict-resolution.js.map +1 -1
  143. package/dist/core/save/save-conflict-resolver.js +277 -0
  144. package/dist/core/save/save-conflict-resolver.js.map +1 -0
  145. package/dist/core/save/save-pipeline.js +162 -0
  146. package/dist/core/save/save-pipeline.js.map +1 -0
  147. package/dist/core/save/save-single-file.js +124 -0
  148. package/dist/core/save/save-single-file.js.map +1 -0
  149. package/dist/core/save/save-types.js +2 -0
  150. package/dist/core/save/save-types.js.map +1 -0
  151. package/dist/core/save/save-yml-resolution.js +77 -39
  152. package/dist/core/save/save-yml-resolution.js.map +1 -1
  153. package/dist/core/save/workspace-rename.js +6 -6
  154. package/dist/core/save/workspace-rename.js.map +1 -1
  155. package/dist/core/scoping/package-scoping.js +14 -2
  156. package/dist/core/scoping/package-scoping.js.map +1 -1
  157. package/dist/core/status/status-file-discovery.js +12 -30
  158. package/dist/core/status/status-file-discovery.js.map +1 -1
  159. package/dist/core/sync/platform-sync.js +7 -4
  160. package/dist/core/sync/platform-sync.js.map +1 -1
  161. package/dist/core/sync/root-files-sync.js +59 -1
  162. package/dist/core/sync/root-files-sync.js.map +1 -1
  163. package/dist/core/token-store.js +73 -0
  164. package/dist/core/token-store.js.map +1 -0
  165. package/dist/core/uninstall/uninstall-file-discovery.js +1 -2
  166. package/dist/core/uninstall/uninstall-file-discovery.js.map +1 -1
  167. package/dist/index.js +4 -0
  168. package/dist/index.js.map +1 -1
  169. package/dist/tui/app.js +95 -0
  170. package/dist/tui/app.js.map +1 -0
  171. package/dist/tui/components/package-list.js +73 -0
  172. package/dist/tui/components/package-list.js.map +1 -0
  173. package/dist/tui/controller.js +365 -0
  174. package/dist/tui/controller.js.map +1 -0
  175. package/dist/tui/index.js +12 -0
  176. package/dist/tui/index.js.map +1 -0
  177. package/dist/tui/services/file-index.js +64 -0
  178. package/dist/tui/services/file-index.js.map +1 -0
  179. package/dist/tui/services/packages.js +18 -0
  180. package/dist/tui/services/packages.js.map +1 -0
  181. package/dist/tui/services/save.js +21 -0
  182. package/dist/tui/services/save.js.map +1 -0
  183. package/dist/tui/state/app-state.js +15 -0
  184. package/dist/tui/state/app-state.js.map +1 -0
  185. package/dist/tui/state.js +17 -0
  186. package/dist/tui/state.js.map +1 -0
  187. package/dist/tui/types.js +2 -0
  188. package/dist/tui/types.js.map +1 -0
  189. package/dist/tui/views/add-file-modal.js +129 -0
  190. package/dist/tui/views/add-file-modal.js.map +1 -0
  191. package/dist/tui/views/file-preview.js +44 -0
  192. package/dist/tui/views/file-preview.js.map +1 -0
  193. package/dist/tui/views/list-packages.js +73 -0
  194. package/dist/tui/views/list-packages.js.map +1 -0
  195. package/dist/tui/views/main-menu.js +29 -0
  196. package/dist/tui/views/main-menu.js.map +1 -0
  197. package/dist/tui/views/manage-view.js +81 -0
  198. package/dist/tui/views/manage-view.js.map +1 -0
  199. package/dist/tui/views/package-hub.js +120 -0
  200. package/dist/tui/views/package-hub.js.map +1 -0
  201. package/dist/tui/views/placeholder.js +24 -0
  202. package/dist/tui/views/placeholder.js.map +1 -0
  203. package/dist/types/index.js.map +1 -1
  204. package/dist/utils/bun-bootstrap.js +72 -0
  205. package/dist/utils/bun-bootstrap.js.map +1 -0
  206. package/dist/utils/file-processing.js +5 -58
  207. package/dist/utils/file-processing.js.map +1 -1
  208. package/dist/utils/formatters.js +3 -1
  209. package/dist/utils/formatters.js.map +1 -1
  210. package/dist/utils/http-client.js +27 -5
  211. package/dist/utils/http-client.js.map +1 -1
  212. package/dist/utils/index-based-installer.js +40 -35
  213. package/dist/utils/index-based-installer.js.map +1 -1
  214. package/dist/utils/install-file-discovery.js +18 -24
  215. package/dist/utils/install-file-discovery.js.map +1 -1
  216. package/dist/utils/install-orchestrator.js +21 -21
  217. package/dist/utils/install-orchestrator.js.map +1 -1
  218. package/dist/utils/jsonc.js +44 -0
  219. package/dist/utils/jsonc.js.map +1 -0
  220. package/dist/utils/manifest-paths.js +27 -0
  221. package/dist/utils/manifest-paths.js.map +1 -0
  222. package/dist/utils/package-copy.js +199 -0
  223. package/dist/utils/package-copy.js.map +1 -0
  224. package/dist/utils/package-filters.js +125 -0
  225. package/dist/utils/package-filters.js.map +1 -0
  226. package/dist/utils/package-index-yml.js +15 -10
  227. package/dist/utils/package-index-yml.js.map +1 -1
  228. package/dist/utils/package-installation.js +4 -113
  229. package/dist/utils/package-installation.js.map +1 -1
  230. package/dist/utils/package-local-files.js +2 -35
  231. package/dist/utils/package-local-files.js.map +1 -1
  232. package/dist/utils/package-management.js +191 -75
  233. package/dist/utils/package-management.js.map +1 -1
  234. package/dist/utils/package-merge.js +48 -0
  235. package/dist/utils/package-merge.js.map +1 -0
  236. package/dist/utils/package-name.js +29 -0
  237. package/dist/utils/package-name.js.map +1 -1
  238. package/dist/utils/package-versioning.js +16 -4
  239. package/dist/utils/package-versioning.js.map +1 -1
  240. package/dist/utils/package-yml.js +41 -12
  241. package/dist/utils/package-yml.js.map +1 -1
  242. package/dist/utils/path-normalization.js +8 -53
  243. package/dist/utils/path-normalization.js.map +1 -1
  244. package/dist/utils/paths.js +17 -9
  245. package/dist/utils/paths.js.map +1 -1
  246. package/dist/utils/platform-file.js +16 -59
  247. package/dist/utils/platform-file.js.map +1 -1
  248. package/dist/utils/platform-mapper.js +29 -41
  249. package/dist/utils/platform-mapper.js.map +1 -1
  250. package/dist/utils/platform-specific-paths.js.map +1 -1
  251. package/dist/utils/platform-utils.js +28 -139
  252. package/dist/utils/platform-utils.js.map +1 -1
  253. package/dist/utils/platform-yaml-merge.js +13 -6
  254. package/dist/utils/platform-yaml-merge.js.map +1 -1
  255. package/dist/utils/prompts.js +0 -31
  256. package/dist/utils/prompts.js.map +1 -1
  257. package/dist/utils/registry-entry-filter.js +38 -24
  258. package/dist/utils/registry-entry-filter.js.map +1 -1
  259. package/dist/utils/registry-paths.js +48 -0
  260. package/dist/utils/registry-paths.js.map +1 -0
  261. package/dist/utils/root-file-installer.js +19 -0
  262. package/dist/utils/root-file-installer.js.map +1 -1
  263. package/dist/utils/root-file-registry.js.map +1 -1
  264. package/dist/utils/tarball.js +6 -2
  265. package/dist/utils/tarball.js.map +1 -1
  266. package/dist/utils/version-ranges.js +11 -8
  267. package/dist/utils/version-ranges.js.map +1 -1
  268. package/package.json +3 -2
  269. package/platforms.jsonc +178 -0
  270. package/specs/auth/auth-device-flow.md +70 -0
  271. package/specs/install/install-behavior.md +30 -0
  272. package/specs/install/package-yml-canonical.md +21 -1
  273. package/specs/install/version-resolution.md +9 -7
  274. package/specs/login/login-device-flow.md +70 -0
  275. package/specs/package/README.md +60 -0
  276. package/specs/package/nested-packages-and-parent-packages.md +79 -0
  277. package/specs/package/package-index-yml.md +171 -0
  278. package/specs/package/package-root-layout.md +78 -0
  279. package/specs/package/registry-payload-and-copy.md +77 -0
  280. package/specs/package/universal-content.md +144 -0
  281. package/specs/platforms.md +193 -0
  282. package/specs/push/push-behavior.md +38 -10
  283. package/specs/push/push-errors-and-hints.md +19 -6
  284. package/specs/push/push-remote-upload.md +3 -0
  285. package/specs/push/push-scoping.md +14 -22
  286. package/specs/push/push-version-selection.md +18 -16
  287. package/specs/save/README.md +40 -0
  288. package/specs/save/save-conflict-resolution.md +146 -0
  289. package/specs/save/save-file-discovery.md +101 -0
  290. package/specs/save/save-frontmatter-overrides.md +81 -0
  291. package/specs/save/save-modes-inputs.md +55 -0
  292. package/specs/save/save-naming-scoping.md +93 -0
  293. package/specs/save/save-package-detection.md +60 -0
  294. package/specs/save/save-registry-sync.md +126 -0
  295. package/specs/save-pack.md +1 -0
  296. package/dist/commands/release.js +0 -33
  297. package/dist/commands/release.js.map +0 -1
  298. package/dist/commands/tag.js +0 -311
  299. package/dist/commands/tag.js.map +0 -1
  300. package/dist/commands/update.js +0 -30
  301. package/dist/commands/update.js.map +0 -1
  302. package/dist/core/add/formula-index-updater.js +0 -290
  303. package/dist/core/add/formula-index-updater.js.map +0 -1
  304. package/dist/core/discovery/ai-files-discovery.js +0 -2
  305. package/dist/core/discovery/ai-files-discovery.js.map +0 -1
  306. package/dist/core/discovery/formula-files-discovery.js +0 -14
  307. package/dist/core/discovery/formula-files-discovery.js.map +0 -1
  308. package/dist/core/discovery/index-files-discovery.js +0 -91
  309. package/dist/core/discovery/index-files-discovery.js.map +0 -1
  310. package/dist/core/discovery/md-files-discovery.js +0 -82
  311. package/dist/core/discovery/md-files-discovery.js.map +0 -1
  312. package/dist/core/discovery/package-files-discovery.js +0 -14
  313. package/dist/core/discovery/package-files-discovery.js.map +0 -1
  314. package/dist/core/discovery/platform-discovery.js +0 -84
  315. package/dist/core/discovery/platform-discovery.js.map +0 -1
  316. package/dist/core/discovery/root-files-discovery.js +0 -2
  317. package/dist/core/discovery/root-files-discovery.js.map +0 -1
  318. package/dist/core/formula.js +0 -170
  319. package/dist/core/formula.js.map +0 -1
  320. package/dist/core/git-registry.js +0 -46
  321. package/dist/core/git-registry.js.map +0 -1
  322. package/dist/core/groundzero.js +0 -277
  323. package/dist/core/groundzero.js.map +0 -1
  324. package/dist/core/install/scenario.js +0 -11
  325. package/dist/core/install/scenario.js.map +0 -1
  326. package/dist/core/package-sync.js +0 -219
  327. package/dist/core/package-sync.js.map +0 -1
  328. package/dist/core/save/formula-file-generator.js +0 -167
  329. package/dist/core/save/formula-file-generator.js.map +0 -1
  330. package/dist/core/save/formula-saver.js +0 -52
  331. package/dist/core/save/formula-saver.js.map +0 -1
  332. package/dist/core/save/formula-yml-generator.js +0 -89
  333. package/dist/core/save/formula-yml-generator.js.map +0 -1
  334. package/dist/core/save/formula-yml-versioning.js +0 -108
  335. package/dist/core/save/formula-yml-versioning.js.map +0 -1
  336. package/dist/core/save/generic-file-sync.js +0 -38
  337. package/dist/core/save/generic-file-sync.js.map +0 -1
  338. package/dist/core/save/md-files-sync.js +0 -33
  339. package/dist/core/save/md-files-sync.js.map +0 -1
  340. package/dist/core/save/package-yml-versioning.js +0 -108
  341. package/dist/core/save/package-yml-versioning.js.map +0 -1
  342. package/dist/core/save/platform-sync.js +0 -95
  343. package/dist/core/save/platform-sync.js.map +0 -1
  344. package/dist/core/save/root-files-sync.js +0 -140
  345. package/dist/core/save/root-files-sync.js.map +0 -1
  346. package/dist/core/save/save-candidate-types.js +0 -2
  347. package/dist/core/save/save-candidate-types.js.map +0 -1
  348. package/dist/core/save/save-conflict-types.js +0 -2
  349. package/dist/core/save/save-conflict-types.js.map +0 -1
  350. package/dist/core/save/save-file-discovery.js +0 -5
  351. package/dist/core/save/save-file-discovery.js.map +0 -1
  352. package/dist/core/status-file-discovery.js +0 -175
  353. package/dist/core/status-file-discovery.js.map +0 -1
  354. package/dist/utils/discovery/file-processing.js +0 -156
  355. package/dist/utils/discovery/file-processing.js.map +0 -1
  356. package/dist/utils/discovery/formula-discovery.js +0 -211
  357. package/dist/utils/discovery/formula-discovery.js.map +0 -1
  358. package/dist/utils/discovery/platform-discovery.js +0 -2
  359. package/dist/utils/discovery/platform-discovery.js.map +0 -1
  360. package/dist/utils/formula-discovery.js +0 -102
  361. package/dist/utils/formula-discovery.js.map +0 -1
  362. package/dist/utils/formula-index-yml.js +0 -122
  363. package/dist/utils/formula-index-yml.js.map +0 -1
  364. package/dist/utils/formula-installation.js +0 -110
  365. package/dist/utils/formula-installation.js.map +0 -1
  366. package/dist/utils/formula-local-files.js +0 -38
  367. package/dist/utils/formula-local-files.js.map +0 -1
  368. package/dist/utils/formula-management.js +0 -191
  369. package/dist/utils/formula-management.js.map +0 -1
  370. package/dist/utils/formula-name.js +0 -97
  371. package/dist/utils/formula-name.js.map +0 -1
  372. package/dist/utils/formula-versioning.js +0 -109
  373. package/dist/utils/formula-versioning.js.map +0 -1
  374. package/dist/utils/formula-yml.js +0 -82
  375. package/dist/utils/formula-yml.js.map +0 -1
  376. package/dist/utils/git.js +0 -54
  377. package/dist/utils/git.js.map +0 -1
  378. package/dist/utils/id-based-discovery.js +0 -126
  379. package/dist/utils/id-based-discovery.js.map +0 -1
  380. package/dist/utils/id-based-installer.js +0 -249
  381. package/dist/utils/id-based-installer.js.map +0 -1
  382. package/dist/utils/index-yml-based-installer.js +0 -375
  383. package/dist/utils/index-yml-based-installer.js.map +0 -1
  384. package/dist/utils/index-yml.js +0 -124
  385. package/dist/utils/index-yml.js.map +0 -1
  386. package/dist/utils/md-frontmatter.js +0 -3
  387. package/dist/utils/md-frontmatter.js.map +0 -1
  388. package/dist/utils/package-link-yml.js +0 -92
  389. package/dist/utils/package-link-yml.js.map +0 -1
  390. package/dist/utils/platform-discovery.js +0 -2
  391. package/dist/utils/platform-discovery.js.map +0 -1
  392. package/dist/utils/platform-frontmatter-split.js +0 -15
  393. package/dist/utils/platform-frontmatter-split.js.map +0 -1
  394. package/dist/utils/timestamp-encoder.js +0 -13
  395. package/dist/utils/timestamp-encoder.js.map +0 -1
  396. package/dist/utils/wip-versioning.js +0 -24
  397. package/dist/utils/wip-versioning.js.map +0 -1
@@ -3,7 +3,10 @@
3
3
  ### Overview
4
4
 
5
5
  The `opkg push` command uploads a local package version from the **local registry** to the **remote registry**.
6
- It is **strictly limited to stable versions** (no prerelease versions like `1.2.3-dev.abc`).
6
+ It allows:
7
+ - **Stable versions** (`x.y.z`).
8
+ - **Unversioned packages** (when `package.yml` omits `version`, represented as `0.0.0`).
9
+ It still rejects prerelease versions like `1.2.3-dev.abc`.
7
10
 
8
11
  This document focuses on user-facing behavior:
9
12
  - CLI shapes and arguments.
@@ -20,11 +23,15 @@ This document focuses on user-facing behavior:
20
23
  - **Package syntax**:
21
24
  - `<name>` – package name, optionally unscoped.
22
25
  - `<name>@<version>` – optional explicit version.
26
+ - `<name@version>/<registry-path>` – partial push of specific registry paths.
27
+ - `--paths <list>` – comma-separated registry paths for partial push.
23
28
 
24
29
  Examples:
25
30
  - `opkg push my-pack`
26
31
  - `opkg push @scope/my-pack`
27
32
  - `opkg push my-pack@1.2.3`
33
+ - `opkg push @scope/my-pack/specs/readme.md` (partial push of a single file)
34
+ - `opkg push @scope/my-pack@1.2.3 --paths specs/readme.md,specs/guide.md`
28
35
 
29
36
  ---
30
37
 
@@ -57,24 +64,25 @@ Examples:
57
64
 
58
65
  ## Implicit version behavior: `opkg push <pkg>`
59
66
 
60
- When no version is specified, the command **only considers stable versions**.
67
+ When no version is specified, the command prefers **stable versions** and can fall back to a **`0.0.0`** package if no stable exists.
61
68
 
62
69
  High-level flow:
63
70
 
64
71
  1. Discover all versions of `<pkg>` from the local registry.
65
72
  2. Compute the latest **stable** version.
66
- 3. If no stable versions exist:
73
+ 3. If no stable versions exist but a **`0.0.0`** entry exists:
74
+ - Use the `0.0.0` package as the candidate.
75
+ 4. If neither stable nor unversioned exists:
67
76
  - Inform the user and exit **gracefully** (non-error).
68
- 4. If a stable version exists:
69
- - Prompt the user to confirm pushing that version.
77
+ 5. If a candidate exists:
78
+ - Prompt the user to confirm pushing that candidate.
70
79
 
71
80
  **Details**
72
81
 
73
- - If no stable versions are found:
74
- - The CLI prints:
75
- - `❌ No stable versions found for package '<pkg>'`
76
- - `💡 Stable versions can be created using "opkg pack <package>".`
77
- - The command exits with a **success** result (no global error message).
82
+ - If no stable versions are found but a `0.0.0` entry exists:
83
+ - The CLI notes it will push the `0.0.0` package.
84
+ - If no stable versions and no unversioned entry:
85
+ - The CLI prints the existing “no stable versions” message and exits successfully.
78
86
  - If a stable version (e.g. `1.2.3`) is found:
79
87
  - The CLI prompts:
80
88
  - `Push latest stable version '1.2.3'?` (default: yes).
@@ -91,6 +99,26 @@ High-level flow:
91
99
 
92
100
  ---
93
101
 
102
+ ## Partial push behavior (paths)
103
+
104
+ - Partial pushes upload only specific registry paths from an existing local package version.
105
+ - Paths can be provided via:
106
+ - `<pkg[@ver]>/<registry-path>`
107
+ - `--paths specs/readme.md,specs/guide.md`
108
+ - Behavior:
109
+ 1. Scope resolution and version selection run first (explicit or latest-stable).
110
+ 2. Requested paths are normalized and validated against the local package files.
111
+ - Missing paths fail the push with a clear missing-path message.
112
+ 3. Tarball is narrowed to:
113
+ - The requested file set.
114
+ - `.openpackage/package.yml`.
115
+ 4. Upload uses the standard `/packages/push` endpoint.
116
+
117
+ Notes:
118
+ - This replaces the previous single-file `f` flow; single-file pushes are just partial pushes with one path.
119
+ - Manifest is required; if `.openpackage/package.yml` is missing locally, the CLI errors.
120
+
121
+
94
122
  ## Stable-only guarantees (behavioral view)
95
123
 
96
124
  From the user’s perspective:
@@ -79,6 +79,16 @@ The following behavior is preserved from the broader CLI error-handling design,
79
79
  - The command returns an error result:
80
80
  - `error: "Version not found"`.
81
81
 
82
+ ### Requested path not found (partial push)
83
+
84
+ - Condition:
85
+ - User requests a partial push (via `--paths` or `<pkg@ver>/<registry-path>`) and one or more paths are missing locally.
86
+ - Behavior:
87
+ - The CLI prints, for each missing path:
88
+ - `❌ Path '<path>' not found in local registry for '<pkg>@<version>'`
89
+ - The command returns an error result:
90
+ - `error: "Requested path not found in local registry"`.
91
+
82
92
  ### Explicit prerelease version
83
93
 
84
94
  - Condition:
@@ -97,12 +107,15 @@ The following behavior is preserved from the broader CLI error-handling design,
97
107
  - User runs `opkg push <pkg>` without specifying a version.
98
108
  - No **stable** versions of `<pkg>` exist in the local registry.
99
109
  - Behavior:
100
- - The CLI prints:
101
- - `❌ No stable versions found for package '<pkg>'`
102
- - `💡 Stable versions can be created using "opkg pack <package>".`
103
- - The command returns a **success** result (no error string), so the global error handler:
104
- - Does **not** print an additional plain `No stable versions found` line.
105
- - This is treated as an informational exit: the user simply needs to create a stable version first.
110
+ - If a **`0.0.0`** entry exists:
111
+ - The CLI attempts to push the `0.0.0` package.
112
+ - If no `0.0.0` entry exists:
113
+ - The CLI prints:
114
+ - `❌ No stable versions found for package '<pkg>'`
115
+ - `💡 Stable versions can be created using "opkg pack <package>".`
116
+ - The command returns a **success** result (no error string), so the global error handler:
117
+ - Does **not** print an additional plain `No stable versions found` line.
118
+ - This is treated as an informational exit: the user needs to create a stable (or unversioned) package first.
106
119
 
107
120
  ---
108
121
 
@@ -41,6 +41,9 @@ Assuming a valid stable package `pkg` and `versionToPush` have been selected:
41
41
  4. **Tarball creation**
42
42
 
43
43
  - `createTarballFromPackage(pkg)` builds a tarball from the package files.
44
+ - For **partial pushes** (paths provided via spec or `--paths`):
45
+ - The tarball is narrowed to only the requested registry paths plus `.openpackage/package.yml`.
46
+ - File count reflects only the selected files.
44
47
  - The CLI prints:
45
48
  - `✓ Creating tarball...`
46
49
  - `✓ Created tarball (<file-count> files, <formatted-size>)`
@@ -5,44 +5,36 @@
5
5
  The `opkg push` command may need to **scope** an unscoped package before pushing it.
6
6
  Scoping ensures that packages in the remote registry are properly namespaced, e.g. `@user/package`.
7
7
 
8
- This document describes how `push` handles scoping and how it keeps the local registry and workspace in sync.
8
+ This document describes how `push` handles scoping for upload. Scoping is applied **only to the upload payload**; the local registry and workspace stay unchanged.
9
9
 
10
10
  ---
11
11
 
12
- ## Scope handling
12
+ ## Scope handling (upload-only)
13
13
 
14
- 1. The command looks up `<package-name>` in the local registry.
14
+ 1. The command looks up `<package-name>` in the local registry using the **input name** (unscoped is allowed).
15
15
  2. If the name is **unscoped** (e.g. `test`):
16
16
  - Authentication is validated using the provided profile/API key.
17
17
  - The current username (or profile’s default scope) is resolved.
18
- - A scoped name is computed (e.g. `@user/test`).
19
- - The local registry package is renamed to the scoped name using `renameRegistryPackage`.
20
- - The workspace package is updated (where possible) using `tryRenameWorkspacePackage`, so:
21
- - `package.yml` and related workspace configuration reflect the new scoped name.
22
- 3. After scoping:
23
- - **All further logic** (version resolution, checks, push) operates on the **scoped name**.
18
+ - A scoped upload name is computed (e.g. `@user/test`) via the existing prompt/default-scope flow.
19
+ - No local rename occurs; the local registry and workspace remain on the unscoped name.
20
+ 3. Before tarball creation:
21
+ - The package is cloned in-memory and its `.openpackage/package.yml` `name` field is rewritten to the scoped upload name.
22
+ - The upload payload (full or partial) uses this in-memory manifest, so the remote receives the scoped identity.
23
+ 4. Version selection and path validation still operate on the local name and local files.
24
24
 
25
25
  ---
26
26
 
27
27
  ## Invariants
28
28
 
29
- - After a successful scope operation:
30
- - The local registry no longer stores the unscoped name as the active location.
31
- - The scoped name (e.g. `@user/test`) is the canonical identity used by:
32
- - Version selection.
33
- - Tarball creation.
34
- - Remote upload.
35
- - The workspace is updated where possible so that:
36
- - Future `save`, `pack`, and `push` operations use the scoped name naturally.
37
- - References within the workspace (e.g. `package.yml`) do not drift from the registry identity.
29
+ - The local registry and workspace are **not** renamed by `push`; the unscoped layout remains intact.
30
+ - The upload payload always carries a scoped name (manifest rewritten in-memory) when pushing an unscoped package.
31
+ - Version selection and missing-path validation use the local name and files; only the upload name changes for remote interaction.
38
32
 
39
33
  ---
40
34
 
41
35
  ## Relationship to version selection
42
36
 
43
- - Scoping happens **before** version selection.
44
- - Once the package name is scoped:
45
- - All lookups for versions (`listPackageVersions`, `packageManager.loadPackage`) use the scoped name.
46
- - The version-selection rules (see `push-version-selection.md`) are applied strictly to the scoped name.
37
+ - Scoping for upload is determined before version selection, but local lookups use the **input name**.
38
+ - Version selection (`listPackageVersions`, `packageManager.loadPackage`) is driven by the local name; only the upload payload uses the scoped name.
47
39
 
48
40
 
@@ -1,9 +1,10 @@
1
- ## Push version selection (stable-only)
1
+ ## Push version selection (stable + unversioned)
2
2
 
3
3
  ### Terminology
4
4
 
5
5
  - **Stable version**: A semver-valid version with no prerelease segment, e.g. `1.2.3`.
6
6
  - **Prerelease version**: A semver-valid version with a prerelease segment, e.g. `1.2.3-dev.abc123`.
7
+ - **Unversioned package**: A package whose `package.yml` omits `version`; represented and stored as semver `0.0.0` locally (one per package) and can be pushed like any other stable version.
7
8
  - **Local registry**: The on-disk package store used by `opkg` (managed via `packageManager`).
8
9
 
9
10
  This document defines the rules `opkg push` uses to decide **which local version** is eligible to be pushed to the remote registry.
@@ -29,8 +30,7 @@ Given `<pkg>@<version>`:
29
30
 
30
31
  3. **Safety check**
31
32
  - After loading, the resulting `pkg.metadata.version` must still be stable.
32
- - If it is not (should only occur in pathological cases):
33
- - The push is rejected as if a prerelease was requested.
33
+ - If it is not (pathological), the push is rejected as a prerelease.
34
34
 
35
35
  **Result**
36
36
 
@@ -42,11 +42,12 @@ Given `<pkg>@<version>`:
42
42
 
43
43
  ## Implicit version: `opkg push <pkg>`
44
44
 
45
- When no version is explicitly supplied, `opkg push` must:
45
+ When no version is explicitly supplied, `opkg push`:
46
46
 
47
- - Consider **all local versions** of `<pkg>`.
48
- - Select the **latest stable version** only.
49
- - Treat the absence of stable versions as a **non-error** (informational) outcome.
47
+ - Considers **all local versions** of `<pkg>`.
48
+ - Prefers the **latest stable** version.
49
+ - If no stable exists but a `0.0.0` entry exists, uses that entry.
50
+ - Treats absence of both stable and `0.0.0` as a **non-error** (informational) outcome.
50
51
 
51
52
  ### Algorithm
52
53
 
@@ -61,28 +62,29 @@ Let `versions = listPackageVersions(pkg)` (all local versions, as directory name
61
62
  - Otherwise, returns the highest stable version using `semver.rsort`.
62
63
 
63
64
  2. If `latestStable === null`:
64
- - There is **no stable version** to push.
65
- - The CLI prints a “no stable versions found” message and a hint to use `opkg pack`.
66
- - The command exits with a **success** result (no error propagated to the global handler).
65
+ - No semver-stable versions exist (including `0.0.0`), so:
66
+ - Print the “no stable versions” message and exit with success.
67
67
 
68
- 3. If `latestStable` is present:
68
+ 3. If `latestStable` is present (including the case where it is `0.0.0`):
69
69
  - That version becomes the candidate `versionToPush`.
70
70
  - The user is asked to confirm before pushing (see behavior spec).
71
71
 
72
72
  **Notes**
73
73
 
74
- - Prerelease-only packages (e.g. `1.0.0-dev.abc`, `1.0.0-dev.def`) result in `latestStable === null`.
74
+ - Prerelease-only packages (e.g. `1.0.0-dev.abc`, `1.0.0-dev.def`) result in `latestStable === null`; if an unversioned entry exists, it is chosen, otherwise informational exit.
75
75
  - Mixed stable and prerelease sets (e.g. `1.0.0`, `1.1.0-dev.abc`, `1.2.0`) always choose the numerically highest stable (`1.2.0`).
76
76
 
77
77
  ---
78
78
 
79
- ## Stable-only guarantees (versioning view)
79
+ ## Stable/unversioned guarantees (versioning view)
80
80
 
81
81
  From the version-selection point of view:
82
82
 
83
- - `push` **never** picks a prerelease version as `versionToPush`.
83
+ - `push` **never** picks a prerelease version.
84
84
  - Explicit prerelease inputs are rejected up front.
85
- - Implicit selection ignores all prerelease versions when computing candidates.
86
- - Existing helper functions (`filterStableVersions`, `getLatestStableVersion`) centralize the definition of stable”.
85
+ - Implicit selection:
86
+ - Prefers latest stable (with `0.0.0` treated as a normal stable version).
87
+ - `0.0.0` pushes are treated the same as other stable versions.
88
+ - Ignores prereleases for candidacy.
87
89
 
88
90
 
@@ -0,0 +1,40 @@
1
+ ### Save Pipeline Specs
2
+
3
+ This directory contains specifications for the **save pipeline** that powers both the `save` (WIP) and `pack` (stable) commands.
4
+
5
+ These docs are **behavioral**: they describe features and logic, not specific modules or functions.
6
+
7
+ ---
8
+
9
+ #### Pipeline Flow
10
+
11
+ The save pipeline executes in this order:
12
+
13
+ 1. **Modes & Inputs** → Determine WIP vs stable mode and parse flags
14
+ 2. **Package Detection** → Find the target package context
15
+ 3. **Naming & Scoping** → Resolve final name, handle renames
16
+ 4. **File Discovery** → Discover candidate files from local and workspace
17
+ 5. **Conflict Resolution** → Decide which content wins for each path
18
+ 6. **Frontmatter & Overrides** → Handle markdown metadata and platform overrides
19
+ 7. **Registry & Sync** → Write to registry, cleanup, and sync to platforms
20
+
21
+ ---
22
+
23
+ #### Files
24
+
25
+ | File | Topic |
26
+ |------|-------|
27
+ | `save-modes-inputs.md` | Overview, WIP vs stable modes, inputs, and flags |
28
+ | `save-package-detection.md` | How the pipeline detects which package to operate on |
29
+ | `save-naming-scoping.md` | Name resolution, scoping decisions, and workspace renames |
30
+ | `save-file-discovery.md` | Candidate sources, first vs subsequent saves, grouping |
31
+ | `save-conflict-resolution.md` | Conflict resolution rules and platform-specific selection |
32
+ | `save-frontmatter-overrides.md` | Markdown frontmatter extraction and YAML overrides |
33
+ | `save-registry-sync.md` | Registry writes, WIP cleanup, and platform sync |
34
+
35
+ ---
36
+
37
+ #### Related Documents
38
+
39
+ - `../save-pack.md` – High‑level split between `save` and `pack` commands.
40
+ - `../save-pack-versioning.md` – Detailed versioning rules for WIP and stable versions.
@@ -0,0 +1,146 @@
1
+ ### Save Pipeline – Conflict Resolution
2
+
3
+ #### 1. Overview
4
+
5
+ When multiple candidates exist for the same registry path, the pipeline must decide which content to use. This document describes the conflict resolution rules.
6
+
7
+ ---
8
+
9
+ #### 2. Resolution Goals
10
+
11
+ For each group, the pipeline decides:
12
+
13
+ - Whether any action is needed (no-op when everything matches).
14
+ - Which content becomes the **universal** package content for that registry path.
15
+ - Which workspace candidates, if any, should be treated as **platform‑specific** sidecars.
16
+
17
+ ---
18
+
19
+ #### 3. Conflict Types
20
+
21
+ There are two main flavors of groups:
22
+
23
+ 1. **Root conflicts** (root documentation file for the package)
24
+ 2. **Regular conflicts** (all other paths)
25
+
26
+ ---
27
+
28
+ #### 4. Root Conflicts
29
+
30
+ For the root package section file (e.g. the unified agents document):
31
+
32
+ ##### Ordering and deduplication
33
+
34
+ - The pipeline orders candidates roughly by:
35
+ - Existing local content first (if any).
36
+ - Workspace candidates by newest modification time, then by display path.
37
+ - Deduplicates candidates by content hash.
38
+
39
+ ##### Single candidate
40
+
41
+ - If there is exactly one unique candidate:
42
+ - That candidate is used as the universal content for the root file.
43
+
44
+ ##### Multiple differing candidates
45
+
46
+ - If the local candidate is newer than or equal to all workspace candidates:
47
+ - The local candidate is selected automatically.
48
+ - Otherwise:
49
+ - With `--force`:
50
+ - The local candidate always wins.
51
+ - Without `--force`:
52
+ - The user is prompted to choose which candidate should become the universal root content.
53
+
54
+ ##### Output
55
+
56
+ - The chosen root content is written into the package's root file location.
57
+ - Platform‑specific root selections (e.g. separate root files for individual platforms) are persisted as separate package files where appropriate.
58
+
59
+ ---
60
+
61
+ #### 5. Regular Conflicts
62
+
63
+ For non‑root paths:
64
+
65
+ ##### Ordering and deduplication
66
+
67
+ - The pipeline again orders and deduplicates candidates.
68
+ - Checks whether any workspace candidate actually **differs** from the local candidate.
69
+
70
+ ##### No local candidate
71
+
72
+ - If the file does not yet exist in the package:
73
+ - The selected workspace candidate becomes the new file content.
74
+
75
+ ##### Identical content
76
+
77
+ - If local and all workspace candidates have identical content:
78
+ - The group is skipped (no changes).
79
+
80
+ ##### Differences with local candidate
81
+
82
+ When there are differences and a local candidate exists:
83
+
84
+ - If the local candidate is newer or as new as any workspace candidate:
85
+ - The local version wins silently (no prompt).
86
+ - If a newer workspace candidate exists:
87
+ - Without `--force`:
88
+ - The user is prompted to choose which candidate should become universal content.
89
+ - With `--force`:
90
+ - The local candidate wins even if workspace content is newer.
91
+
92
+ ---
93
+
94
+ #### 6. Resolution Principles
95
+
96
+ In all cases, the goal is to:
97
+
98
+ - Prefer local content when it is at least as new as workspace content.
99
+ - Ask the user only when a newer workspace change would override local content.
100
+ - Respect an explicit `--force` override in favor of local content.
101
+
102
+ ---
103
+
104
+ #### 7. Platform‑Specific Selection
105
+
106
+ Some workspace candidates are associated with specific platforms (e.g. platform‑specific variants of a shared file).
107
+
108
+ ##### Marking platform-specific candidates
109
+
110
+ - Before choosing the universal content, the user can be offered a chance to:
111
+ - Mark one or more workspace candidates as **platform‑specific**.
112
+ - These marked candidates will be written to platform‑specific registry paths instead of becoming the universal content.
113
+
114
+ ##### After marking
115
+
116
+ - The remaining candidates (local + unmarked workspace candidates) participate in universal conflict resolution as described above.
117
+ - Marked candidates are saved as platform‑specific sidecars if they are not chosen as the universal content.
118
+
119
+ ##### Use case
120
+
121
+ This mechanism lets a user:
122
+
123
+ - Keep a single universal file.
124
+ - Simultaneously maintain richer, platform‑specific versions where needed.
125
+
126
+ ---
127
+
128
+ #### 8. Escalation from YAML Overrides to Full Platform Markdown
129
+
130
+ When a registry path participates in the frontmatter/YAML override pipeline (e.g. `.openpackage/agents/*.md`) **and** the user marks one or more workspace candidates as platform‑specific during conflict resolution:
131
+
132
+ - **Universal body update**
133
+ - The universal markdown file keeps its existing frontmatter.
134
+ - If the selected universal candidate’s body differs, only the **markdown body** is updated.
135
+ - Frontmatter for that path continues to be managed by the YAML override pipeline.
136
+
137
+ - **Escalating a platform to full `.platform.md`**
138
+ - Each marked platform‑specific workspace candidate is written to a platform‑specific markdown path (e.g. `yaml-test.qwen.md`) using the **full candidate content** (frontmatter + body).
139
+ - For root conflicts, only the section body is used (consistent with root handling elsewhere).
140
+
141
+ - **Interaction with YAML overrides**
142
+ - If a platform has an existing YAML override file (e.g. `yaml-test.qwen.yml`) and is escalated to a full `.platform.md`:
143
+ - The corresponding YAML override file is removed as redundant.
144
+ - That platform is removed from the frontmatter merge plan for that registry path.
145
+ - After escalation, the remaining frontmatter/YAML plans (if any) are applied only for platforms that still use YAML overrides, ensuring universal frontmatter is not recomputed based on escalated full‑markdown variants.
146
+
@@ -0,0 +1,101 @@
1
+ ### Save Pipeline – File Discovery
2
+
3
+ #### 1. Overview
4
+
5
+ Before copying a package into the registry, the save pipeline must decide **which files and content** belong to the package. This document covers how candidate files are discovered and organized.
6
+
7
+ ---
8
+
9
+ #### 2. Candidate Sources
10
+
11
+ For each save/pack run, the pipeline considers up to four sets of candidates:
12
+
13
+ ##### Local platform candidates
14
+
15
+ - Files already present in the **package directory** under `.openpackage/packages/<name>/`.
16
+ - Excludes:
17
+ - Internal `package.index.yml` metadata.
18
+ - Certain root marker files (e.g. the unified root agents file) that are handled specially.
19
+ - Only includes paths that are allowed by the registry path rules (e.g. skip internal or unsupported paths).
20
+
21
+ ##### Workspace platform candidates
22
+
23
+ - Files discovered in the **workspace** that can map into package registry paths (e.g. documentation, rules, platform‑specific content).
24
+ - Each discovered file carries:
25
+ - A target registry path.
26
+ - Its full workspace path.
27
+ - A modification time (mtime).
28
+ - Optional platform tag (e.g. a specific platform associated with that file).
29
+
30
+ ##### Local root candidates
31
+
32
+ - Root‑level documentation files that already exist as part of the package (e.g. `AGENTS.md` or platform‑specific root docs in the package directory).
33
+ - Represent root **sections** for a package inside a shared root file.
34
+
35
+ ##### Workspace root candidates
36
+
37
+ - Root‑level files in the workspace (outside the package directory) that contain dedicated sections for packages.
38
+ - These are discovered and turned into candidates representing just the **package's section body** within the larger root file.
39
+
40
+ ---
41
+
42
+ #### 3. Candidate Properties
43
+
44
+ Each candidate includes:
45
+
46
+ - Its **source** (`local` vs `workspace`).
47
+ - The **registry path** it maps to.
48
+ - The file content (or section body, for root candidates).
49
+ - A content hash for deduplication.
50
+ - The last modification time.
51
+ - Optional **platform** information for platform‑specific variants.
52
+
53
+ ---
54
+
55
+ #### 4. First Save vs Subsequent Saves
56
+
57
+ The behavior changes depending on whether the package already has an index (`package.index.yml`) with file mapping information.
58
+
59
+ ##### First save (no index present, or empty file mapping)
60
+
61
+ - The pipeline focuses on **root files** (shared documentation).
62
+ - It:
63
+ - Groups local root candidates and workspace root candidates by registry path.
64
+ - Prompts the user where needed to resolve differences.
65
+ - Writes a unified root file (e.g. `AGENTS.md`) plus any platform‑specific copies.
66
+ - The final set of files for the package snapshot is simply the filtered contents of the package directory after root conflicts have been resolved.
67
+
68
+ ##### Subsequent saves (index present with file mappings)
69
+
70
+ - The pipeline uses `package.index.yml` as a **filter** for which workspace paths are relevant:
71
+ - It builds a set of allowed registry paths and directories based on the index's `files` keys.
72
+ - Workspace candidates whose registry paths are outside this allowed set are ignored, except for root files that are deliberately allowed.
73
+ - It merges local and workspace candidates:
74
+ - Local platform candidates.
75
+ - Local root candidates.
76
+ - Workspace platform candidates filtered by allowed registry paths.
77
+ - Workspace root candidates filtered similarly.
78
+ - These merged candidates are then grouped and passed through conflict resolution.
79
+
80
+ This split ensures that:
81
+
82
+ - The **first** save can safely bootstrap root files and initial content.
83
+ - Later saves don't accidentally pull in arbitrary workspace files that were never part of the package.
84
+
85
+ ---
86
+
87
+ #### 5. Grouping Candidates by Registry Path
88
+
89
+ For conflict resolution, the pipeline groups candidates by their **normalized registry path**:
90
+
91
+ - Each **group** contains:
92
+ - At most one `local` candidate (the current package content for that path).
93
+ - Zero or more `workspace` candidates (workspace versions mapping to the same path).
94
+ - Root and non‑root paths are handled in the same grouping mechanism, but root groups get special conflict rules (see `save-conflict-resolution.md`).
95
+
96
+ Grouping allows the pipeline to reason about each registry path independently:
97
+
98
+ - Whether it has only local content, only workspace content, or both.
99
+ - Whether workspace content is identical to or different from the local content.
100
+ - Whether there are platform‑specific workspace choices for that path.
101
+
@@ -0,0 +1,81 @@
1
+ ### Save Pipeline – Frontmatter and YAML Overrides
2
+
3
+ #### 1. Overview
4
+
5
+ For markdown files, the pipeline manages **frontmatter** and platform‑specific overrides to keep shared metadata centralized while allowing platform-specific behavior.
6
+
7
+ ---
8
+
9
+ #### 2. Workspace Markdown Candidates
10
+
11
+ - For each platform, the latest workspace markdown candidate for a given registry path is considered.
12
+ - The frontmatter is normalized and separated from the markdown body.
13
+
14
+ ---
15
+
16
+ #### 3. Universal Frontmatter Extraction
17
+
18
+ - The pipeline computes frontmatter keys and values that are **identical across all platform entries** for that path.
19
+ - These shared keys form the **universal frontmatter** that should live in the base markdown file.
20
+
21
+ ---
22
+
23
+ #### 4. Platform‑Specific Overrides
24
+
25
+ For each platform:
26
+
27
+ - The per‑platform frontmatter is compared against the universal frontmatter.
28
+ - Only the **difference** per platform is treated as that platform's override, or omitted if empty.
29
+ - Platform overrides are written into **separate YAML files** in a dedicated overrides location.
30
+
31
+ ---
32
+
33
+ #### 5. Conflicts with Existing Overrides
34
+
35
+ When a platform override file already exists for a path:
36
+
37
+ - The pipeline compares existing and new frontmatter, taking modification times into account.
38
+ - Typically:
39
+ - If the newer change comes from the workspace, it is preferred by default.
40
+ - When there is a conflicting but not clearly newer override, the user may be prompted to choose between workspace and existing override content where appropriate.
41
+
42
+ ---
43
+
44
+ #### 6. Resulting Layout
45
+
46
+ - One universal markdown file with shared frontmatter.
47
+ - Zero or more per‑platform YAML override files capturing only the per‑platform differences.
48
+
49
+ This scheme keeps:
50
+
51
+ - Shared metadata centralized in the universal file.
52
+ - Platform‑specific behavior in small, explicit override files.
53
+ - Markdown bodies free from duplication where possible.
54
+
55
+ ---
56
+
57
+ #### 7. Final File Inclusion Rules
58
+
59
+ After all conflicts and frontmatter merges are resolved, the pipeline reads the final contents of the package directory and applies a last round of filtering.
60
+
61
+ ##### Excluded
62
+
63
+ - `package.index.yml`.
64
+ - Internal files that are not considered part of the package content.
65
+
66
+ ##### Included
67
+
68
+ - Paths allowed by the regular registry path rules.
69
+ - Root files (the unified root agents file and related root docs).
70
+ - YAML override files that represent platform‑specific metadata.
71
+ - Root‑level files adjacent to `package.yml` that are intended as part of the package.
72
+
73
+ ---
74
+
75
+ #### 8. Output
76
+
77
+ The resulting list of files, with paths relative to the package directory, is what gets:
78
+
79
+ - Copied into the local registry under the computed version.
80
+ - Used to drive platform sync and any subsequent operations in the save pipeline.
81
+