loadouts 0.1.11

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 (261) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +108 -0
  3. package/bundled/loadouts/loadouts.yaml +5 -0
  4. package/bundled/skills/loadout-usage/SKILL.md +110 -0
  5. package/dist/builtins/index.d.ts +14 -0
  6. package/dist/builtins/index.d.ts.map +1 -0
  7. package/dist/builtins/index.js +58 -0
  8. package/dist/builtins/index.js.map +1 -0
  9. package/dist/builtins/kinds/extension.d.ts +3 -0
  10. package/dist/builtins/kinds/extension.d.ts.map +1 -0
  11. package/dist/builtins/kinds/extension.js +9 -0
  12. package/dist/builtins/kinds/extension.js.map +1 -0
  13. package/dist/builtins/kinds/instruction.d.ts +3 -0
  14. package/dist/builtins/kinds/instruction.d.ts.map +1 -0
  15. package/dist/builtins/kinds/instruction.js +8 -0
  16. package/dist/builtins/kinds/instruction.js.map +1 -0
  17. package/dist/builtins/kinds/prompt.d.ts +3 -0
  18. package/dist/builtins/kinds/prompt.d.ts.map +1 -0
  19. package/dist/builtins/kinds/prompt.js +8 -0
  20. package/dist/builtins/kinds/prompt.js.map +1 -0
  21. package/dist/builtins/kinds/rule.d.ts +3 -0
  22. package/dist/builtins/kinds/rule.d.ts.map +1 -0
  23. package/dist/builtins/kinds/rule.js +10 -0
  24. package/dist/builtins/kinds/rule.js.map +1 -0
  25. package/dist/builtins/kinds/skill.d.ts +3 -0
  26. package/dist/builtins/kinds/skill.d.ts.map +1 -0
  27. package/dist/builtins/kinds/skill.js +8 -0
  28. package/dist/builtins/kinds/skill.js.map +1 -0
  29. package/dist/builtins/kinds/theme.d.ts +3 -0
  30. package/dist/builtins/kinds/theme.d.ts.map +1 -0
  31. package/dist/builtins/kinds/theme.js +8 -0
  32. package/dist/builtins/kinds/theme.js.map +1 -0
  33. package/dist/builtins/tools/claude-code.d.ts +3 -0
  34. package/dist/builtins/tools/claude-code.d.ts.map +1 -0
  35. package/dist/builtins/tools/claude-code.js +30 -0
  36. package/dist/builtins/tools/claude-code.js.map +1 -0
  37. package/dist/builtins/tools/codex.d.ts +3 -0
  38. package/dist/builtins/tools/codex.d.ts.map +1 -0
  39. package/dist/builtins/tools/codex.js +17 -0
  40. package/dist/builtins/tools/codex.js.map +1 -0
  41. package/dist/builtins/tools/cursor.d.ts +3 -0
  42. package/dist/builtins/tools/cursor.d.ts.map +1 -0
  43. package/dist/builtins/tools/cursor.js +22 -0
  44. package/dist/builtins/tools/cursor.js.map +1 -0
  45. package/dist/builtins/tools/opencode.d.ts +3 -0
  46. package/dist/builtins/tools/opencode.d.ts.map +1 -0
  47. package/dist/builtins/tools/opencode.js +45 -0
  48. package/dist/builtins/tools/opencode.js.map +1 -0
  49. package/dist/builtins/tools/pi.d.ts +3 -0
  50. package/dist/builtins/tools/pi.d.ts.map +1 -0
  51. package/dist/builtins/tools/pi.js +22 -0
  52. package/dist/builtins/tools/pi.js.map +1 -0
  53. package/dist/cli/commands/activate.d.ts +12 -0
  54. package/dist/cli/commands/activate.d.ts.map +1 -0
  55. package/dist/cli/commands/activate.js +70 -0
  56. package/dist/cli/commands/activate.js.map +1 -0
  57. package/dist/cli/commands/check.d.ts +12 -0
  58. package/dist/cli/commands/check.d.ts.map +1 -0
  59. package/dist/cli/commands/check.js +152 -0
  60. package/dist/cli/commands/check.js.map +1 -0
  61. package/dist/cli/commands/clear.d.ts +12 -0
  62. package/dist/cli/commands/clear.d.ts.map +1 -0
  63. package/dist/cli/commands/clear.js +25 -0
  64. package/dist/cli/commands/clear.js.map +1 -0
  65. package/dist/cli/commands/create.d.ts +11 -0
  66. package/dist/cli/commands/create.d.ts.map +1 -0
  67. package/dist/cli/commands/create.js +113 -0
  68. package/dist/cli/commands/create.js.map +1 -0
  69. package/dist/cli/commands/deactivate.d.ts +12 -0
  70. package/dist/cli/commands/deactivate.d.ts.map +1 -0
  71. package/dist/cli/commands/deactivate.js +67 -0
  72. package/dist/cli/commands/deactivate.js.map +1 -0
  73. package/dist/cli/commands/diff.d.ts +16 -0
  74. package/dist/cli/commands/diff.d.ts.map +1 -0
  75. package/dist/cli/commands/diff.js +144 -0
  76. package/dist/cli/commands/diff.js.map +1 -0
  77. package/dist/cli/commands/docs.d.ts +12 -0
  78. package/dist/cli/commands/docs.d.ts.map +1 -0
  79. package/dist/cli/commands/docs.js +115 -0
  80. package/dist/cli/commands/docs.js.map +1 -0
  81. package/dist/cli/commands/edit.d.ts +11 -0
  82. package/dist/cli/commands/edit.d.ts.map +1 -0
  83. package/dist/cli/commands/edit.js +71 -0
  84. package/dist/cli/commands/edit.js.map +1 -0
  85. package/dist/cli/commands/fallback.d.ts +9 -0
  86. package/dist/cli/commands/fallback.d.ts.map +1 -0
  87. package/dist/cli/commands/fallback.js +35 -0
  88. package/dist/cli/commands/fallback.js.map +1 -0
  89. package/dist/cli/commands/info.d.ts +23 -0
  90. package/dist/cli/commands/info.d.ts.map +1 -0
  91. package/dist/cli/commands/info.js +314 -0
  92. package/dist/cli/commands/info.js.map +1 -0
  93. package/dist/cli/commands/init.d.ts +18 -0
  94. package/dist/cli/commands/init.d.ts.map +1 -0
  95. package/dist/cli/commands/init.js +255 -0
  96. package/dist/cli/commands/init.js.map +1 -0
  97. package/dist/cli/commands/install.d.ts +27 -0
  98. package/dist/cli/commands/install.d.ts.map +1 -0
  99. package/dist/cli/commands/install.js +586 -0
  100. package/dist/cli/commands/install.js.map +1 -0
  101. package/dist/cli/commands/instructions.d.ts +8 -0
  102. package/dist/cli/commands/instructions.d.ts.map +1 -0
  103. package/dist/cli/commands/instructions.js +218 -0
  104. package/dist/cli/commands/instructions.js.map +1 -0
  105. package/dist/cli/commands/kinds.d.ts +6 -0
  106. package/dist/cli/commands/kinds.d.ts.map +1 -0
  107. package/dist/cli/commands/kinds.js +59 -0
  108. package/dist/cli/commands/kinds.js.map +1 -0
  109. package/dist/cli/commands/list.d.ts +12 -0
  110. package/dist/cli/commands/list.d.ts.map +1 -0
  111. package/dist/cli/commands/list.js +182 -0
  112. package/dist/cli/commands/list.js.map +1 -0
  113. package/dist/cli/commands/policy.d.ts +28 -0
  114. package/dist/cli/commands/policy.d.ts.map +1 -0
  115. package/dist/cli/commands/policy.js +50 -0
  116. package/dist/cli/commands/policy.js.map +1 -0
  117. package/dist/cli/commands/remove.d.ts +24 -0
  118. package/dist/cli/commands/remove.d.ts.map +1 -0
  119. package/dist/cli/commands/remove.js +64 -0
  120. package/dist/cli/commands/remove.js.map +1 -0
  121. package/dist/cli/commands/render-engine.d.ts +36 -0
  122. package/dist/cli/commands/render-engine.d.ts.map +1 -0
  123. package/dist/cli/commands/render-engine.js +177 -0
  124. package/dist/cli/commands/render-engine.js.map +1 -0
  125. package/dist/cli/commands/rule.d.ts +11 -0
  126. package/dist/cli/commands/rule.d.ts.map +1 -0
  127. package/dist/cli/commands/rule.js +302 -0
  128. package/dist/cli/commands/rule.js.map +1 -0
  129. package/dist/cli/commands/sanitize.d.ts +14 -0
  130. package/dist/cli/commands/sanitize.d.ts.map +1 -0
  131. package/dist/cli/commands/sanitize.js +62 -0
  132. package/dist/cli/commands/sanitize.js.map +1 -0
  133. package/dist/cli/commands/skill.d.ts +11 -0
  134. package/dist/cli/commands/skill.d.ts.map +1 -0
  135. package/dist/cli/commands/skill.js +380 -0
  136. package/dist/cli/commands/skill.js.map +1 -0
  137. package/dist/cli/commands/status.d.ts +26 -0
  138. package/dist/cli/commands/status.d.ts.map +1 -0
  139. package/dist/cli/commands/status.js +454 -0
  140. package/dist/cli/commands/status.js.map +1 -0
  141. package/dist/cli/commands/sync.d.ts +14 -0
  142. package/dist/cli/commands/sync.d.ts.map +1 -0
  143. package/dist/cli/commands/sync.js +53 -0
  144. package/dist/cli/commands/sync.js.map +1 -0
  145. package/dist/cli/commands/update.d.ts +3 -0
  146. package/dist/cli/commands/update.d.ts.map +1 -0
  147. package/dist/cli/commands/update.js +48 -0
  148. package/dist/cli/commands/update.js.map +1 -0
  149. package/dist/cli/index.d.ts +11 -0
  150. package/dist/cli/index.d.ts.map +1 -0
  151. package/dist/cli/index.js +134 -0
  152. package/dist/cli/index.js.map +1 -0
  153. package/dist/core/config.d.ts +64 -0
  154. package/dist/core/config.d.ts.map +1 -0
  155. package/dist/core/config.js +166 -0
  156. package/dist/core/config.js.map +1 -0
  157. package/dist/core/discovery.d.ts +50 -0
  158. package/dist/core/discovery.d.ts.map +1 -0
  159. package/dist/core/discovery.js +249 -0
  160. package/dist/core/discovery.js.map +1 -0
  161. package/dist/core/fallback.d.ts +23 -0
  162. package/dist/core/fallback.d.ts.map +1 -0
  163. package/dist/core/fallback.js +119 -0
  164. package/dist/core/fallback.js.map +1 -0
  165. package/dist/core/import-discovery.d.ts +56 -0
  166. package/dist/core/import-discovery.d.ts.map +1 -0
  167. package/dist/core/import-discovery.js +304 -0
  168. package/dist/core/import-discovery.js.map +1 -0
  169. package/dist/core/kindLoader.d.ts +119 -0
  170. package/dist/core/kindLoader.d.ts.map +1 -0
  171. package/dist/core/kindLoader.js +141 -0
  172. package/dist/core/kindLoader.js.map +1 -0
  173. package/dist/core/manifest.d.ts +39 -0
  174. package/dist/core/manifest.d.ts.map +1 -0
  175. package/dist/core/manifest.js +167 -0
  176. package/dist/core/manifest.js.map +1 -0
  177. package/dist/core/plugin.d.ts +22 -0
  178. package/dist/core/plugin.d.ts.map +1 -0
  179. package/dist/core/plugin.js +20 -0
  180. package/dist/core/plugin.js.map +1 -0
  181. package/dist/core/registry.d.ts +115 -0
  182. package/dist/core/registry.d.ts.map +1 -0
  183. package/dist/core/registry.js +105 -0
  184. package/dist/core/registry.js.map +1 -0
  185. package/dist/core/render.d.ts +64 -0
  186. package/dist/core/render.d.ts.map +1 -0
  187. package/dist/core/render.js +457 -0
  188. package/dist/core/render.js.map +1 -0
  189. package/dist/core/resolve.d.ts +39 -0
  190. package/dist/core/resolve.d.ts.map +1 -0
  191. package/dist/core/resolve.js +128 -0
  192. package/dist/core/resolve.js.map +1 -0
  193. package/dist/core/schema.d.ts +308 -0
  194. package/dist/core/schema.d.ts.map +1 -0
  195. package/dist/core/schema.js +81 -0
  196. package/dist/core/schema.js.map +1 -0
  197. package/dist/core/scope.d.ts +74 -0
  198. package/dist/core/scope.d.ts.map +1 -0
  199. package/dist/core/scope.js +176 -0
  200. package/dist/core/scope.js.map +1 -0
  201. package/dist/core/template.d.ts +32 -0
  202. package/dist/core/template.d.ts.map +1 -0
  203. package/dist/core/template.js +32 -0
  204. package/dist/core/template.js.map +1 -0
  205. package/dist/core/tokens.d.ts +33 -0
  206. package/dist/core/tokens.d.ts.map +1 -0
  207. package/dist/core/tokens.js +97 -0
  208. package/dist/core/tokens.js.map +1 -0
  209. package/dist/core/types.d.ts +103 -0
  210. package/dist/core/types.d.ts.map +1 -0
  211. package/dist/core/types.js +10 -0
  212. package/dist/core/types.js.map +1 -0
  213. package/dist/index.d.ts +12 -0
  214. package/dist/index.d.ts.map +1 -0
  215. package/dist/index.js +24 -0
  216. package/dist/index.js.map +1 -0
  217. package/dist/lib/artifact-paths.d.ts +39 -0
  218. package/dist/lib/artifact-paths.d.ts.map +1 -0
  219. package/dist/lib/artifact-paths.js +83 -0
  220. package/dist/lib/artifact-paths.js.map +1 -0
  221. package/dist/lib/artifact-table.d.ts +126 -0
  222. package/dist/lib/artifact-table.d.ts.map +1 -0
  223. package/dist/lib/artifact-table.js +263 -0
  224. package/dist/lib/artifact-table.js.map +1 -0
  225. package/dist/lib/editor.d.ts +17 -0
  226. package/dist/lib/editor.d.ts.map +1 -0
  227. package/dist/lib/editor.js +33 -0
  228. package/dist/lib/editor.js.map +1 -0
  229. package/dist/lib/fs.d.ts +87 -0
  230. package/dist/lib/fs.d.ts.map +1 -0
  231. package/dist/lib/fs.js +229 -0
  232. package/dist/lib/fs.js.map +1 -0
  233. package/dist/lib/git.d.ts +13 -0
  234. package/dist/lib/git.d.ts.map +1 -0
  235. package/dist/lib/git.js +28 -0
  236. package/dist/lib/git.js.map +1 -0
  237. package/dist/lib/gitignore.d.ts +26 -0
  238. package/dist/lib/gitignore.d.ts.map +1 -0
  239. package/dist/lib/gitignore.js +97 -0
  240. package/dist/lib/gitignore.js.map +1 -0
  241. package/dist/lib/loadout-column.d.ts +66 -0
  242. package/dist/lib/loadout-column.d.ts.map +1 -0
  243. package/dist/lib/loadout-column.js +66 -0
  244. package/dist/lib/loadout-column.js.map +1 -0
  245. package/dist/lib/output.d.ts +15 -0
  246. package/dist/lib/output.d.ts.map +1 -0
  247. package/dist/lib/output.js +32 -0
  248. package/dist/lib/output.js.map +1 -0
  249. package/dist/lib/scope-indicators.d.ts +60 -0
  250. package/dist/lib/scope-indicators.d.ts.map +1 -0
  251. package/dist/lib/scope-indicators.js +110 -0
  252. package/dist/lib/scope-indicators.js.map +1 -0
  253. package/docs/authoring.md +182 -0
  254. package/docs/commands.md +192 -0
  255. package/docs/concepts.md +114 -0
  256. package/docs/index.md +60 -0
  257. package/docs/quickstart.md +100 -0
  258. package/docs/troubleshooting.md +147 -0
  259. package/docs/visual-language.md +251 -0
  260. package/docs/workflows.md +184 -0
  261. package/package.json +54 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Ownership manifest and drift detection
