outfitter 0.3.4 → 0.4.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 (197) hide show
  1. package/dist/actions/add.d.ts +1 -1
  2. package/dist/actions/add.js +6 -5
  3. package/dist/actions/check-automation.d.ts +4 -2
  4. package/dist/actions/check-automation.js +13 -9
  5. package/dist/actions/check.d.ts +2 -2
  6. package/dist/actions/check.js +6 -6
  7. package/dist/actions/demo.d.ts +1 -1
  8. package/dist/actions/demo.js +3 -4
  9. package/dist/actions/docs.d.ts +7 -7
  10. package/dist/actions/docs.js +12 -13
  11. package/dist/actions/doctor.d.ts +1 -1
  12. package/dist/actions/doctor.js +8 -7
  13. package/dist/actions/init.d.ts +9 -7
  14. package/dist/actions/init.js +12 -10
  15. package/dist/actions/scaffold.d.ts +1 -1
  16. package/dist/actions/scaffold.js +14 -12
  17. package/dist/actions/shared.d.ts +1 -20
  18. package/dist/actions/shared.js +1 -5
  19. package/dist/actions/upgrade.d.ts +10 -2
  20. package/dist/actions/upgrade.js +15 -8
  21. package/dist/actions.js +51 -48
  22. package/dist/cli.js +51 -48
  23. package/dist/commands/add.js +4 -3
  24. package/dist/commands/check-action-ceremony.d.ts +1 -1
  25. package/dist/commands/check-action-ceremony.js +2 -2
  26. package/dist/commands/check-action-registry.d.ts +52 -0
  27. package/dist/commands/check-action-registry.js +13 -0
  28. package/dist/commands/check-docs-sentinel.d.ts +1 -1
  29. package/dist/commands/check-docs-sentinel.js +2 -2
  30. package/dist/commands/check-orchestrator.d.ts +1 -1
  31. package/dist/commands/check-orchestrator.js +2 -2
  32. package/dist/commands/check-preset-versions.d.ts +4 -2
  33. package/dist/commands/check-preset-versions.js +8 -3
  34. package/dist/commands/check-publish-guardrails.d.ts +1 -1
  35. package/dist/commands/check-publish-guardrails.js +2 -2
  36. package/dist/commands/check-surface-map-format.d.ts +1 -1
  37. package/dist/commands/check-surface-map-format.js +2 -2
  38. package/dist/commands/check-surface-map.d.ts +1 -1
  39. package/dist/commands/check-surface-map.js +2 -2
  40. package/dist/commands/check-tsdoc.d.ts +2 -2
  41. package/dist/commands/check.js +3 -3
  42. package/dist/commands/demo.d.ts +1 -1
  43. package/dist/commands/demo.js +1 -1
  44. package/dist/commands/docs-api.d.ts +3 -3
  45. package/dist/commands/docs-api.js +2 -2
  46. package/dist/commands/docs-export.d.ts +2 -2
  47. package/dist/commands/docs-export.js +2 -2
  48. package/dist/commands/docs-list.d.ts +2 -2
  49. package/dist/commands/docs-list.js +2 -2
  50. package/dist/commands/docs-search.d.ts +2 -2
  51. package/dist/commands/docs-search.js +2 -2
  52. package/dist/commands/docs-show.d.ts +2 -2
  53. package/dist/commands/docs-show.js +2 -2
  54. package/dist/commands/doctor.js +6 -5
  55. package/dist/commands/init-execution.d.ts +5 -5
  56. package/dist/commands/init-execution.js +6 -4
  57. package/dist/commands/init-option-resolution.d.ts +3 -3
  58. package/dist/commands/init-option-resolution.js +1 -1
  59. package/dist/commands/init-output.d.ts +5 -5
  60. package/dist/commands/init-output.js +3 -3
  61. package/dist/commands/init.d.ts +5 -5
  62. package/dist/commands/init.js +9 -9
  63. package/dist/commands/scaffold-output.js +3 -3
  64. package/dist/commands/scaffold-planning.d.ts +9 -10
  65. package/dist/commands/scaffold-planning.js +6 -4
  66. package/dist/commands/scaffold.js +12 -10
  67. package/dist/commands/shared-deps.js +2 -2
  68. package/dist/commands/upgrade-codemod-builder.d.ts +45 -0
  69. package/dist/commands/upgrade-codemod-builder.js +14 -0
  70. package/dist/commands/upgrade-output.js +2 -2
  71. package/dist/commands/upgrade-workspace.js +5 -1
  72. package/dist/commands/upgrade.js +9 -5
  73. package/dist/create/index.d.ts +1 -1
  74. package/dist/create/planner.js +1 -1
  75. package/dist/engine/blocks.d.ts +2 -2
  76. package/dist/engine/blocks.js +5 -4
  77. package/dist/engine/config.d.ts +2 -2
  78. package/dist/engine/config.js +6 -5
  79. package/dist/engine/dependency-versions.d.ts +11 -3
  80. package/dist/engine/dependency-versions.js +6 -2
  81. package/dist/engine/executor.d.ts +2 -2
  82. package/dist/engine/executor.js +23 -10
  83. package/dist/engine/index.d.ts +8 -9
  84. package/dist/engine/index.js +1 -5
  85. package/dist/engine/package-json.d.ts +3 -0
  86. package/dist/engine/package-json.js +10 -0
  87. package/dist/engine/post-scaffold.js +1 -1
  88. package/dist/engine/preset.d.ts +3 -3
  89. package/dist/engine/preset.js +4 -2
  90. package/dist/engine/render-plan.js +2 -2
  91. package/dist/engine/types.d.ts +1 -1
  92. package/dist/engine/workspace.d.ts +2 -2
  93. package/dist/engine/workspace.js +3 -2
  94. package/dist/index.d.ts +7 -7
  95. package/dist/manifest.js +1 -1
  96. package/dist/output-mode.d.ts +2 -2
  97. package/dist/output-mode.js +2 -4
  98. package/dist/scaffold-e2e/cli.d.ts +19 -0
  99. package/dist/scaffold-e2e/cli.js +14 -0
  100. package/dist/scaffold-e2e/config.d.ts +6 -0
  101. package/dist/scaffold-e2e/config.js +14 -0
  102. package/dist/scaffold-e2e/runner.d.ts +29 -0
  103. package/dist/scaffold-e2e/runner.js +17 -0
  104. package/dist/scaffold-e2e/template-guardrails.d.ts +25 -0
  105. package/dist/scaffold-e2e/template-guardrails.js +183 -0
  106. package/dist/scaffold-e2e/workspace.d.ts +27 -0
  107. package/dist/scaffold-e2e/workspace.js +18 -0
  108. package/dist/scripts/scaffold-e2e.js +78 -0
  109. package/dist/shared/{outfitter-qsrx7m4w.js → outfitter-00wxeg2g.js} +8 -8
  110. package/dist/shared/{outfitter-ex8gn945.js → outfitter-0cspz333.js} +4 -6
  111. package/dist/shared/{outfitter-rp89dafm.js → outfitter-0xp447gf.js} +55 -8
  112. package/dist/shared/outfitter-0zs8makw.js +94 -0
  113. package/dist/shared/{outfitter-5d9wbzhh.d.ts → outfitter-11r5ny73.d.ts} +2 -2
  114. package/dist/shared/{outfitter-gyayfx5r.js → outfitter-18s82np1.js} +1 -1
  115. package/dist/shared/{outfitter-954y4mzx.d.ts → outfitter-1dekqnaw.d.ts} +1 -1
  116. package/dist/shared/{outfitter-6t7xeyg1.js → outfitter-1t8gjzw6.js} +91 -3
  117. package/dist/shared/{outfitter-tqznjgbm.js → outfitter-23159bef.js} +6 -3
  118. package/dist/shared/{outfitter-y6ee0k45.d.ts → outfitter-232ean4v.d.ts} +1 -1
  119. package/dist/shared/{outfitter-3rcrvva8.js → outfitter-247et71q.js} +8 -8
  120. package/dist/shared/{outfitter-xs94pkfe.js → outfitter-3f261xh0.js} +5 -7
  121. package/dist/shared/outfitter-4bs5a2n4.js +31 -0
  122. package/dist/shared/{outfitter-76k25svs.js → outfitter-4c4q091c.js} +6 -28
  123. package/dist/shared/{outfitter-738z4c37.js → outfitter-4t818mq5.js} +21 -9
  124. package/dist/shared/{outfitter-4s9meh3j.js → outfitter-4w9sc6bw.js} +24 -0
  125. package/dist/shared/outfitter-5gaptj9b.js +83 -0
  126. package/dist/shared/{outfitter-2ysjerp6.d.ts → outfitter-5rc4hxdn.d.ts} +2 -2
  127. package/dist/shared/{outfitter-ttjr95y9.js → outfitter-7krhbg3b.js} +3 -3
  128. package/dist/shared/{outfitter-7n7vsz95.js → outfitter-7t7ces1x.js} +1 -42
  129. package/dist/shared/{outfitter-gdc7b7de.d.ts → outfitter-8231g70k.d.ts} +1 -1
  130. package/dist/shared/{outfitter-yhb23pjc.js → outfitter-8jd1sak0.js} +4 -4
  131. package/dist/shared/{outfitter-wkt0a0ra.js → outfitter-93e1shd4.js} +4 -6
  132. package/dist/shared/{outfitter-wyg1tpp5.d.ts → outfitter-a93yanw6.d.ts} +1 -1
  133. package/dist/shared/{outfitter-mstr60zz.js → outfitter-aa5nzw14.js} +47 -22
  134. package/dist/shared/{outfitter-fxry5n58.js → outfitter-b05mvbmx.js} +4 -4
  135. package/dist/shared/{outfitter-1fy7byz5.js → outfitter-bsjq8gkk.js} +6 -4
  136. package/dist/shared/{outfitter-ssrtakh3.js → outfitter-c50y6yr3.js} +4 -4
  137. package/dist/shared/{outfitter-cyvr4r8d.d.ts → outfitter-crxe5gth.d.ts} +4 -0
  138. package/dist/shared/{outfitter-fj2v5ffz.js → outfitter-d5kz9x7g.js} +1 -1
  139. package/dist/shared/{outfitter-dmfac1w9.js → outfitter-dna8exj2.js} +19 -10
  140. package/dist/shared/{outfitter-940h0x7b.js → outfitter-e44qcs0w.js} +3 -3
  141. package/dist/shared/{outfitter-r2awqszh.d.ts → outfitter-e5x0ybqt.d.ts} +12 -3
  142. package/dist/shared/{outfitter-3tx3adgj.js → outfitter-ez7qffv5.js} +8 -6
  143. package/dist/shared/outfitter-fgrqt6aq.js +179 -0
  144. package/dist/shared/outfitter-fhahf9f3.d.ts +14 -0
  145. package/dist/shared/{outfitter-c7sbs7es.js → outfitter-g6r9avgz.js} +3 -3
  146. package/dist/shared/{outfitter-ssynegbs.js → outfitter-ga59fa45.js} +1 -1
  147. package/dist/shared/{outfitter-tavatb5p.js → outfitter-gna739c3.js} +86 -31
  148. package/dist/shared/{outfitter-nxvjxrmw.d.ts → outfitter-grt5ngqq.d.ts} +1 -1
  149. package/dist/shared/{outfitter-58rn1sj1.d.ts → outfitter-gsjbcta2.d.ts} +1 -1
  150. package/dist/shared/{outfitter-q1g58t85.js → outfitter-gvpwpqnc.js} +0 -1
  151. package/dist/shared/{outfitter-x39awx8g.js → outfitter-hr4cvmjy.js} +47 -25
  152. package/dist/shared/{outfitter-xwzjgswj.js → outfitter-hw9f0zq9.js} +57 -21
  153. package/dist/shared/{outfitter-ksyvwmb5.js → outfitter-jhpcjeg1.js} +5 -5
  154. package/dist/shared/outfitter-kc46jq69.d.ts +12 -0
  155. package/dist/shared/{outfitter-px5sv5gn.js → outfitter-ksg34wka.js} +5 -5
  156. package/dist/shared/{outfitter-wrcqq29p.js → outfitter-n63ygpv3.js} +30 -23
  157. package/dist/shared/{outfitter-qsd5638j.js → outfitter-n9jp1abt.js} +16 -40
  158. package/dist/shared/{outfitter-d0kqashd.d.ts → outfitter-nhft74pe.d.ts} +4 -2
  159. package/dist/shared/{outfitter-8kmak0wc.d.ts → outfitter-nx1sywpb.d.ts} +1 -1
  160. package/dist/shared/{outfitter-5r6q2749.d.ts → outfitter-pw74st5t.d.ts} +1 -1
  161. package/dist/shared/{outfitter-h0wmtxw8.d.ts → outfitter-qka1skyw.d.ts} +1 -1
  162. package/dist/shared/{outfitter-6mpkh3zn.js → outfitter-qps83547.js} +30 -23
  163. package/dist/shared/outfitter-qzd5djgx.js +88 -0
  164. package/dist/shared/{outfitter-hf5bj2gq.js → outfitter-sh015v8k.js} +4 -4
  165. package/dist/shared/{outfitter-ypcvwg1s.js → outfitter-srznx3hj.js} +1 -1
  166. package/dist/shared/{outfitter-n0ed012k.js → outfitter-ssq33ym3.js} +8 -8
  167. package/dist/shared/outfitter-sxf8jjjn.js +7 -0
  168. package/dist/shared/{outfitter-p2wn07b7.js → outfitter-t535h0mw.js} +1 -1
  169. package/dist/shared/{outfitter-znbqe5zy.d.ts → outfitter-t8dg4tg5.d.ts} +1 -1
  170. package/dist/shared/{outfitter-ydw7x6bh.js → outfitter-t8mvabed.js} +1 -1
  171. package/dist/shared/{outfitter-6ddf91vh.js → outfitter-t9xkn37g.js} +11 -12
  172. package/dist/shared/{outfitter-x4cc5xsq.js → outfitter-wmgzyymq.js} +4 -4
  173. package/dist/shared/outfitter-wna6gp2t.js +56 -0
  174. package/dist/shared/outfitter-x0r7mfvy.js +142 -0
  175. package/dist/shared/{outfitter-b9cpnr7e.js → outfitter-x4f8v5vf.js} +1 -1
  176. package/dist/shared/{outfitter-x8w5sjnd.d.ts → outfitter-x6322tjp.d.ts} +1 -1
  177. package/dist/shared/{outfitter-6rtcemk7.d.ts → outfitter-xa4915yp.d.ts} +3 -2
  178. package/dist/shared/{outfitter-2z61gp5w.js → outfitter-xg5yryp2.js} +1 -1
  179. package/dist/shared/{outfitter-rdpw2sbp.d.ts → outfitter-xn5km042.d.ts} +6 -0
  180. package/dist/shared/{outfitter-cyhzstz0.js → outfitter-xqr4fp1n.js} +4 -4
  181. package/dist/shared/outfitter-z6tg0swx.js +87 -0
  182. package/dist/shared/{outfitter-1gmrtapz.js → outfitter-zmzrsvcn.js} +17 -21
  183. package/dist/shared/{outfitter-8ggmja91.js → outfitter-zqj1nte1.js} +66 -67
  184. package/dist/targets/index.d.ts +2 -2
  185. package/dist/targets/registry.d.ts +2 -2
  186. package/dist/targets/registry.js +13 -1
  187. package/dist/targets/types.d.ts +1 -1
  188. package/package.json +85 -39
  189. package/dist/actions/docs-output-mode.d.ts +0 -4
  190. package/dist/actions/docs-output-mode.js +0 -8
  191. package/dist/engine/template.d.ts +0 -4
  192. package/dist/engine/template.js +0 -34
  193. package/dist/shared/outfitter-7r12fj7f.js +0 -30
  194. package/dist/shared/outfitter-a79xrm12.d.ts +0 -17
  195. package/dist/shared/outfitter-ec83h4v2.js +0 -17
  196. package/dist/shared/outfitter-fbvfd5zq.d.ts +0 -13
  197. package/dist/shared/outfitter-yywcypw8.js +0 -53
