opkg 0.5.0 → 0.6.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 (272) hide show
  1. package/README.md +49 -8
  2. package/dist/commands/add.js +11 -276
  3. package/dist/commands/add.js.map +1 -1
  4. package/dist/commands/init.js +73 -145
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/commands/install.js +26 -668
  7. package/dist/commands/install.js.map +1 -1
  8. package/dist/commands/pack.js +10 -137
  9. package/dist/commands/pack.js.map +1 -1
  10. package/dist/commands/push.js +14 -8
  11. package/dist/commands/push.js.map +1 -1
  12. package/dist/commands/save.js +12 -167
  13. package/dist/commands/save.js.map +1 -1
  14. package/dist/commands/status.js +2 -2
  15. package/dist/commands/status.js.map +1 -1
  16. package/dist/commands/uninstall.js +5 -5
  17. package/dist/commands/uninstall.js.map +1 -1
  18. package/dist/constants/index.js +18 -45
  19. package/dist/constants/index.js.map +1 -1
  20. package/dist/constants/workspace.js +9 -0
  21. package/dist/constants/workspace.js.map +1 -0
  22. package/dist/core/add/add-conflict-handler.js +68 -0
  23. package/dist/core/add/add-conflict-handler.js.map +1 -0
  24. package/dist/core/add/add-pipeline.js +137 -0
  25. package/dist/core/add/add-pipeline.js.map +1 -0
  26. package/dist/core/add/package-index-updater.js +62 -33
  27. package/dist/core/add/package-index-updater.js.map +1 -1
  28. package/dist/core/add/platform-path-transformer.js +47 -0
  29. package/dist/core/add/platform-path-transformer.js.map +1 -0
  30. package/dist/core/add/source-collector.js +57 -0
  31. package/dist/core/add/source-collector.js.map +1 -0
  32. package/dist/core/dependency-resolver.js +3 -1
  33. package/dist/core/dependency-resolver.js.map +1 -1
  34. package/dist/core/directory.js +2 -2
  35. package/dist/core/directory.js.map +1 -1
  36. package/dist/core/discovery/file-discovery.js +55 -54
  37. package/dist/core/discovery/file-discovery.js.map +1 -1
  38. package/dist/core/discovery/platform-files-discovery.js +32 -17
  39. package/dist/core/discovery/platform-files-discovery.js.map +1 -1
  40. package/dist/core/install/bulk-install-pipeline.js +199 -0
  41. package/dist/core/install/bulk-install-pipeline.js.map +1 -0
  42. package/dist/core/install/canonical-plan.js +123 -0
  43. package/dist/core/install/canonical-plan.js.map +1 -0
  44. package/dist/core/install/dry-run.js +2 -2
  45. package/dist/core/install/dry-run.js.map +1 -1
  46. package/dist/core/install/index.js +3 -0
  47. package/dist/core/install/index.js.map +1 -0
  48. package/dist/core/install/install-errors.js +41 -0
  49. package/dist/core/install/install-errors.js.map +1 -0
  50. package/dist/core/install/install-flow.js +2 -5
  51. package/dist/core/install/install-flow.js.map +1 -1
  52. package/dist/core/install/install-pipeline.js +228 -0
  53. package/dist/core/install/install-pipeline.js.map +1 -0
  54. package/dist/core/install/install-reporting.js +99 -0
  55. package/dist/core/install/install-reporting.js.map +1 -0
  56. package/dist/core/install/platform-resolution.js +6 -6
  57. package/dist/core/install/platform-resolution.js.map +1 -1
  58. package/dist/core/install/remote-flow.js +67 -1
  59. package/dist/core/install/remote-flow.js.map +1 -1
  60. package/dist/core/openpackage.js +16 -8
  61. package/dist/core/openpackage.js.map +1 -1
  62. package/dist/core/package-context.js +246 -0
  63. package/dist/core/package-context.js.map +1 -0
  64. package/dist/core/package.js +3 -2
  65. package/dist/core/package.js.map +1 -1
  66. package/dist/core/platforms.js +126 -217
  67. package/dist/core/platforms.js.map +1 -1
  68. package/dist/core/registry/registry-rename.js +2 -1
  69. package/dist/core/registry/registry-rename.js.map +1 -1
  70. package/dist/core/registry.js +10 -3
  71. package/dist/core/registry.js.map +1 -1
  72. package/dist/core/remote-pull.js +2 -1
  73. package/dist/core/remote-pull.js.map +1 -1
  74. package/dist/core/save/constants.js +4 -0
  75. package/dist/core/save/constants.js.map +1 -1
  76. package/dist/core/save/name-resolution.js +31 -0
  77. package/dist/core/save/name-resolution.js.map +1 -0
  78. package/dist/core/save/package-detection.js +147 -0
  79. package/dist/core/save/package-detection.js.map +1 -0
  80. package/dist/core/save/package-saver.js +46 -43
  81. package/dist/core/save/package-saver.js.map +1 -1
  82. package/dist/core/save/package-yml-generator.js +50 -71
  83. package/dist/core/save/package-yml-generator.js.map +1 -1
  84. package/dist/core/save/root-save-candidates.js.map +1 -1
  85. package/dist/core/save/save-candidate-loader.js +89 -0
  86. package/dist/core/save/save-candidate-loader.js.map +1 -0
  87. package/dist/core/save/save-conflict-resolution.js +72 -410
  88. package/dist/core/save/save-conflict-resolution.js.map +1 -1
  89. package/dist/core/save/save-conflict-resolver.js +277 -0
  90. package/dist/core/save/save-conflict-resolver.js.map +1 -0
  91. package/dist/core/save/save-pipeline.js +151 -0
  92. package/dist/core/save/save-pipeline.js.map +1 -0
  93. package/dist/core/save/save-types.js +2 -0
  94. package/dist/core/save/save-types.js.map +1 -0
  95. package/dist/core/save/save-yml-resolution.js +77 -39
  96. package/dist/core/save/save-yml-resolution.js.map +1 -1
  97. package/dist/core/save/workspace-rename.js +6 -6
  98. package/dist/core/save/workspace-rename.js.map +1 -1
  99. package/dist/core/scoping/package-scoping.js +13 -1
  100. package/dist/core/scoping/package-scoping.js.map +1 -1
  101. package/dist/core/status/status-file-discovery.js +12 -30
  102. package/dist/core/status/status-file-discovery.js.map +1 -1
  103. package/dist/core/sync/platform-sync.js +7 -4
  104. package/dist/core/sync/platform-sync.js.map +1 -1
  105. package/dist/core/sync/root-files-sync.js +59 -1
  106. package/dist/core/sync/root-files-sync.js.map +1 -1
  107. package/dist/core/uninstall/uninstall-file-discovery.js +1 -2
  108. package/dist/core/uninstall/uninstall-file-discovery.js.map +1 -1
  109. package/dist/types/index.js.map +1 -1
  110. package/dist/utils/file-processing.js +5 -58
  111. package/dist/utils/file-processing.js.map +1 -1
  112. package/dist/utils/index-based-installer.js +15 -33
  113. package/dist/utils/index-based-installer.js.map +1 -1
  114. package/dist/utils/install-file-discovery.js +3 -3
  115. package/dist/utils/install-file-discovery.js.map +1 -1
  116. package/dist/utils/install-orchestrator.js +21 -21
  117. package/dist/utils/install-orchestrator.js.map +1 -1
  118. package/dist/utils/jsonc.js +44 -0
  119. package/dist/utils/jsonc.js.map +1 -0
  120. package/dist/utils/package-copy.js +199 -0
  121. package/dist/utils/package-copy.js.map +1 -0
  122. package/dist/utils/package-filters.js +125 -0
  123. package/dist/utils/package-filters.js.map +1 -0
  124. package/dist/utils/package-index-yml.js +15 -10
  125. package/dist/utils/package-index-yml.js.map +1 -1
  126. package/dist/utils/package-installation.js +4 -113
  127. package/dist/utils/package-installation.js.map +1 -1
  128. package/dist/utils/package-local-files.js +2 -35
  129. package/dist/utils/package-local-files.js.map +1 -1
  130. package/dist/utils/package-management.js +59 -37
  131. package/dist/utils/package-management.js.map +1 -1
  132. package/dist/utils/package-yml.js +24 -0
  133. package/dist/utils/package-yml.js.map +1 -1
  134. package/dist/utils/path-normalization.js +8 -53
  135. package/dist/utils/path-normalization.js.map +1 -1
  136. package/dist/utils/paths.js +17 -9
  137. package/dist/utils/paths.js.map +1 -1
  138. package/dist/utils/platform-file.js +16 -59
  139. package/dist/utils/platform-file.js.map +1 -1
  140. package/dist/utils/platform-mapper.js +29 -41
  141. package/dist/utils/platform-mapper.js.map +1 -1
  142. package/dist/utils/platform-specific-paths.js.map +1 -1
  143. package/dist/utils/platform-utils.js +28 -139
  144. package/dist/utils/platform-utils.js.map +1 -1
  145. package/dist/utils/platform-yaml-merge.js +13 -6
  146. package/dist/utils/platform-yaml-merge.js.map +1 -1
  147. package/dist/utils/registry-entry-filter.js +38 -24
  148. package/dist/utils/registry-entry-filter.js.map +1 -1
  149. package/dist/utils/registry-paths.js +10 -0
  150. package/dist/utils/registry-paths.js.map +1 -0
  151. package/dist/utils/root-file-installer.js +19 -0
  152. package/dist/utils/root-file-installer.js.map +1 -1
  153. package/dist/utils/root-file-registry.js.map +1 -1
  154. package/package.json +3 -2
  155. package/platforms.jsonc +178 -0
  156. package/specs/package/README.md +60 -0
  157. package/specs/package/nested-packages-and-parent-packages.md +79 -0
  158. package/specs/package/package-index-yml.md +171 -0
  159. package/specs/package/package-root-layout.md +78 -0
  160. package/specs/package/registry-payload-and-copy.md +77 -0
  161. package/specs/package/universal-content.md +144 -0
  162. package/specs/platforms.md +193 -0
  163. package/specs/save/README.md +40 -0
  164. package/specs/save/save-conflict-resolution.md +146 -0
  165. package/specs/save/save-file-discovery.md +101 -0
  166. package/specs/save/save-frontmatter-overrides.md +81 -0
  167. package/specs/save/save-modes-inputs.md +53 -0
  168. package/specs/save/save-naming-scoping.md +93 -0
  169. package/specs/save/save-package-detection.md +60 -0
  170. package/specs/save/save-registry-sync.md +126 -0
  171. package/dist/commands/release.js +0 -33
  172. package/dist/commands/release.js.map +0 -1
  173. package/dist/commands/tag.js +0 -311
  174. package/dist/commands/tag.js.map +0 -1
  175. package/dist/commands/update.js +0 -30
  176. package/dist/commands/update.js.map +0 -1
  177. package/dist/core/add/formula-index-updater.js +0 -290
  178. package/dist/core/add/formula-index-updater.js.map +0 -1
  179. package/dist/core/discovery/ai-files-discovery.js +0 -2
  180. package/dist/core/discovery/ai-files-discovery.js.map +0 -1
  181. package/dist/core/discovery/formula-files-discovery.js +0 -14
  182. package/dist/core/discovery/formula-files-discovery.js.map +0 -1
  183. package/dist/core/discovery/index-files-discovery.js +0 -91
  184. package/dist/core/discovery/index-files-discovery.js.map +0 -1
  185. package/dist/core/discovery/md-files-discovery.js +0 -82
  186. package/dist/core/discovery/md-files-discovery.js.map +0 -1
  187. package/dist/core/discovery/package-files-discovery.js +0 -14
  188. package/dist/core/discovery/package-files-discovery.js.map +0 -1
  189. package/dist/core/discovery/platform-discovery.js +0 -84
  190. package/dist/core/discovery/platform-discovery.js.map +0 -1
  191. package/dist/core/discovery/root-files-discovery.js +0 -2
  192. package/dist/core/discovery/root-files-discovery.js.map +0 -1
  193. package/dist/core/formula.js +0 -170
  194. package/dist/core/formula.js.map +0 -1
  195. package/dist/core/git-registry.js +0 -46
  196. package/dist/core/git-registry.js.map +0 -1
  197. package/dist/core/groundzero.js +0 -277
  198. package/dist/core/groundzero.js.map +0 -1
  199. package/dist/core/install/scenario.js +0 -11
  200. package/dist/core/install/scenario.js.map +0 -1
  201. package/dist/core/package-sync.js +0 -219
  202. package/dist/core/package-sync.js.map +0 -1
  203. package/dist/core/save/formula-file-generator.js +0 -167
  204. package/dist/core/save/formula-file-generator.js.map +0 -1
  205. package/dist/core/save/formula-saver.js +0 -52
  206. package/dist/core/save/formula-saver.js.map +0 -1
  207. package/dist/core/save/formula-yml-generator.js +0 -89
  208. package/dist/core/save/formula-yml-generator.js.map +0 -1
  209. package/dist/core/save/formula-yml-versioning.js +0 -108
  210. package/dist/core/save/formula-yml-versioning.js.map +0 -1
  211. package/dist/core/save/generic-file-sync.js +0 -38
  212. package/dist/core/save/generic-file-sync.js.map +0 -1
  213. package/dist/core/save/md-files-sync.js +0 -33
  214. package/dist/core/save/md-files-sync.js.map +0 -1
  215. package/dist/core/save/package-yml-versioning.js +0 -108
  216. package/dist/core/save/package-yml-versioning.js.map +0 -1
  217. package/dist/core/save/platform-sync.js +0 -95
  218. package/dist/core/save/platform-sync.js.map +0 -1
  219. package/dist/core/save/root-files-sync.js +0 -140
  220. package/dist/core/save/root-files-sync.js.map +0 -1
  221. package/dist/core/save/save-candidate-types.js +0 -2
  222. package/dist/core/save/save-candidate-types.js.map +0 -1
  223. package/dist/core/save/save-conflict-types.js +0 -2
  224. package/dist/core/save/save-conflict-types.js.map +0 -1
  225. package/dist/core/save/save-file-discovery.js +0 -5
  226. package/dist/core/save/save-file-discovery.js.map +0 -1
  227. package/dist/core/status-file-discovery.js +0 -175
  228. package/dist/core/status-file-discovery.js.map +0 -1
  229. package/dist/utils/discovery/file-processing.js +0 -156
  230. package/dist/utils/discovery/file-processing.js.map +0 -1
  231. package/dist/utils/discovery/formula-discovery.js +0 -211
  232. package/dist/utils/discovery/formula-discovery.js.map +0 -1
  233. package/dist/utils/discovery/platform-discovery.js +0 -2
  234. package/dist/utils/discovery/platform-discovery.js.map +0 -1
  235. package/dist/utils/formula-discovery.js +0 -102
  236. package/dist/utils/formula-discovery.js.map +0 -1
  237. package/dist/utils/formula-index-yml.js +0 -122
  238. package/dist/utils/formula-index-yml.js.map +0 -1
  239. package/dist/utils/formula-installation.js +0 -110
  240. package/dist/utils/formula-installation.js.map +0 -1
  241. package/dist/utils/formula-local-files.js +0 -38
  242. package/dist/utils/formula-local-files.js.map +0 -1
  243. package/dist/utils/formula-management.js +0 -191
  244. package/dist/utils/formula-management.js.map +0 -1
  245. package/dist/utils/formula-name.js +0 -97
  246. package/dist/utils/formula-name.js.map +0 -1
  247. package/dist/utils/formula-versioning.js +0 -109
  248. package/dist/utils/formula-versioning.js.map +0 -1
  249. package/dist/utils/formula-yml.js +0 -82
  250. package/dist/utils/formula-yml.js.map +0 -1
  251. package/dist/utils/git.js +0 -54
  252. package/dist/utils/git.js.map +0 -1
  253. package/dist/utils/id-based-discovery.js +0 -126
  254. package/dist/utils/id-based-discovery.js.map +0 -1
  255. package/dist/utils/id-based-installer.js +0 -249
  256. package/dist/utils/id-based-installer.js.map +0 -1
  257. package/dist/utils/index-yml-based-installer.js +0 -375
  258. package/dist/utils/index-yml-based-installer.js.map +0 -1
  259. package/dist/utils/index-yml.js +0 -124
  260. package/dist/utils/index-yml.js.map +0 -1
  261. package/dist/utils/md-frontmatter.js +0 -3
  262. package/dist/utils/md-frontmatter.js.map +0 -1
  263. package/dist/utils/package-link-yml.js +0 -92
  264. package/dist/utils/package-link-yml.js.map +0 -1
  265. package/dist/utils/platform-discovery.js +0 -2
  266. package/dist/utils/platform-discovery.js.map +0 -1
  267. package/dist/utils/platform-frontmatter-split.js +0 -15
  268. package/dist/utils/platform-frontmatter-split.js.map +0 -1
  269. package/dist/utils/timestamp-encoder.js +0 -13
  270. package/dist/utils/timestamp-encoder.js.map +0 -1
  271. package/dist/utils/wip-versioning.js +0 -24
  272. package/dist/utils/wip-versioning.js.map +0 -1