3
+ */
4
+ import * as path from "node:path";
5
+ import { fileExists, readFile, writeFile, removeFile, isSymlink, readSymlinkTarget, findSymlinkParent, hashFile, hashDir, isDirectory, } from "../lib/fs.js";
6
+ import { AppliedStateSchema, LegacyAppliedStateSchema, LegacyManifestEntrySchema } from "./schema.js";
7
+ import { z } from "zod";
8
+ const STATE_FILE = ".state.json";
9
+ /**
10
+ * Get the path to the state file in a loadout root.
11
+ */
12
+ function getStatePath(loadoutRoot) {
13
+ return path.join(loadoutRoot, STATE_FILE);
14
+ }
15
+ /**
16
+ * Load the applied state from disk.
17
+ * Handles migration from legacy single-loadout format to multi-loadout format.
18
+ */
19
+ export function loadState(loadoutRoot) {
20
+ const statePath = getStatePath(loadoutRoot);
21
+ if (!fileExists(statePath)) {
22
+ return null;
23
+ }
24
+ try {
25
+ const content = readFile(statePath);
26
+ const parsed = JSON.parse(content);
27
+ // Try new format first (multi-loadout, multi-tool)
28
+ const newFormat = AppliedStateSchema.safeParse(parsed);
29
+ if (newFormat.success) {
30
+ return newFormat.data;
31
+ }
32
+ // Try single-tool entries format (active[] but tool instead of tools)
33
+ const singleToolFormat = z.object({
34
+ active: z.array(z.string()),
35
+ mode: z.enum(["symlink", "copy", "generate"]),
36
+ appliedAt: z.string(),
37
+ entries: z.array(LegacyManifestEntrySchema),
38
+ shadowed: z.array(z.object({
39
+ tool: z.string(),
40
+ kind: z.string(),
41
+ sourcePath: z.string(),
42
+ targetPath: z.string(),
43
+ })).default([]),
44
+ }).safeParse(parsed);
45
+ if (singleToolFormat.success) {
46
+ return {
47
+ active: singleToolFormat.data.active,
48
+ mode: singleToolFormat.data.mode,
49
+ appliedAt: singleToolFormat.data.appliedAt,
50
+ entries: singleToolFormat.data.entries.map(e => ({
51
+ ...e,
52
+ tools: [e.tool],
53
+ })),
54
+ shadowed: singleToolFormat.data.shadowed,
55
+ };
56
+ }
57
+ // Try legacy single-loadout format (loadout instead of active)
58
+ const legacyFormat = LegacyAppliedStateSchema.safeParse(parsed);
59
+ if (legacyFormat.success) {
60
+ return {
61
+ active: [legacyFormat.data.loadout],
62
+ mode: legacyFormat.data.mode,
63
+ appliedAt: legacyFormat.data.appliedAt,
64
+ entries: legacyFormat.data.entries.map((e) => ({
65
+ ...e,
66
+ tools: [e.tool],
67
+ })),
68
+ shadowed: legacyFormat.data.shadowed,
69
+ };
70
+ }
71
+ return null;
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
77
+ /**
78
+ * Save the applied state to disk.
79
+ */
80
+ export function saveState(loadoutRoot, state) {
81
+ const statePath = getStatePath(loadoutRoot);
82
+ writeFile(statePath, JSON.stringify(state, null, 2));
83
+ }
84
+ /**
85
+ * Clear the applied state.
86
+ */
87
+ export function clearState(loadoutRoot) {
88
+ const statePath = getStatePath(loadoutRoot);
89
+ removeFile(statePath);
90
+ }
91
+ /**
92
+ * Check if a target path is owned by the manifest.
93
+ */
94
+ export function isOwned(state, targetPath) {
95
+ if (!state)
96
+ return false;
97
+ return state.entries.some((e) => e.targetPath === targetPath);
98
+ }
99
+ /**
100
+ * Check if a target path exists and is NOT owned by the manifest.
101
+ * This is what we need to detect before overwriting.
102
+ */
103
+ export function isUnmanagedCollision(state, targetPath, projectRoot) {
104
+ // Handle both absolute (global) and relative (project) target paths
105
+ const fullPath = path.isAbsolute(targetPath)
106
+ ? targetPath
107
+ : path.join(projectRoot, targetPath);
108
+ if (!fileExists(fullPath) && !isDirectory(fullPath)) {
109
+ return false; // Doesn't exist, no collision
110
+ }
111
+ return !isOwned(state, targetPath);
112
+ }
113
+ export function detectDrift(state, projectRoot) {
114
+ const results = [];
115
+ for (const entry of state.entries) {
116
+ // Handle both absolute (global) and relative (project) target paths
117
+ const fullPath = path.isAbsolute(entry.targetPath)
118
+ ? entry.targetPath
119
+ : path.join(projectRoot, entry.targetPath);
120
+ if (!fileExists(fullPath) && !isDirectory(fullPath)) {
121
+ results.push({ entry, status: "missing" });
122
+ continue;
123
+ }
124
+ // Check if symlink was converted to regular file
125
+ if (entry.mode === "symlink" && !isSymlink(fullPath)) {
126
+ results.push({ entry, status: "unlinked" });
127
+ continue;
128
+ }
129
+ // Check if symlink points to the expected source
130
+ if (entry.mode === "symlink") {
131
+ const actualTarget = readSymlinkTarget(fullPath);
132
+ if (actualTarget !== entry.sourcePath) {
133
+ results.push({ entry, status: "misdirected" });
134
+ continue;
135
+ }
136
+ // Check if any parent directory is a symlink (external structure)
137
+ const symlinkParent = findSymlinkParent(fullPath);
138
+ if (symlinkParent) {
139
+ results.push({ entry, status: "misdirected" });
140
+ continue;
141
+ }
142
+ }
143
+ // Check hash — for symlinks, check source; for copy/generate, check target
144
+ const hashPath = entry.mode === "symlink" ? entry.sourcePath : fullPath;
145
+ // For symlinks, if source file was deleted, it's a broken symlink
146
+ if (entry.mode === "symlink" && !fileExists(hashPath) && !isDirectory(hashPath)) {
147
+ results.push({ entry, status: "broken" });
148
+ continue;
149
+ }
150
+ const currentHash = isDirectory(hashPath)
151
+ ? hashDir(hashPath)
152
+ : hashFile(hashPath);
153
+ if (currentHash !== entry.renderedHash) {
154
+ results.push({ entry, status: "modified" });
155
+ continue;
156
+ }
157
+ results.push({ entry, status: "ok" });
158
+ }
159
+ return results;
160
+ }
161
+ /**
162
+ * Get all target paths that would be affected by applying new entries.
163
+ */
164
+ export function getAffectedPaths(entries) {
165
+ return new Set(entries.map((e) => e.targetPath));
166
+ }
167
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/core/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Plugin API — the public surface through which built-ins, YAML kinds, and
3
+ * future JS plugins register capabilities into the registry.
4
+ *
5
+ * External plugins import from "loadout/plugin" (see package.json exports).
6
+ * The `apiVersion` field guards against breaking API changes.
7
+ */
8
+ import type { Registry, KindSpec, ToolSpec, TransformFn, HookEvent, HookFn } from "./registry.js";
9
+ /** Versioned plugin API passed to each plugin's register() function. */
10
+ export interface PluginAPI {
11
+ readonly apiVersion: 1;
12
+ registerKind(spec: KindSpec): void;
13
+ registerTool(spec: ToolSpec): void;
14
+ registerTransform(name: string, fn: TransformFn): void;
15
+ registerHook(event: HookEvent, fn: HookFn): void;
16
+ /** Read-only access — useful for plugins that extend or inspect others. */
17
+ getKind(id: string): KindSpec | undefined;
18
+ getTool(name: string): ToolSpec | undefined;
19
+ }
20
+ /** Create a PluginAPI that delegates to the given registry. */
21
+ export declare function createPluginAPI(reg: Registry): PluginAPI;
22
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/core/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAElG,wEAAwE;AACxE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACvB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACnC,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACnC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IACvD,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,2EAA2E;IAC3E,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC7C;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,CAUxD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Plugin API — the public surface through which built-ins, YAML kinds, and
3
+ * future JS plugins register capabilities into the registry.
4
+ *
5
+ * External plugins import from "loadout/plugin" (see package.json exports).
6
+ * The `apiVersion` field guards against breaking API changes.
7
+ */
8
+ /** Create a PluginAPI that delegates to the given registry. */
9
+ export function createPluginAPI(reg) {
10
+ return {
11
+ apiVersion: 1,
12
+ registerKind: (spec) => reg.registerKind(spec),
13
+ registerTool: (spec) => reg.registerTool(spec),
14
+ registerTransform: (name, fn) => reg.registerTransform(name, fn),
15
+ registerHook: (event, fn) => reg.registerHook(event, fn),
16
+ getKind: (id) => reg.getKind(id),
17
+ getTool: (name) => reg.getTool(name),
18
+ };
19
+ }
20
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/core/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgBH,+DAA+D;AAC/D,MAAM,UAAU,eAAe,CAAC,GAAa;IAC3C,OAAO;QACL,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;QAC9C,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;QAC9C,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC;QAChE,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC;QACxD,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;KACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Core registry — the single source of truth for tools, kinds, transforms,
3
+ * and hooks. Built-ins, YAML kinds, and JS plugins all register here.
4
+ *
5
+ * The singleton `registry` is populated at CLI startup before any command
6
+ * runs. Tests should create a fresh Registry instance rather than using the
7
+ * singleton.
8
+ */
9
+ import type { Scope, OutputMode, ResolvedItem, ValidationResult, CommandContext, RenderPlan } from "./types.js";
10
+ /** A path template string or scope-specific pair. */
11
+ export type PathTemplate = string | {
12
+ project: string;
13
+ global: string;
14
+ };
15
+ /**
16
+ * Inline transform: accepts raw source content, returns transformed content.
17
+ * Can also be a string that names a registered transform.
18
+ */
19
+ export type TransformFn = (raw: string) => string;
20
+ /**
21
+ * Generator: produces file content from scratch (e.g. CLAUDE.md wrapper).
22
+ * Receives the resolved source item for access to sourcePath / relativePath.
23
+ */
24
+ export type GenerateFn = (item: ResolvedItem) => string;
25
+ /** How a (tool, kind) pair maps source → target. */
26
+ export interface OutputMapping {
27
+ /** Path template. Tokens: {base} {home} {stem} {ext} {name} {relative} {kind} */
28
+ path: PathTemplate;
29
+ /** Override source extension (include the dot). */
30
+ ext?: string;
31
+ /**
32
+ * Output mode. If omitted, inferred:
33
+ * generate fn present → "generate"
34
+ * transform present → "copy"
35
+ * otherwise → "symlink"
36
+ */
37
+ mode?: OutputMode;
38
+ /** Content transform. Either an inline function or a registered transform name. */
39
+ transform?: TransformFn | string;
40
+ /** Content generator. Mutually exclusive with transform. */
41
+ generate?: GenerateFn;
42
+ }
43
+ export interface KindSpec {
44
+ /** Unique identifier. Built-ins: "rule", "skill", "instruction". */
45
+ id: string;
46
+ description?: string;
47
+ /**
48
+ * Returns true if a `.loadouts/`-relative path belongs to this kind.
49
+ * Called in insertion order; first match wins.
50
+ */
51
+ detect: (relativePath: string) => boolean;
52
+ /** Whether this artifact is a single file or a directory tree. */
53
+ layout: "file" | "dir";
54
+ /**
55
+ * Fallback output mapping per tool name.
56
+ * Tool-level `targets` overrides these. Primarily used by YAML-defined kinds
57
+ * so custom tools don't need to be updated for every new kind.
58
+ */
59
+ defaultTargets?: Record<string, OutputMapping>;
60
+ }
61
+ export interface ToolSpec {
62
+ /** Unique tool name. E.g. "claude-code", "cursor". */
63
+ name: string;
64
+ /** Root directories where this tool reads config, per scope. */
65
+ basePath: Record<Scope, string>;
66
+ /** Kind IDs this tool supports. Unsupported kinds are silently skipped. */
67
+ supports: string[];
68
+ /**
69
+ * Per-kind output mapping overrides.
70
+ * Takes precedence over `kind.defaultTargets[toolName]`.
71
+ */
72
+ targets?: Record<string, OutputMapping>;
73
+ /** Optional prerequisite validator called by `loadout check`. */
74
+ validate?: (scope: Scope) => Promise<ValidationResult>;
75
+ }
76
+ export type HookEvent = "pre-apply" | "post-apply" | "pre-render" | "post-render" | "pre-clean" | "post-clean";
77
+ export type HookFn = (ctx: CommandContext, plan: RenderPlan) => Promise<void>;
78
+ export declare class Registry {
79
+ private _kinds;
80
+ private _tools;
81
+ private _transforms;
82
+ private _hooks;
83
+ registerKind(spec: KindSpec): void;
84
+ registerTool(spec: ToolSpec): void;
85
+ registerTransform(name: string, fn: TransformFn): void;
86
+ registerHook(event: HookEvent, fn: HookFn): void;
87
+ getKind(id: string): KindSpec | undefined;
88
+ getTool(name: string): ToolSpec | undefined;
89
+ getTransform(name: string): TransformFn | undefined;
90
+ getHooks(event: HookEvent): HookFn[];
91
+ allKinds(): KindSpec[];
92
+ allTools(): ToolSpec[];
93
+ allToolNames(): string[];
94
+ /**
95
+ * Return the first registered kind whose `detect` predicate matches the
96
+ * given `.loadouts/`-relative path, or undefined if none matches.
97
+ */
98
+ inferKind(relativePath: string): string | undefined;
99
+ /**
100
+ * Resolve the output mapping for a (tool, kind) pair.
101
+ *
102
+ * Precedence: tool.targets[kindId] > kind.defaultTargets[toolName]
103
+ *
104
+ * A tool's explicit `supports` list governs its own target overrides.
105
+ * A kind's `defaultTargets` implicitly extends support to any listed tool,
106
+ * enabling YAML-defined kinds to work with existing tools without requiring
107
+ * those tools to be modified.
108
+ *
109
+ * Returns undefined if no mapping exists for the combination.
110
+ */
111
+ resolveMapping(toolName: string, kindId: string): OutputMapping | undefined;
112
+ }
113
+ /** Module singleton — populated at startup, shared across all commands. */
114
+ export declare const registry: Registry;
115
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Core registry — the single source of truth for tools, kinds, transforms,
3
+ * and hooks. Built-ins, YAML kinds, and JS plugins all register here.
4
+ *
5
+ * The singleton `registry` is populated at CLI startup before any command
6
+ * runs. Tests should create a fresh Registry instance rather than using the
7
+ * singleton.
8
+ */
9
+ // ---------------------------------------------------------------------------
10
+ // Registry
11
+ // ---------------------------------------------------------------------------
12
+ export class Registry {
13
+ _kinds = new Map();
14
+ _tools = new Map();
15
+ _transforms = new Map();
16
+ _hooks = new Map();
17
+ // ── Registration ──────────────────────────────────────────────────────────
18
+ registerKind(spec) {
19
+ if (this._kinds.has(spec.id)) {
20
+ throw new Error(`Kind "${spec.id}" is already registered.`);
21
+ }
22
+ this._kinds.set(spec.id, spec);
23
+ }
24
+ registerTool(spec) {
25
+ if (this._tools.has(spec.name)) {
26
+ throw new Error(`Tool "${spec.name}" is already registered.`);
27
+ }
28
+ this._tools.set(spec.name, spec);
29
+ }
30
+ registerTransform(name, fn) {
31
+ this._transforms.set(name, fn);
32
+ }
33
+ registerHook(event, fn) {
34
+ const list = this._hooks.get(event) ?? [];
35
+ this._hooks.set(event, [...list, fn]);
36
+ }
37
+ // ── Lookup ────────────────────────────────────────────────────────────────
38
+ getKind(id) {
39
+ return this._kinds.get(id);
40
+ }
41
+ getTool(name) {
42
+ return this._tools.get(name);
43
+ }
44
+ getTransform(name) {
45
+ return this._transforms.get(name);
46
+ }
47
+ getHooks(event) {
48
+ return this._hooks.get(event) ?? [];
49
+ }
50
+ allKinds() {
51
+ return [...this._kinds.values()];
52
+ }
53
+ allTools() {
54
+ return [...this._tools.values()];
55
+ }
56
+ allToolNames() {
57
+ return [...this._tools.keys()];
58
+ }
59
+ // ── Inference & resolution ────────────────────────────────────────────────
60
+ /**
61
+ * Return the first registered kind whose `detect` predicate matches the
62
+ * given `.loadouts/`-relative path, or undefined if none matches.
63
+ */
64
+ inferKind(relativePath) {
65
+ for (const kind of this._kinds.values()) {
66
+ if (kind.detect(relativePath))
67
+ return kind.id;
68
+ }
69
+ return undefined;
70
+ }
71
+ /**
72
+ * Resolve the output mapping for a (tool, kind) pair.
73
+ *
74
+ * Precedence: tool.targets[kindId] > kind.defaultTargets[toolName]
75
+ *
76
+ * A tool's explicit `supports` list governs its own target overrides.
77
+ * A kind's `defaultTargets` implicitly extends support to any listed tool,
78
+ * enabling YAML-defined kinds to work with existing tools without requiring
79
+ * those tools to be modified.
80
+ *
81
+ * Returns undefined if no mapping exists for the combination.
82
+ */
83
+ resolveMapping(toolName, kindId) {
84
+ const tool = this._tools.get(toolName);
85
+ const kind = this._kinds.get(kindId);
86
+ if (!tool || !kind)
87
+ return undefined;
88
+ // Tool-level override — only applies when the tool explicitly supports the kind
89
+ if (tool.supports.includes(kindId) && tool.targets?.[kindId]) {
90
+ return tool.targets[kindId];
91
+ }
92
+ // Kind-level default — implicitly extends support for listed tools
93
+ if (kind.defaultTargets?.[toolName]) {
94
+ return kind.defaultTargets[toolName];
95
+ }
96
+ // Tool supports the kind but has no specific mapping (treated as pass-through)
97
+ if (tool.supports.includes(kindId)) {
98
+ return undefined;
99
+ }
100
+ return undefined;
101
+ }
102
+ }
103
+ /** Module singleton — populated at startup, shared across all commands. */
104
+ export const registry = new Registry();
105
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Generic render pipeline.
3
+ *
4
+ * planRender() — computes what would be written without touching disk.
5
+ * renderOutput() — renders one (item, spec) pair to content + hash.
6
+ * applyPlan() — writes the plan to disk and saves state.
7
+ * removeManaged() — removes all outputs recorded in a state file.
8
+ *
9
+ * All tool/kind logic is delegated to the registry; this module contains
10
+ * no per-tool or per-kind switch statements.
11
+ */
12
+ import type { ResolvedLoadout, ResolvedItem, OutputSpec, RenderedOutput, RenderPlan, OutputMode, Scope } from "./types.js";
13
+ /**
14
+ * Resolve the OutputSpec for one (item, tool) pair using the registry.
15
+ * Returns null if the tool doesn't support this kind or has no mapping.
16
+ */
17
+ export declare function resolveOutputSpec(toolName: string, item: ResolvedItem, scope: Scope): OutputSpec | null;
18
+ /**
19
+ * Render one (item, spec) pair to content + hash.
20
+ * No disk I/O — used for planning, token estimation, and copy/generate writes.
21
+ *
22
+ * All items are now file-based (dir-layout items are expanded in planRender).
23
+ */
24
+ export declare function renderOutput(item: ResolvedItem, spec: OutputSpec): Promise<RenderedOutput>;
25
+ /**
26
+ * Compute what would be written for a resolved loadout without touching disk.
27
+ *
28
+ * Dir-layout items (skills) are expanded into individual file outputs,
29
+ * allowing users to have custom files alongside loadout-managed ones.
30
+ */
31
+ export declare function planRender(loadout: ResolvedLoadout, projectRoot: string, scope: Scope, statePath?: string): Promise<RenderPlan>;
32
+ /**
33
+ * Apply a render plan to disk and save state.
34
+ *
35
+ * `mode` is top-level metadata recorded in the state file; per-output modes
36
+ * live on each spec and govern the actual write strategy.
37
+ */
38
+ export declare function applyPlan(plan: RenderPlan, loadout: ResolvedLoadout, projectRoot: string, mode?: OutputMode, scope?: Scope): Promise<void>;
39
+ export interface ApplyResult {
40
+ totalOutputs: number;
41
+ byTool: Map<string, number>;
42
+ changes: {
43
+ updated: string[];
44
+ added: string[];
45
+ removed: string[];
46
+ };
47
+ }
48
+ /**
49
+ * Apply multiple loadouts, merging their outputs with deduplication by targetPath.
50
+ */
51
+ export declare function applyMultiPlan(plans: Array<{
52
+ loadout: ResolvedLoadout;
53
+ plan: RenderPlan;
54
+ }>, loadoutRoot: string, projectRoot: string, mode?: OutputMode, scope?: Scope): Promise<ApplyResult>;
55
+ /**
56
+ * Remove all managed outputs recorded in the state file.
57
+ * Does NOT clear the state file — callers decide whether to call clearState().
58
+ * Also removes the loadout-managed section from .gitignore (project scope only).
59
+ */
60
+ export declare function removeManaged(loadoutRoot: string, projectRoot: string, scope?: Scope): Promise<{
61
+ removed: string[];
62
+ missing: string[];
63
+ }>;
64
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/core/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAsBH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EAIV,UAAU,EACV,KAAK,EACN,MAAM,YAAY,CAAC;AAMpB;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,KAAK,GACX,UAAU,GAAG,IAAI,CA8BnB;AAMD;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,cAAc,CAAC,CA4BzB;AA2ED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,KAAK,EACZ,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAoDrB;AAgBD;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,UAAsB,EAC5B,KAAK,GAAE,KAAiB,GACvB,OAAO,CAAC,IAAI,CAAC,CA+Ef;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,EAC5D,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,UAAsB,EAC5B,KAAK,GAAE,KAAiB,GACvB,OAAO,CAAC,WAAW,CAAC,CA4HtB;AAMD;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,KAAiB,GACvB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAgDnD"}