@@ -0,0 +1,179 @@
1
+ // @bun
2
+ import {
3
+ resolveStructuredOutputMode
4
+ } from "./outfitter-sxf8jjjn.js";
5
+
6
+ // apps/outfitter/src/commands/check-action-registry.ts
7
+ import { existsSync, readdirSync, readFileSync } from "fs";
8
+ import { join, relative, resolve } from "path";
9
+ import { Result } from "@outfitter/contracts";
10
+ var COMMANDS_RELATIVE_DIR = "apps/outfitter/src/commands";
11
+ var ACTIONS_RELATIVE_DIR = "apps/outfitter/src/actions";
12
+ var REGISTRY_RELATIVE_PATH = "apps/outfitter/src/actions.ts";
13
+ var LOCAL_REGISTRY_RELATIVE_PATH = "src/actions.ts";
14
+ var LOCAL_COMMANDS_RELATIVE_DIR = "src/commands";
15
+
16
+ class CheckActionRegistryError extends Error {
17
+ _tag = "CheckActionRegistryError";
18
+ constructor(message, options) {
19
+ super(message, options);
20
+ this.name = "CheckActionRegistryError";
21
+ }
22
+ }
23
+ function extractCommandImports(content) {
24
+ const imports = new Set;
25
+ const importPattern = /from\s+["']\.\.\/commands\/([^"']+)\.(js|ts)["']/g;
26
+ let match;
27
+ while ((match = importPattern.exec(content)) !== null) {
28
+ const basename = match[1];
29
+ if (basename) {
30
+ imports.add(`${basename}.ts`);
31
+ }
32
+ }
33
+ return imports;
34
+ }
35
+ function parseNamedImportSymbols(namedImports) {
36
+ const withoutComments = namedImports.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
37
+ return withoutComments.split(",").map((raw) => raw.trim()).filter((raw) => raw.length > 0).map((raw) => {
38
+ const withoutType = raw.replace(/^type\s+/, "").trim();
39
+ if (!withoutType) {
40
+ return "";
41
+ }
42
+ const aliased = withoutType.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*)$/);
43
+ return aliased?.[2] ?? withoutType;
44
+ }).filter((symbol) => symbol.length > 0);
45
+ }
46
+ function extractRegisteredActionFiles(registryContent) {
47
+ const importMap = new Map;
48
+ const importPattern = /import\s+\{([^}]+)\}\s+from\s+["']\.\/actions\/([^"']+)\.(js|ts)["']/g;
49
+ let match;
50
+ while ((match = importPattern.exec(registryContent)) !== null) {
51
+ const symbols = parseNamedImportSymbols(match[1]);
52
+ const file = `${match[2]}.ts`;
53
+ for (const sym of symbols) {
54
+ importMap.set(sym, file);
55
+ }
56
+ }
57
+ const addPattern = /\.add\((\w+)\)/g;
58
+ const registeredFiles = new Set;
59
+ while ((match = addPattern.exec(registryContent)) !== null) {
60
+ const symbol = match[1];
61
+ const file = importMap.get(symbol);
62
+ if (file) {
63
+ registeredFiles.add(file);
64
+ }
65
+ }
66
+ return registeredFiles;
67
+ }
68
+ function listTsFiles(dir, rootDir = dir) {
69
+ const files = [];
70
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
71
+ if (entry.name.startsWith("__")) {
72
+ continue;
73
+ }
74
+ const entryPath = resolve(dir, entry.name);
75
+ if (entry.isDirectory()) {
76
+ files.push(...listTsFiles(entryPath, rootDir));
77
+ continue;
78
+ }
79
+ if (!entry.isFile()) {
80
+ continue;
81
+ }
82
+ if (entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts")) {
83
+ files.push(relative(rootDir, entryPath).replaceAll("\\", "/"));
84
+ }
85
+ }
86
+ return files.toSorted();
87
+ }
88
+ function resolveWorkspaceRoot(cwd) {
89
+ const resolved = resolve(cwd);
90
+ if (existsSync(resolve(resolved, REGISTRY_RELATIVE_PATH))) {
91
+ return resolved;
92
+ }
93
+ if (existsSync(resolve(resolved, LOCAL_REGISTRY_RELATIVE_PATH)) && existsSync(resolve(resolved, LOCAL_COMMANDS_RELATIVE_DIR))) {
94
+ const parentWorkspace = resolve(resolved, "../..");
95
+ if (existsSync(resolve(parentWorkspace, REGISTRY_RELATIVE_PATH))) {
96
+ return parentWorkspace;
97
+ }
98
+ }
99
+ let current = resolved;
100
+ while (true) {
101
+ if (existsSync(resolve(current, REGISTRY_RELATIVE_PATH))) {
102
+ return current;
103
+ }
104
+ const parent = resolve(current, "..");
105
+ if (parent === current) {
106
+ return resolved;
107
+ }
108
+ current = parent;
109
+ }
110
+ }
111
+ async function runCheckActionRegistry(options) {
112
+ try {
113
+ const workspaceRoot = resolveWorkspaceRoot(options.cwd);
114
+ const commandsDir = resolve(workspaceRoot, COMMANDS_RELATIVE_DIR);
115
+ const actionsDir = resolve(workspaceRoot, ACTIONS_RELATIVE_DIR);
116
+ const registryPath = resolve(workspaceRoot, REGISTRY_RELATIVE_PATH);
117
+ const registryContent = readFileSync(registryPath, "utf-8");
118
+ const registeredActionFiles = extractRegisteredActionFiles(registryContent);
119
+ const referencedCommands = new Set;
120
+ for (const actionFile of registeredActionFiles) {
121
+ const filePath = resolve(actionsDir, actionFile);
122
+ const content = readFileSync(filePath, "utf-8");
123
+ const imports = extractCommandImports(content);
124
+ for (const imp of imports) {
125
+ referencedCommands.add(imp);
126
+ }
127
+ }
128
+ const commandFiles = listTsFiles(commandsDir);
129
+ const registered = [];
130
+ const unregistered = [];
131
+ for (const file of commandFiles) {
132
+ const relativePath = join(COMMANDS_RELATIVE_DIR, file);
133
+ if (referencedCommands.has(file)) {
134
+ registered.push(relativePath);
135
+ } else {
136
+ unregistered.push(relativePath);
137
+ }
138
+ }
139
+ return Result.ok({
140
+ actionsDir,
141
+ commandsDir,
142
+ ok: unregistered.length === 0,
143
+ registered,
144
+ registeredCount: registered.length,
145
+ totalCommands: commandFiles.length,
146
+ unregistered,
147
+ unregisteredCount: unregistered.length
148
+ });
149
+ } catch (error) {
150
+ const message = error instanceof Error ? error.message : "Failed to scan action registry";
151
+ return Result.err(new CheckActionRegistryError(message, { cause: error }));
152
+ }
153
+ }
154
+ async function printCheckActionRegistryResult(result, options) {
155
+ const structuredMode = resolveStructuredOutputMode(options?.mode);
156
+ if (structuredMode) {
157
+ const serialized = structuredMode === "json" ? JSON.stringify(result, null, 2) : JSON.stringify(result);
158
+ process.stdout.write(`${serialized}
159
+ `);
160
+ return;
161
+ }
162
+ process.stdout.write(`[action-registry] scanned ${result.totalCommands} command files in ${result.commandsDir}
163
+ `);
164
+ process.stdout.write(`[action-registry] ${result.registeredCount} registered, ${result.unregisteredCount} unregistered
165
+ `);
166
+ if (result.unregistered.length === 0) {
167
+ process.stdout.write(`[action-registry] all command files are referenced by action definitions
168
+ `);
169
+ return;
170
+ }
171
+ process.stderr.write(`[action-registry] unregistered command files:
172
+ `);
173
+ for (const file of result.unregistered) {
174
+ process.stderr.write(` - ${file}
175
+ `);
176
+ }
177
+ }
178
+
179
+ export { CheckActionRegistryError, runCheckActionRegistry, printCheckActionRegistryResult };
@@ -0,0 +1,14 @@
1
+ import { OutputMode } from "@outfitter/cli/types";
2
+ type StructuredOutputMode = Extract<OutputMode, "json" | "jsonl">;
3
+ /** Output modes resolvable from CLI flags and env vars. */
4
+ type CliOutputMode = "human" | "json" | "jsonl";
5
+ /**
6
+ * Narrow an output mode to its structured subset (`"json"` | `"jsonl"`).
7
+ *
8
+ * Returns `undefined` when the mode is not machine-readable (e.g., `"human"`,
9
+ * `"tree"`, `"table"`) or when `mode` is `undefined`.
10
+ * This is a pure type-narrowing helper — it does NOT check env vars.
11
+ * Use `resolveOutputMode()` from `@outfitter/cli/query` for full resolution.
12
+ */
13
+ declare function resolveStructuredOutputMode(mode?: OutputMode): StructuredOutputMode | undefined;
14
+ export { StructuredOutputMode, CliOutputMode, resolveStructuredOutputMode };
@@ -7,7 +7,7 @@ import {
7
7
  } from "./outfitter-svts4wk2.js";