@@ -0,0 +1,144 @@
1
+ ### Universal Content
2
+
3
+ #### Universal Content vs. Root-Level Content
4
+
5
+ Packages contain two types of content:
6
+
7
+ | Type | Location | Description | Install Behavior |
8
+ |------|----------|-------------|------------------|
9
+ | **Universal content** | `<package-root>/.openpackage/<subdir>/` | Platform-normalized files | Mapped to platform-specific paths |
10
+ | **Root-level content** | `<package-root>/<path>` (outside `.openpackage/`) | Any files/dirs at package root | Copied 1:1 to same relative path |
11
+
12
+ ---
13
+
14
+ #### Universal Content Layout under `.openpackage/`
15
+
16
+ Inside `.openpackage/`, each universal subdir is canonical:
17
+
18
+ ```text
19
+ <package-root>/
20
+ .openpackage/
21
+ package.yml # package manifest
22
+ agents/
23
+ <name>.md # universal markdown
24
+ <name>.<platform>.md # platform-suffixed markdown (optional)
25
+ <name>.<platform>.yml # YAML override for frontmatter (optional)
26
+ rules/
27
+ ...
28
+ commands/
29
+ ...
30
+ skills/
31
+ ...
32
+ <custom-subdirs>/ # any additional subdirs
33
+ ...
34
+ ```
35
+
36
+ **Definitions:**
37
+
38
+ - **Universal markdown**:
39
+ - Paths like `.openpackage/agents/foo.md`
40
+ - Contains shared body and (after save) shared frontmatter
41
+
42
+ - **Platform-suffixed markdown**:
43
+ - Paths like `.openpackage/agents/foo.<platform>.md`
44
+ - Represents platform-specific variants of a universal file
45
+
46
+ - **YAML override files**:
47
+ - Paths like `.openpackage/agents/foo.<platform>.yml`
48
+ - Contains only the **per-platform difference** in frontmatter
49
+
50
+ ---
51
+
52
+ #### Root-Level Content (Outside `.openpackage/`)
53
+
54
+ Root-level content lives at the package root, **not** under `.openpackage/`:
55
+
56
+ ```text
57
+ <package-root>/
58
+ .openpackage/
59
+ ... # universal content inside
60
+ <root-dir>/ # any root-level directory
61
+ helper.md
62
+ prompts/
63
+ system.md
64
+ AGENTS.md # root files
65
+ CLAUDE.md
66
+ README.md
67
+ ```
68
+
69
+ Root-level content:
70
+ - Is stored and copied **without transformation**
71
+ - Maps to the **same relative path** in the workspace
72
+ - Includes any directories at the package root, platform root files (`AGENTS.md`, `CLAUDE.md`), etc.
73
+
74
+ ---
75
+
76
+ #### Registry Paths (Keys in `package.index.yml`)
77
+
78
+ Registry paths are **relative to the package root**:
79
+
80
+ | Content Type | Example Registry Path |
81
+ |--------------|----------------------|
82
+ | Universal content | `.openpackage/commands/test.md` |
83
+ | Root-level content | `<dir>/helper.md` |
84
+ | Root files | `AGENTS.md` |
85
+
86
+ **Rules:**
87
+
88
+ - Universal subdir content **always** has `.openpackage/` prefix
89
+ - Root-level content uses its natural path (no prefix)
90
+ - Root files use their filename directly
91
+
92
+ ---
93
+
94
+ #### Install Mapping Examples
95
+
96
+ **Universal content** (platform-specific mapping):
97
+
98
+ | Registry Path | Installed Paths |
99
+ |---------------|-----------------|
100
+ | `.openpackage/commands/test.md` | `.cursor/commands/test.md`, `.opencode/commands/test.md`, etc. |
101
+ | `.openpackage/rules/auth.md` | `.cursor/rules/auth.mdc`, etc. |
102
+
103
+ **Root-level content** (1:1 mapping):
104
+
105
+ | Registry Path | Installed Path |
106
+ |---------------|----------------|
107
+ | `<dir>/helper.md` | `<dir>/helper.md` |
108
+ | `AGENTS.md` | `AGENTS.md` |
109
+
110
+ ---
111
+
112
+ #### Consistent Layout Across Locations
113
+
114
+ These layouts apply identically whether the package lives at:
115
+
116
+ - **Workspace root**: `cwd/` (content at `cwd/.openpackage/...`)
117
+ - **Nested package**: `cwd/.openpackage/packages/<name>/` (content at `cwd/.openpackage/packages/<name>/.openpackage/...`)
118
+ - **Registry**: `~/.openpackage/registry/<name>/<version>/` (content at `.../.openpackage/...`)
119
+
120
+ ---
121
+
122
+ #### Frontmatter and Overrides
123
+
124
+ In the canonical structure:
125
+
126
+ - Each universal markdown file (`.openpackage/<subdir>/<name>.md`) is the **single source of truth** for:
127
+ - Markdown body
128
+ - Shared frontmatter keys/common metadata
129
+
130
+ - Platform overrides live alongside their universal file:
131
+
132
+ ```text
133
+ .openpackage/agents/foo.md # universal body + shared frontmatter
134
+ .openpackage/agents/foo.claude.yml # CLAUDE-specific frontmatter diff
135
+ .openpackage/agents/foo.claude.md # optional CLAUDE-specific markdown body
136
+ ```
137
+
138
+ The save pipeline:
139
+
140
+ 1. Normalizes workspace markdown and computes:
141
+ - Universal frontmatter to keep in `foo.md`
142
+ - Per-platform differences to write as `foo.<platform>.yml`
143
+ 2. Writes override files into the `.openpackage/<subdir>/` tree
144
+
@@ -0,0 +1,193 @@
1
+ ## Platforms system behavior
2
+
3
+ ### Overview
4
+
5
+ The platforms system provides a unified way to describe and work with different AI coding platforms (e.g. Cursor, Claude, Gemini) so that OpenPackage can manage platform‑specific rules, commands, agents, and skills consistently.
6
+
7
+ From a user’s perspective, the platform layer answers:
8
+ - **Which platforms are supported in this workspace?**
9
+ - **Where do their files live on disk?**
10
+ - **Which files belong to which platform (or to the generic `ai` space)?**
11
+ - **Which root files (e.g. `CLAUDE.md`) are active, and how are they used?**
12
+
13
+ All platform definitions (names, directories, root files, and subdirectories) are centralized in `platforms.jsonc`.
14
+
15
+ Each platform entry in `platforms.jsonc` has the shape:
16
+
17
+ - `name` (string): Human‑readable display name.
18
+ - `rootDir` (string): Platform root directory (e.g. `.cursor`, `.claude`).
19
+ - `rootFile?` (string): Optional root file at the project root (e.g. `CLAUDE.md`, `QWEN.md`).
20
+ - `subdirs` (object): Map from universal subdir keys (`rules`, `commands`, `agents`, `skills`) to:
21
+ - `path` (string): Directory path under `rootDir`.
22
+ - `exts?` (string[]): Allowed workspace file extensions. When omitted, all extensions are allowed; when an empty array, no extensions are allowed.
23
+ - `transformations?` (array): Optional extension conversion rules with `{ packageExt, workspaceExt }` entries that describe how files convert between registry and workspace formats.
24
+ - `aliases?` (string[]): Optional CLI aliases that resolve to this platform.
25
+ - `enabled?` (boolean): When `false`, the platform exists in config but is treated as disabled.
26
+
27
+ ---
28
+
29
+ ### Platform identities and aliases
30
+
31
+ - **Platform id**: each platform has a lowercase id (e.g. `cursor`, `claude`, `gemini`) used throughout the CLI. These ids are the top‑level keys in `platforms.jsonc`.
32
+ - **Enabled flag**:
33
+ - Platforms are considered **enabled by default**.
34
+ - A platform can be explicitly disabled in `platforms.jsonc` via `enabled: false` (e.g. for experimental or unsupported platforms).
35
+ - Functions that list or iterate platforms only include **enabled** platforms by default, with optional flags to include disabled ones when needed.
36
+ - **Aliases**:
37
+ - Each platform can declare **human‑friendly aliases** in its `aliases` array (e.g. `claudecode`, `codexcli`, `geminicli`, `kilocode`, `qwencode`).
38
+ - Aliases are resolved case‑insensitively to a canonical platform id.
39
+ - User‑facing prompts or configs can accept either the platform id or any of its aliases.
40
+
41
+ **Resolution behavior**
42
+
43
+ - Given a string input (e.g. from CLI flags, prompts, or config), the system:
44
+ - First treats it as a platform id (after lowercasing).
45
+ - If there is no direct match, it looks it up in the alias map.
46
+ - If neither match, the platform is treated as **unknown**.
47
+
48
+ ---
49
+
50
+ ### Directory layout and universal subdirs
51
+
52
+ Each platform defines:
53
+ - A **root directory** (e.g. `.cursor`, `.claude`, `.gemini`).
54
+ - Optional **root file** at the project root (e.g. `CLAUDE.md`, `GEMINI.md`, `QWEN.md`, `WARP.md`, or the shared `AGENTS.md`).
55
+ - A set of **universal subdirectories**, which describe where different kinds of content live:
56
+ - `rules` – steering/rules files for a platform.
57
+ - `commands` – command/workflow prompt files.
58
+ - `agents` – agent definitions.
59
+ - `skills` – skill or tool definitions.
60
+
61
+ For each subdirectory, the platform definition specifies:
62
+ - **Path** under the platform root (e.g. `.cursor/rules`, `.factory/droids`, `.kilo/workflows`).
63
+ - **Allowed extensions**: which workspace file extensions are considered part of that subdir (e.g. `.md`, `.mdc`, `.toml`). Omit to allow any extension or set an empty list to disallow all files.
64
+ - **Extension transformations**: optional `{ packageExt, workspaceExt }` pairs that describe how files convert between registry/universal formats and platform-specific workspace formats (e.g. Cursor rules convert `.md` registry files to `.mdc` in the workspace).
65
+
66
+ **Examples**
67
+
68
+ - Cursor:
69
+ - Root dir: `.cursor`
70
+ - Subdirs:
71
+ - `rules` → `.cursor/rules` (reads `.mdc` + `.md`, writes `.mdc`)
72
+ - `commands` → `.cursor/commands` (reads/writes `.md`)
73
+ - Claude:
74
+ - Root dir: `.claude`
75
+ - Root file: `CLAUDE.md`
76
+ - Subdirs:
77
+ - `commands` → `.claude/commands`
78
+ - `agents` → `.claude/agents`
79
+ - `skills` → `.claude/skills`
80
+
81
+ The system exposes a **platform directory map** that, given a working directory, tells callers where each platform’s `rules`, `commands`, `agents`, and `skills` live on disk.
82
+
83
+ ---
84
+
85
+ ### Platform detection
86
+
87
+ The platforms system can **detect which platforms are present** in a workspace using two signals:
88
+
89
+ 1. **Platform directories**:
90
+ - For each enabled platform, the system checks whether its root directory exists in the current working directory.
91
+ - If the root directory exists, the platform is marked as **present**.
92
+
93
+ 2. **Root files**:
94
+ - For platforms that define a root file (e.g. `CLAUDE.md`, `GEMINI.md`, `QWEN.md`, `WARP.md`), the system checks whether those files exist at the project root.
95
+ - If a root file exists, the corresponding platform is also marked as **present**, even if its standard directory structure is missing.
96
+ - The shared `AGENTS.md` file is treated as **universal** and not attributed to a single platform.
97
+
98
+ The result is:
99
+ - A list of **detection results** (for each platform: `{ name, detected }`).
100
+ - A convenience list of **detected platforms only** (used by higher‑level features like setup flows).
101
+
102
+ ---
103
+
104
+ ### Platform‑specific directories and creation
105
+
106
+ The platforms system provides helpers for:
107
+
108
+ - **Getting directory paths** for each platform:
109
+ - For each enabled platform, callers can retrieve:
110
+ - The `rules` directory path.
111
+ - Optional `commands`, `agents`, and `skills` directory paths.
112
+ - Optional `rootFile` path, if the platform defines one.
113
+ - **Creating missing platform directories**:
114
+ - Given a list of platform ids and a working directory:
115
+ - Ensures the `rules` directory exists for each platform.
116
+ - Creates directories as needed and returns a list of newly created paths.
117
+
118
+ These helpers allow commands like `opkg init` or platform setup flows to create the necessary folder structure for one or more platforms.
119
+
120
+ ---
121
+
122
+ ### Validating platform structure
123
+
124
+ For a given platform and working directory, the system can validate:
125
+ - That the `rules` directory exists.
126
+ - That the configured root file (if any) exists.
127
+
128
+ It returns:
129
+ - A simple `{ valid: boolean, issues: string[] }` result:
130
+ - `valid = true` when the platform’s required directories/files are present.
131
+ - `issues` describing any missing or inconsistent paths.
132
+
133
+ This is used by higher‑level commands to surface actionable warnings when a platform’s layout is incomplete.
134
+
135
+ ---
136
+
137
+ ### File extension behavior
138
+
139
+ For each platform’s `rules` subdir, the system exposes:
140
+ - The **set of file extensions** that are considered valid rules files for that platform.
141
+ - Example: Cursor rules accept `.mdc` and `.md`, Gemini commands accept `.toml`, etc.
142
+ - Higher‑level discovery utilities rely on this to:
143
+ - Filter files by extension when searching for platform content.
144
+ - Decide which files are safe to manage or delete during uninstall/cleanup operations.
145
+
146
+ ---
147
+
148
+ ### Universal subdirectory listing
149
+
150
+ For any detected platform, the platforms system can return a **normalized list of its subdirectories**:
151
+ - Each entry includes:
152
+ - The full directory path.
153
+ - The universal label (`rules`, `commands`, `agents`, `skills`).
154
+ - A short leaf name (last path component) for display.
155
+
156
+ This list is consumed by:
157
+ - Discovery utilities that search for platform‑specific files.
158
+ - Uninstall/cleanup flows that remove platform files for a given package.
159
+
160
+ ---
161
+
162
+ ### Platform inference from file paths
163
+
164
+ The platforms system helps determine which platform a given file belongs to by:
165
+
166
+ 1. **Using path‑to‑platform mappings**:
167
+ - A dedicated mapper converts full workspace paths into a *universal* representation that includes the platform id where possible (e.g. `.cursor/rules/foo.mdc` → platform `cursor` + `rules/foo.mdc`).
168
+
169
+ 2. **Checking for generic workspace directories**:
170
+ - Files that do not live under a known platform root (for example, a conventional `ai/` folder) are treated as workspace-level content rather than being assigned to a specific platform.
171
+
172
+ 3. **Looking at source directory names**:
173
+ - If a file lives under a known platform root directory (e.g. `.cursor`, `.claude`), the system infers that platform from the directory.
174
+
175
+ 4. **Parsing registry paths with platform suffixes**:
176
+ - As a final fallback, the system inspects registry paths for explicit platform suffixes (e.g. `rules/file.cursor.md`) and maps them back to a platform id when possible.
177
+
178
+ The result is a best‑effort platform id (or a `workspace` classification) for a given file, which other components use to route content to the right registry paths and conflict‑resolution logic.
179
+
180
+ ---
181
+
182
+ ### Root file handling
183
+
184
+ The platforms system exposes **all known platform root filenames**, derived from the `rootFile` fields in `platforms.jsonc` plus the universal `AGENTS.md`. These are used to:
185
+ - Discover root files in the workspace or in the local registry.
186
+ - Map root files back to platforms (except for universal `AGENTS.md`).
187
+ - Coordinate how content from multiple platform‑specific root files is merged or extracted into a universal view (handled by other utilities).
188
+
189
+ From a behavioral perspective:
190
+ - Platforms that define root files can participate in root‑file‑based flows (e.g. reading/writing `CLAUDE.md`).
191
+ - Platforms without root files rely exclusively on their directory layout (`.cursor/rules`, `.kilo/workflows`, etc.).
192
+
193
+
@@ -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
+