8
8
  import {
9
9
  resolveStructuredOutputMode
10
- } from "./outfitter-7r12fj7f.js";
10
+ } from "./outfitter-sxf8jjjn.js";
11
11
 
12
12
  // apps/outfitter/src/commands/docs-show.ts
13
13
  import { readFile } from "fs/promises";
@@ -63,7 +63,7 @@ async function printDocsShowResults(result, options) {
63
63
  });
64
64
  process.stdout.write(filtered);
65
65
  } else {
66
- await output(result, { mode: structuredMode });
66
+ await output(result, structuredMode);
67
67
  }
68
68
  return;
69
69
  }
@@ -86,7 +86,7 @@ async function printDocsShowResults(result, options) {
86
86
  lines.push("-".repeat(60));
87
87
  lines.push("");
88
88
  lines.push(result.content);
89
- await output(lines, { mode: "human" });
89
+ await output(lines, "human");
90
90
  }
91
91
 
92
92
  export { runDocsShow, printDocsShowResults };
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  resolveStructuredOutputMode
4
- } from "./outfitter-7r12fj7f.js";
4
+ } from "./outfitter-sxf8jjjn.js";
5
5
 
6
6
  // apps/outfitter/src/commands/check-publish-guardrails.ts
7
7
  import { existsSync, readdirSync, readFileSync } from "fs";
@@ -3,23 +3,63 @@ import {
3
3
  deriveBinName,
4
4
  deriveProjectName,
5
5
  executePlan,
6
+ getPresetsBaseDir,
6
7
  isPathWithin,
7
8
  resolveAuthor,
8
9
  resolveYear,
9
10
  scaffoldWorkspaceRoot,
10
11
  validateProjectDirectoryName
11
- } from "./outfitter-q1g58t85.js";
12
- import {
13
- runPostScaffold
14
- } from "./outfitter-4s9meh3j.js";
12
+ } from "./outfitter-gvpwpqnc.js";
15
13
  import {
16
14
  OperationCollector
17
15
  } from "./outfitter-1h7k8xxt.js";
16
+ import {
17
+ runPostScaffold
18
+ } from "./outfitter-4w9sc6bw.js";
18
19
 
19
20
  // apps/outfitter/src/commands/init-execution.ts
20
- import { existsSync } from "fs";
21
+ import { existsSync, readdirSync } from "fs";
21
22
  import { basename, join, resolve } from "path";
22
23
  import { Result } from "@outfitter/contracts";
24
+ function discoverPresetExamples() {
25
+ const examplesRoot = join(getPresetsBaseDir(), "_examples");
26
+ if (!existsSync(examplesRoot)) {
27
+ return new Map;
28
+ }
29
+ const examplesByPreset = new Map;
30
+ for (const entry of readdirSync(examplesRoot, { withFileTypes: true })) {
31
+ if (!entry.isDirectory()) {
32
+ continue;
33
+ }
34
+ const separatorIndex = entry.name.indexOf("-");
35
+ if (separatorIndex <= 0 || separatorIndex >= entry.name.length - 1) {
36
+ continue;
37
+ }
38
+ const preset = entry.name.slice(0, separatorIndex);
39
+ const example = entry.name.slice(separatorIndex + 1);
40
+ const existing = examplesByPreset.get(preset) ?? [];
41
+ existing.push(example);
42
+ examplesByPreset.set(preset, existing);
43
+ }
44
+ return new Map([...examplesByPreset.entries()].map(([preset, examples]) => [
45
+ preset,
46
+ [...new Set(examples)].toSorted()
47
+ ]));
48
+ }
49
+ function getPresetExamples() {
50
+ return discoverPresetExamples();
51
+ }
52
+ function validateExample(preset, example) {
53
+ const presetExamples = getPresetExamples();
54
+ const available = presetExamples.get(preset);
55
+ if (!available || available.length === 0) {
56
+ return Result.err(`Preset '${preset}' has no available examples. ` + `Only these presets support --example: ${[...presetExamples.keys()].join(", ")}`);
57
+ }
58
+ if (!available.includes(example)) {
59
+ return Result.err(`Unknown example '${example}' for preset '${preset}'. ` + `Available examples: ${available.join(", ")}`);
60
+ }
61
+ return Result.ok(`_examples/${preset}-${example}`);
62
+ }
23
63
  function toExecutionErrorMessage(error) {
24
64
  if (error instanceof Error) {
25
65
  return error.message;
@@ -47,6 +87,13 @@ function buildInitPlan(target, input, projectDir, resolvedBinName) {
47
87
  includeTooling: input.includeTooling,
48
88
  overlayBaseTemplate: true
49
89
  },
90
+ ...input.exampleOverlayDir ? [
91
+ {
92
+ type: "copy-example-overlay",
93
+ preset: input.exampleOverlayDir,
94
+ targetDir: projectDir
95
+ }
96
+ ] : [],
50
97
  { type: "inject-shared-config" },
51
98
  ...input.local ? [{ type: "rewrite-local-dependencies", mode: "workspace" }] : [],
52
99
  ...blocks.length > 0 ? [{ type: "add-blocks", blocks }] : []
@@ -54,38 +101,46 @@ function buildInitPlan(target, input, projectDir, resolvedBinName) {
54
101
  };
55
102
  }
56
103
  async function executeInitPipeline(input, target, options) {
57
- const projectName = deriveProjectName(input.packageName);
58
- if (input.structure === "workspace") {
104
+ let resolvedInput = input;
105
+ if (input.example) {
106
+ const exampleResult = validateExample(input.preset, input.example);
107
+ if (exampleResult.isErr()) {
108
+ return Result.err(exampleResult.error);
109
+ }
110
+ resolvedInput = { ...input, exampleOverlayDir: exampleResult.value };
111
+ }
112
+ const projectName = deriveProjectName(resolvedInput.packageName);
113
+ if (resolvedInput.structure === "workspace") {
59
114
  const invalidProjectName = validateProjectDirectoryName(projectName);
60
115
  if (invalidProjectName) {
61
116
  return Result.err(`Invalid workspace project name '${projectName}': ${invalidProjectName}`);
62
117
  }
63
118
  }
64
119
  const collector = options.dryRun ? new OperationCollector : undefined;
65
- const projectBaseDir = resolve(input.rootDir, target.placement);
120
+ const projectBaseDir = resolve(resolvedInput.rootDir, target.placement);
66
121
  const resolvedProjectDir = resolve(projectBaseDir, projectName);
67
- if (input.structure === "workspace" && !isPathWithin(projectBaseDir, resolvedProjectDir)) {
122
+ if (resolvedInput.structure === "workspace" && !isPathWithin(projectBaseDir, resolvedProjectDir)) {
68
123
  return Result.err(`Invalid workspace project name '${projectName}': path escapes '${projectBaseDir}'`);
69
124
  }
70
- const projectDir = input.structure === "workspace" ? resolvedProjectDir : input.rootDir;
71
- if (input.structure === "single") {
72
- if (existsSync(join(input.rootDir, "package.json")) && !options.force) {
73
- return Result.err(`Directory '${input.rootDir}' already has a package.json. Use --force to overwrite, or use 'outfitter add' for existing projects.`);
125
+ const projectDir = resolvedInput.structure === "workspace" ? resolvedProjectDir : resolvedInput.rootDir;
126
+ if (resolvedInput.structure === "single") {
127
+ if (existsSync(join(resolvedInput.rootDir, "package.json")) && !options.force) {
128
+ return Result.err(`Directory '${resolvedInput.rootDir}' already has a package.json. Use --force to overwrite, or use 'outfitter add' for existing projects.`);
74
129
  }
75
130
  } else {
76
- const workspaceName = input.workspaceName ?? basename(input.rootDir);
77
- const workspacePackageJsonPath = join(input.rootDir, "package.json");
131
+ const workspaceName = resolvedInput.workspaceName ?? basename(resolvedInput.rootDir);
132
+ const workspacePackageJsonPath = join(resolvedInput.rootDir, "package.json");
78
133
  if (options.dryRun) {
79
134
  if (existsSync(workspacePackageJsonPath) && !options.force) {
80
- return Result.err(`Directory '${input.rootDir}' already has a package.json. Use --force to overwrite.`);
135
+ return Result.err(`Directory '${resolvedInput.rootDir}' already has a package.json. Use --force to overwrite.`);
81
136
  }
82
137
  collector?.add({
83
138
  type: "dir-create",
84
- path: join(input.rootDir, "apps")
139
+ path: join(resolvedInput.rootDir, "apps")
85
140
  });
86
141
  collector?.add({
87
142
  type: "dir-create",
88
- path: join(input.rootDir, "packages")
143
+ path: join(resolvedInput.rootDir, "packages")
89
144
  });
90
145
  collector?.add(existsSync(workspacePackageJsonPath) ? {
91
146
  type: "file-overwrite",
@@ -96,7 +151,7 @@ async function executeInitPipeline(input, target, options) {
96
151
  path: workspacePackageJsonPath,
97
152
  source: "generated"
98
153
  });
99
- const readmePath = join(input.rootDir, "README.md");
154
+ const readmePath = join(resolvedInput.rootDir, "README.md");
100
155
  if (options.force || !existsSync(readmePath)) {
101
156
  collector?.add(existsSync(readmePath) ? {
102
157
  type: "file-overwrite",
@@ -108,7 +163,7 @@ async function executeInitPipeline(input, target, options) {
108
163
  source: "generated"
109
164
  });
110
165
  }
111
- const gitignorePath = join(input.rootDir, ".gitignore");
166
+ const gitignorePath = join(resolvedInput.rootDir, ".gitignore");
112
167
  if (options.force || !existsSync(gitignorePath)) {
113
168
  collector?.add(existsSync(gitignorePath) ? {
114
169
  type: "file-overwrite",
@@ -121,14 +176,14 @@ async function executeInitPipeline(input, target, options) {
121
176
  });
122
177
  }
123
178
  } else {
124
- const workspaceResult = scaffoldWorkspaceRoot(input.rootDir, workspaceName, options.force);
179
+ const workspaceResult = scaffoldWorkspaceRoot(resolvedInput.rootDir, workspaceName, options.force);
125
180
  if (workspaceResult.isErr()) {
126
181
  return Result.err(workspaceResult.error.message);
127
182
  }
128
183
  }
129
184
  }
130
- const resolvedBinName = input.binName ?? deriveBinName(deriveProjectName(input.packageName));
131
- const plan = buildInitPlan(target, input, projectDir, resolvedBinName);
185
+ const resolvedBinName = resolvedInput.binName ?? deriveBinName(deriveProjectName(resolvedInput.packageName));
186
+ const plan = buildInitPlan(target, resolvedInput, projectDir, resolvedBinName);
132
187
  const executeResult = await executePlan(plan, {
133
188
  force: options.force,
134
189
  ...collector ? { collector } : {}
@@ -137,11 +192,11 @@ async function executeInitPipeline(input, target, options) {
137
192
  return Result.err(toExecutionErrorMessage(executeResult.error));
138
193
  }
139
194
  const postScaffoldResult = await runPostScaffold({
140
- rootDir: input.rootDir,
195
+ rootDir: resolvedInput.rootDir,
141
196
  projectDir,
142
197
  origin: "init",
143
- target: input.preset,
144
- structure: input.structure,
198
+ target: resolvedInput.preset,
199
+ structure: resolvedInput.structure,
145
200
  skipInstall: options.skipInstall,
146
201
  skipGit: options.skipGit,
147
202
  skipCommit: options.skipCommit,
@@ -152,15 +207,15 @@ async function executeInitPipeline(input, target, options) {
152
207
  return Result.err("Post-scaffold step failed");
153
208
  }
154
209
  return Result.ok({
155
- structure: input.structure,
156
- rootDir: input.rootDir,
210
+ structure: resolvedInput.structure,
211
+ rootDir: resolvedInput.rootDir,
157
212
  projectDir,
158
- preset: input.preset,
159
- packageName: input.packageName,
213
+ preset: resolvedInput.preset,
214
+ packageName: resolvedInput.packageName,
160
215
  blocksAdded: executeResult.value.blocksAdded,
161
216
  postScaffold: postScaffoldResult.value,
162
217
  ...collector ? { dryRunPlan: collector.toJSON() } : {}
163
218
  });
164
219
  }
165
220
 
166
- export { executeInitPipeline };
221
+ export { validateExample, executeInitPipeline };
@@ -1,4 +1,4 @@
1
- import { CliOutputMode } from "./outfitter-a79xrm12.js";
1
+ import { CliOutputMode } from "./outfitter-fhahf9f3.js";
2
2
  import { InternalError, Result } from "@outfitter/contracts";
3
3
  /** Validated input for the docs.search action handler. */
4
4
  interface DocsSearchInput {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Unique identifier for a scaffold target.
3
3
  */
4
- type TargetId = "minimal" | "cli" | "mcp" | "daemon" | "library" | "full-stack" | "api" | "worker" | "web";
4
+ type TargetId = "minimal" | "basic" | "cli" | "mcp" | "daemon" | "library" | "full-stack" | "api" | "worker" | "web";
5
5
  /**
6
6
  * Whether the target produces a runnable application or a library package.
7
7
  */
@@ -3,6 +3,5 @@ export { injectSharedConfig, rewriteLocalDependencies } from "./config.js";
3
3
  export { executePlan } from "./executor.js";
4
4
  export { deriveBinName, deriveProjectName, isPathWithin, resolveAuthor, resolvePackageName, resolveYear, sanitizePackageName, validatePackageName, validateProjectDirectoryName } from "./names.js";
5
5
  export { copyPresetFiles, getOutputFilename, getPresetsBaseDir, isBinaryFile, replacePlaceholders } from "./preset.js";
6
- export { copyTemplateFiles, getTemplatesDir } from "./template.js";
7
6
  export { ScaffoldError } from "./types.js";
8
7
  export { buildWorkspaceRootPackageJson, detectWorkspaceRoot, scaffoldWorkspaceRoot } from "./workspace.js";
@@ -3,7 +3,10 @@
3
3
  import { existsSync, readdirSync, readFileSync } from "fs";
4
4
  import { dirname, join, resolve } from "path";
5
5
  import { fileURLToPath } from "url";
6
+ import { extractMessage, InternalError, Result } from "@outfitter/contracts";
6
7
  import { getResolvedVersions } from "@outfitter/presets";
8
+ import { isPlainObject } from "@outfitter/types";
9
+ var OUTFITTER_PACKAGE_NAME = "outfitter";
7
10
  var DEPENDENCY_SECTIONS = [
8
11
  "dependencies",
9
12
  "devDependencies",
@@ -17,8 +20,13 @@ function clearResolvedVersionsCache() {
17
20
  function readJsonFile(path) {
18
21
  return JSON.parse(readFileSync(path, "utf-8"));
19
22
  }
20
- function isRecord(value) {
21
- return typeof value === "object" && value !== null && !Array.isArray(value);
23
+ function normalizeVersionRange(version) {
24
+ const trimmed = version.trim();
25
+ const semverMatch = trimmed.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
26
+ if (semverMatch) {
27
+ return semverMatch[0];
28
+ }
29
+ return trimmed.replace(/^[\^~>=<]+/, "");
22
30
  }
23
31
  function normalizeRange(value) {
24
32
  const trimmed = value.trim();
@@ -37,20 +45,20 @@ function findOutfitterPackageRoot() {
37
45
  if (existsSync(packageJsonPath)) {
38
46
  try {
39
47
  const parsed = readJsonFile(packageJsonPath);
40
- if (isRecord(parsed) && typeof parsed["name"] === "string" && parsed["name"] === "outfitter") {
41
- return currentDir;
48
+ if (isPlainObject(parsed) && typeof parsed["name"] === "string" && parsed["name"] === OUTFITTER_PACKAGE_NAME) {
49
+ return Result.ok(currentDir);
42
50
  }
43
51
  } catch {}
44
52
  }
45
53
  currentDir = dirname(currentDir);
46
54
  }
47
- throw new Error(`Unable to find outfitter package root (walked 10 levels up from ${dirname(fileURLToPath(import.meta.url))}). ` + "Ensure this module is running from within the outfitter package tree.");
55
+ return Result.err(InternalError.create(`Unable to find outfitter package root (walked 10 levels up from ${dirname(fileURLToPath(import.meta.url))}). ` + "Ensure this module is running from within the outfitter package tree."));
48
56
  }
49
57
  function collectOutfitterDepsFromPackageJson(packageJson) {
50
58
  const collected = {};
51
59
  for (const section of DEPENDENCY_SECTIONS) {
52
60
  const sectionValue = packageJson[section];
53
- if (!isRecord(sectionValue)) {
61
+ if (!isPlainObject(sectionValue)) {
54
62
  continue;
55
63
  }
56
64
  for (const [name, value] of Object.entries(sectionValue)) {
@@ -83,7 +91,7 @@ function collectWorkspacePackageRanges(packageRoot) {
83
91
  }
84
92
  try {
85
93
  const parsed = readJsonFile(packageJsonPath);
86
- if (!isRecord(parsed)) {
94
+ if (!isPlainObject(parsed)) {
87
95
  continue;
88
96
  }
89
97
  const name = parsed["name"];
@@ -100,28 +108,37 @@ function collectWorkspacePackageRanges(packageRoot) {
100
108
  }
101
109
  function resolvePresetDependencyVersions() {
102
110
  if (cachedResolvedVersions) {
103
- return cachedResolvedVersions;
111
+ return Result.ok(cachedResolvedVersions);
112
+ }
113
+ try {
114
+ const { all: presetsVersions } = getResolvedVersions();
115
+ const packageRootResult = findOutfitterPackageRoot();
116
+ if (packageRootResult.isErr()) {
117
+ return packageRootResult;
118
+ }
119
+ const packageRoot = packageRootResult.value;
120
+ const packageJsonPath = join(packageRoot, "package.json");
121
+ const raw = existsSync(packageJsonPath) ? readJsonFile(packageJsonPath) : undefined;
122
+ const fromOutfitterPackage = raw !== undefined && isPlainObject(raw) ? collectOutfitterDepsFromPackageJson(raw) : {};
123
+ const fromWorkspacePackages = collectWorkspacePackageRanges(packageRoot);
124
+ const internal = {
125
+ ...fromOutfitterPackage,
126
+ ...fromWorkspacePackages
127
+ };
128
+ cachedResolvedVersions = {
129
+ internal,
130
+ external: { ...presetsVersions }
131
+ };
132
+ return Result.ok(cachedResolvedVersions);
133
+ } catch (error) {
134
+ return Result.err(InternalError.create(`Failed to resolve preset dependency versions: ${extractMessage(error)}`));
104
135
  }
105
- const { all: presetsVersions } = getResolvedVersions();
106
- const packageRoot = findOutfitterPackageRoot();
107
- const packageJsonPath = join(packageRoot, "package.json");
108
- const raw = existsSync(packageJsonPath) ? readJsonFile(packageJsonPath) : undefined;
109
- const fromOutfitterPackage = raw !== undefined && isRecord(raw) ? collectOutfitterDepsFromPackageJson(raw) : {};
110
- const fromWorkspacePackages = collectWorkspacePackageRanges(packageRoot);
111
- const internal = {
112
- ...fromOutfitterPackage,
113
- ...fromWorkspacePackages
114
- };
115
- cachedResolvedVersions = {
116
- internal,
117
- external: { ...presetsVersions }
118
- };
119
- return cachedResolvedVersions;
120
136
  }
121
137
  function applyResolvedDependencyVersions(parsedPackageJson, versions) {
138
+ const unresolved = new Set;
122
139
  for (const section of DEPENDENCY_SECTIONS) {
123
140
  const sectionValue = parsedPackageJson[section];
124
- if (!isRecord(sectionValue)) {
141
+ if (!isPlainObject(sectionValue)) {
125
142
  continue;
126
143
  }
127
144
  for (const [name, value] of Object.entries(sectionValue)) {
@@ -138,9 +155,14 @@ function applyResolvedDependencyVersions(parsedPackageJson, versions) {
138
155
  const resolvedExternal = versions.external[name];
139
156
  if (resolvedExternal) {
140
157
  sectionValue[name] = resolvedExternal;
158
+ continue;
159
+ }
160
+ if (value === "catalog:") {
161
+ unresolved.add(name);
141
162
  }
142
163
  }
143
164
  }
165
+ return [...unresolved].toSorted();
144
166
  }
145
167
 
146
- export { clearResolvedVersionsCache, resolvePresetDependencyVersions, applyResolvedDependencyVersions };
168
+ export { DEPENDENCY_SECTIONS, clearResolvedVersionsCache, normalizeVersionRange, resolvePresetDependencyVersions, applyResolvedDependencyVersions };