atomism 0.1.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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/chunk-34O5KJWR.js +81 -0
  4. package/dist/chunk-34O5KJWR.js.map +1 -0
  5. package/dist/chunk-55AP34JO.js +116 -0
  6. package/dist/chunk-55AP34JO.js.map +1 -0
  7. package/dist/chunk-6MDHM2B4.js +17 -0
  8. package/dist/chunk-6MDHM2B4.js.map +1 -0
  9. package/dist/chunk-GU2R4KLP.js +43 -0
  10. package/dist/chunk-GU2R4KLP.js.map +1 -0
  11. package/dist/chunk-H7WC3NXZ.js +39 -0
  12. package/dist/chunk-H7WC3NXZ.js.map +1 -0
  13. package/dist/chunk-P33CQFMY.js +329 -0
  14. package/dist/chunk-P33CQFMY.js.map +1 -0
  15. package/dist/chunk-P6X7T4KA.js +200 -0
  16. package/dist/chunk-P6X7T4KA.js.map +1 -0
  17. package/dist/chunk-PLQJM2KT.js +9 -0
  18. package/dist/chunk-PLQJM2KT.js.map +1 -0
  19. package/dist/chunk-RS2IEGW3.js +10 -0
  20. package/dist/chunk-RS2IEGW3.js.map +1 -0
  21. package/dist/chunk-S6Z5G5DB.js +84 -0
  22. package/dist/chunk-S6Z5G5DB.js.map +1 -0
  23. package/dist/chunk-UVUDQ4XP.js +259 -0
  24. package/dist/chunk-UVUDQ4XP.js.map +1 -0
  25. package/dist/chunk-UWVZQSP4.js +597 -0
  26. package/dist/chunk-UWVZQSP4.js.map +1 -0
  27. package/dist/chunk-YKJO3ZFY.js +308 -0
  28. package/dist/chunk-YKJO3ZFY.js.map +1 -0
  29. package/dist/cli.d.ts +1 -0
  30. package/dist/cli.js +152 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/create-atom-AXPDBYQL.js +153 -0
  33. package/dist/create-atom-AXPDBYQL.js.map +1 -0
  34. package/dist/escalate-BTEJT5NL.js +211 -0
  35. package/dist/escalate-BTEJT5NL.js.map +1 -0
  36. package/dist/extract-RPKCTINT.js +514 -0
  37. package/dist/extract-RPKCTINT.js.map +1 -0
  38. package/dist/graduate-453M7ZRQ.js +222 -0
  39. package/dist/graduate-453M7ZRQ.js.map +1 -0
  40. package/dist/helpers-PJPFPYBQ.js +11 -0
  41. package/dist/helpers-PJPFPYBQ.js.map +1 -0
  42. package/dist/history-OPD7NLZW.js +258 -0
  43. package/dist/history-OPD7NLZW.js.map +1 -0
  44. package/dist/import-generator-4CKRBMTE.js +1864 -0
  45. package/dist/import-generator-4CKRBMTE.js.map +1 -0
  46. package/dist/index.d.ts +230 -0
  47. package/dist/index.js +41 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/init-2FINDMYK.js +741 -0
  50. package/dist/init-2FINDMYK.js.map +1 -0
  51. package/dist/list-NEBVBGG3.js +71 -0
  52. package/dist/list-NEBVBGG3.js.map +1 -0
  53. package/dist/parser-3BILOSOO.js +157 -0
  54. package/dist/parser-3BILOSOO.js.map +1 -0
  55. package/dist/plan-DNVARHWH.js +249 -0
  56. package/dist/plan-DNVARHWH.js.map +1 -0
  57. package/dist/register-XTRMSH7Y.js +91 -0
  58. package/dist/register-XTRMSH7Y.js.map +1 -0
  59. package/dist/revert-J4CRDE2K.js +87 -0
  60. package/dist/revert-J4CRDE2K.js.map +1 -0
  61. package/dist/run-3GI3SBYL.js +188 -0
  62. package/dist/run-3GI3SBYL.js.map +1 -0
  63. package/dist/scan-generators-ST4TBEY7.js +375 -0
  64. package/dist/scan-generators-ST4TBEY7.js.map +1 -0
  65. package/dist/signatures-K5QIL4WG.js +258 -0
  66. package/dist/signatures-K5QIL4WG.js.map +1 -0
  67. package/dist/skills-assign-IHOXX4AI.js +182 -0
  68. package/dist/skills-assign-IHOXX4AI.js.map +1 -0
  69. package/dist/skills-load-JSD5UG2K.js +20 -0
  70. package/dist/skills-load-JSD5UG2K.js.map +1 -0
  71. package/dist/skills-scan-WACJFRJN.js +25 -0
  72. package/dist/skills-scan-WACJFRJN.js.map +1 -0
  73. package/dist/skills-suggest-JFI2NUJI.js +269 -0
  74. package/dist/skills-suggest-JFI2NUJI.js.map +1 -0
  75. package/dist/status-KQVSAZFR.js +111 -0
  76. package/dist/status-KQVSAZFR.js.map +1 -0
  77. package/dist/suggest-IFFJQFIW.js +183 -0
  78. package/dist/suggest-IFFJQFIW.js.map +1 -0
  79. package/dist/test-HP3FG3MO.js +152 -0
  80. package/dist/test-HP3FG3MO.js.map +1 -0
  81. package/dist/test-gen-2ZGPOP35.js +347 -0
  82. package/dist/test-gen-2ZGPOP35.js.map +1 -0
  83. package/dist/trust-4R26DULG.js +248 -0
  84. package/dist/trust-4R26DULG.js.map +1 -0
  85. package/dist/validate-generator-46H2LYYQ.js +410 -0
  86. package/dist/validate-generator-46H2LYYQ.js.map +1 -0
  87. package/dist/workflow-5UVLBS7J.js +655 -0
  88. package/dist/workflow-5UVLBS7J.js.map +1 -0
  89. package/package.json +84 -0
@@ -0,0 +1,1864 @@
1
+ import {
2
+ toErrorMessage
3
+ } from "./chunk-PLQJM2KT.js";
4
+
5
+ // src/importer/plop.ts
6
+ import { join, basename } from "path";
7
+ import { readFile, writeFile, mkdir, access } from "fs/promises";
8
+ var PROMPT_TYPE_MAP = {
9
+ input: "z.string()",
10
+ number: "z.number()",
11
+ confirm: "z.boolean()",
12
+ list: "z.string()",
13
+ // Will be enum if choices exist
14
+ rawlist: "z.string()",
15
+ expand: "z.string()",
16
+ checkbox: "z.array(z.string())",
17
+ password: "z.string()",
18
+ editor: "z.string()"
19
+ };
20
+ async function pathExists(path) {
21
+ try {
22
+ await access(path);
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ function extractGenerators(content) {
29
+ const generators = /* @__PURE__ */ new Map();
30
+ const regex = /setGenerator\s*\(\s*['"]([^'"]+)['"]\s*,\s*(\{[\s\S]*?\})\s*\)/g;
31
+ let match;
32
+ while ((match = regex.exec(content)) !== null) {
33
+ const name = match[1];
34
+ const config = match[2];
35
+ if (name && config) {
36
+ generators.set(name, config);
37
+ }
38
+ }
39
+ return generators;
40
+ }
41
+ function escapeStringLiteral(str) {
42
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
43
+ }
44
+ function areBracesBalanced(str) {
45
+ let count = 0;
46
+ for (const char of str) {
47
+ if (char === "{") count++;
48
+ if (char === "}") count--;
49
+ if (count < 0) return false;
50
+ }
51
+ return count === 0;
52
+ }
53
+ function parsePrompts(config) {
54
+ const prompts = [];
55
+ const promptsMatch = config.match(/prompts\s*:\s*\[([\s\S]*?)\]/);
56
+ if (!promptsMatch) {
57
+ return prompts;
58
+ }
59
+ const promptsStr = promptsMatch[1];
60
+ if (!promptsStr) return prompts;
61
+ const promptParts = promptsStr.split(/\{\s*type\s*:/);
62
+ for (const part of promptParts) {
63
+ if (!part.trim()) continue;
64
+ const promptStr = "{ type:" + part;
65
+ const typeMatch = promptStr.match(/type\s*:\s*['"]([^'"]+)['"]/);
66
+ const type = typeMatch?.[1];
67
+ if (!type) continue;
68
+ const nameMatch = promptStr.match(/name\s*:\s*['"]([^'"]+)['"]/);
69
+ const name = nameMatch?.[1];
70
+ if (!name) continue;
71
+ const messageMatch = promptStr.match(/message\s*:\s*['"]([^'"]+)['"]/);
72
+ const message = messageMatch?.[1];
73
+ const defaultMatch = promptStr.match(/default\s*:\s*['"]?([^'",}]+)['"]?/);
74
+ const defaultVal = defaultMatch?.[1];
75
+ prompts.push({
76
+ type,
77
+ name,
78
+ message,
79
+ default: defaultVal
80
+ });
81
+ }
82
+ return prompts;
83
+ }
84
+ function parseActions(config) {
85
+ const actions = [];
86
+ const actionsMatch = config.match(/actions\s*:\s*\[([\s\S]*?)\]/);
87
+ if (!actionsMatch) {
88
+ return actions;
89
+ }
90
+ const actionsStr = actionsMatch[1];
91
+ if (!actionsStr) return actions;
92
+ const actionParts = actionsStr.split(/\{\s*type\s*:/);
93
+ for (const part of actionParts) {
94
+ if (!part.trim()) continue;
95
+ const actionStr = "{ type:" + part;
96
+ const typeMatch = actionStr.match(/type\s*:\s*['"]([^'"]+)['"]/);
97
+ const type = typeMatch?.[1];
98
+ if (!type) continue;
99
+ const pathMatch = actionStr.match(/path\s*:\s*['"`]([^'"`]*(?:\{\{[^}]+\}\}[^'"`]*)*)['"`]/);
100
+ const path = pathMatch?.[1];
101
+ const templateFileMatch = actionStr.match(/templateFile\s*:\s*['"`]([^'"`]+)['"`]/);
102
+ const templateFile = templateFileMatch?.[1];
103
+ const templateMatch = actionStr.match(/template\s*:\s*['"`]([^'"`]+)['"`]/);
104
+ const template = templateMatch?.[1];
105
+ actions.push({
106
+ type,
107
+ path,
108
+ templateFile,
109
+ template
110
+ });
111
+ }
112
+ return actions;
113
+ }
114
+ function promptsToZod(prompts) {
115
+ if (prompts.length === 0) {
116
+ return "z.object({})";
117
+ }
118
+ const fields = prompts.map((prompt) => {
119
+ let zodType = PROMPT_TYPE_MAP[prompt.type] || "z.string()";
120
+ if (prompt.message) {
121
+ zodType += `.describe('${escapeStringLiteral(prompt.message)}')`;
122
+ }
123
+ if (prompt.default !== void 0) {
124
+ if (typeof prompt.default === "string") {
125
+ zodType += `.default('${escapeStringLiteral(prompt.default)}')`;
126
+ } else if (typeof prompt.default === "number") {
127
+ zodType += `.default(${prompt.default})`;
128
+ } else if (typeof prompt.default === "boolean") {
129
+ zodType += `.default(${prompt.default})`;
130
+ }
131
+ }
132
+ return ` ${prompt.name}: ${zodType}`;
133
+ });
134
+ return `z.object({
135
+ ${fields.join(",\n")}
136
+ })`;
137
+ }
138
+ function actionsToTemplate(actions, prompts) {
139
+ const paramNames = prompts.map((p) => p.name);
140
+ const destructure = paramNames.length > 0 ? `{ ${paramNames.join(", ")} }` : "_params";
141
+ const outputs = [];
142
+ for (const action of actions) {
143
+ if (action.type === "add" && action.path) {
144
+ const path = action.path.replace(/\{\{([^}]+)\}\}/g, "${$1}");
145
+ outputs.push(` // Output: ${path}`);
146
+ if (action.templateFile) {
147
+ outputs.push(` // Template: ${action.templateFile}`);
148
+ } else if (action.template) {
149
+ outputs.push(` // Inline template`);
150
+ }
151
+ } else if (action.type === "modify" && action.path) {
152
+ const path = action.path.replace(/\{\{([^}]+)\}\}/g, "${$1}");
153
+ outputs.push(` // Modify: ${path}`);
154
+ } else if (action.type === "append" && action.path) {
155
+ const path = action.path.replace(/\{\{([^}]+)\}\}/g, "${$1}");
156
+ outputs.push(` // Append to: ${path}`);
157
+ }
158
+ }
159
+ if (outputs.length === 0) {
160
+ outputs.push(" // No actions to convert");
161
+ }
162
+ return `(${destructure}) => {
163
+ ${outputs.join("\n")}
164
+ return \`
165
+ // TODO: Implement template based on Plop actions
166
+ // Original actions: ${actions.length}
167
+ \`;
168
+ }`;
169
+ }
170
+ function generateAtomicGenerator(parsed) {
171
+ const zodSchema = promptsToZod(parsed.prompts);
172
+ const templateFn = actionsToTemplate(parsed.actions, parsed.prompts);
173
+ return `/**
174
+ * Atomic generator: ${parsed.name}
175
+ *
176
+ * Imported from Plop generator.
177
+ * ${parsed.description ? `Description: ${parsed.description}` : ""}
178
+ *
179
+ * @module generators/${parsed.name}
180
+ */
181
+
182
+ import { z } from 'atomism';
183
+
184
+ /**
185
+ * Generator parameters schema.
186
+ *
187
+ * Converted from Plop prompts:
188
+ ${parsed.prompts.map((p) => ` * - ${p.name}: ${p.type}${p.message ? ` (${p.message})` : ""}`).join("\n")}
189
+ */
190
+ export const paramsSchema = ${zodSchema};
191
+
192
+ export type Params = z.infer<typeof paramsSchema>;
193
+
194
+ /**
195
+ * Template function.
196
+ *
197
+ * Converted from Plop actions:
198
+ ${parsed.actions.map((a) => ` * - ${a.type}: ${a.path || a.templateFile || "inline"}`).join("\n")}
199
+ */
200
+ export const template = ${templateFn};
201
+
202
+ /**
203
+ * Generator metadata.
204
+ */
205
+ export const generator = {
206
+ name: '${escapeStringLiteral(parsed.name)}',
207
+ description: '${escapeStringLiteral(parsed.description || `Generator for ${parsed.name}`)}',
208
+ params: paramsSchema,
209
+ template,
210
+ language: 'typescript' as const,
211
+ };
212
+
213
+ export default generator;
214
+ `;
215
+ }
216
+ async function importPlopGenerator(options) {
217
+ const {
218
+ projectRoot,
219
+ generatorName,
220
+ outputDir = "generators",
221
+ overwrite = false
222
+ } = options;
223
+ const result = {
224
+ success: false,
225
+ mappings: [],
226
+ warnings: [],
227
+ errors: []
228
+ };
229
+ const plopFiles = ["plopfile.js", "plopfile.ts", "plopfile.mjs", "plopfile.cjs"];
230
+ let plopPath = null;
231
+ for (const file of plopFiles) {
232
+ const path = join(projectRoot, file);
233
+ if (await pathExists(path)) {
234
+ plopPath = path;
235
+ break;
236
+ }
237
+ }
238
+ if (!plopPath) {
239
+ result.errors.push("No plopfile found in project root");
240
+ return result;
241
+ }
242
+ let content;
243
+ try {
244
+ content = await readFile(plopPath, "utf-8");
245
+ } catch (err) {
246
+ result.errors.push(`Failed to read plopfile: ${toErrorMessage(err)}`);
247
+ return result;
248
+ }
249
+ const generators = extractGenerators(content);
250
+ if (!generators.has(generatorName)) {
251
+ result.errors.push(
252
+ `Generator '${generatorName}' not found. Available: ${Array.from(generators.keys()).join(", ") || "none"}`
253
+ );
254
+ return result;
255
+ }
256
+ const generatorConfig = generators.get(generatorName);
257
+ if (!areBracesBalanced(generatorConfig)) {
258
+ result.warnings.push(
259
+ `Generator '${generatorName}' config may be malformed (unbalanced braces). Parsing may be incomplete.`
260
+ );
261
+ }
262
+ const prompts = parsePrompts(generatorConfig);
263
+ const actions = parseActions(generatorConfig);
264
+ for (const prompt of prompts) {
265
+ result.mappings.push({
266
+ source: `prompt:${prompt.name}`,
267
+ target: `params.${prompt.name}`,
268
+ type: "prompt",
269
+ notes: `${prompt.type} \u2192 ${PROMPT_TYPE_MAP[prompt.type] || "z.string()"}`
270
+ });
271
+ }
272
+ for (const action of actions) {
273
+ result.mappings.push({
274
+ source: `action:${action.type}`,
275
+ target: action.path || action.templateFile || "inline",
276
+ type: "action",
277
+ notes: action.templateFile ? `templateFile: ${action.templateFile}` : void 0
278
+ });
279
+ }
280
+ const templateFiles = actions.filter((a) => a.templateFile).map((a) => a.templateFile);
281
+ const parsed = {
282
+ name: generatorName,
283
+ prompts,
284
+ actions,
285
+ templateFiles
286
+ };
287
+ const descMatch = generatorConfig.match(/description\s*:\s*['"]([^'"]+)['"]/);
288
+ if (descMatch) {
289
+ parsed.description = descMatch[1];
290
+ }
291
+ const atomicGenerator = generateAtomicGenerator(parsed);
292
+ const safeGeneratorName = basename(generatorName);
293
+ if (safeGeneratorName !== generatorName || generatorName.includes("..")) {
294
+ result.errors.push(
295
+ `Invalid generator name '${generatorName}': must be a simple filename without path components`
296
+ );
297
+ return result;
298
+ }
299
+ const outputPath = join(projectRoot, outputDir);
300
+ try {
301
+ await mkdir(outputPath, { recursive: true });
302
+ } catch (err) {
303
+ result.errors.push(`Failed to create output directory: ${toErrorMessage(err)}`);
304
+ return result;
305
+ }
306
+ const generatorPath = join(outputPath, `${safeGeneratorName}.ts`);
307
+ if (await pathExists(generatorPath) && !overwrite) {
308
+ result.errors.push(
309
+ `Generator file already exists: ${generatorPath}. Use --overwrite to replace.`
310
+ );
311
+ return result;
312
+ }
313
+ try {
314
+ await writeFile(generatorPath, atomicGenerator, "utf-8");
315
+ } catch (err) {
316
+ result.errors.push(`Failed to write generator: ${toErrorMessage(err)}`);
317
+ return result;
318
+ }
319
+ if (prompts.length === 0) {
320
+ result.warnings.push("No prompts found - generator will have empty params");
321
+ }
322
+ if (actions.length === 0) {
323
+ result.warnings.push("No actions found - template function is placeholder");
324
+ }
325
+ for (const action of actions) {
326
+ if (action.templateFile) {
327
+ result.warnings.push(
328
+ `Template file '${action.templateFile}' needs manual conversion`
329
+ );
330
+ }
331
+ }
332
+ result.success = true;
333
+ result.generatorPath = generatorPath;
334
+ return result;
335
+ }
336
+
337
+ // src/importer/hygen.ts
338
+ import { join as join2, basename as basename2 } from "path";
339
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, access as access2, readdir } from "fs/promises";
340
+ var PROMPT_TYPE_MAP2 = {
341
+ input: "z.string()",
342
+ text: "z.string()",
343
+ number: "z.number()",
344
+ confirm: "z.boolean()",
345
+ toggle: "z.boolean()",
346
+ select: "z.string()",
347
+ multiselect: "z.array(z.string())",
348
+ list: "z.string()",
349
+ autocomplete: "z.string()"
350
+ };
351
+ async function pathExists2(path) {
352
+ try {
353
+ await access2(path);
354
+ return true;
355
+ } catch {
356
+ return false;
357
+ }
358
+ }
359
+ function escapeStringLiteral2(str) {
360
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
361
+ }
362
+ function sanitizeJsDocContent(str) {
363
+ return str.replace(/\*\//g, "*\\/").replace(/\/\*/g, "/\\*");
364
+ }
365
+ function parseFrontmatter(content) {
366
+ const frontmatter = {};
367
+ const normalizedContent = content.replace(/\r\n/g, "\n");
368
+ if (!normalizedContent.startsWith("---")) {
369
+ return { frontmatter, body: normalizedContent };
370
+ }
371
+ const endIndex = normalizedContent.indexOf("\n---", 3);
372
+ if (endIndex === -1) {
373
+ return { frontmatter, body: normalizedContent };
374
+ }
375
+ const frontmatterStr = normalizedContent.substring(4, endIndex);
376
+ const body = normalizedContent.substring(endIndex + 4).trim();
377
+ const lines = frontmatterStr.split("\n");
378
+ for (const line of lines) {
379
+ const match = line.match(/^(\w+):\s*(.+)$/);
380
+ if (match) {
381
+ const key = match[1];
382
+ let value = match[2].trim();
383
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
384
+ value = value.slice(1, -1);
385
+ }
386
+ if (key) {
387
+ frontmatter[key] = value;
388
+ }
389
+ }
390
+ }
391
+ return { frontmatter, body };
392
+ }
393
+ function convertEjsToTemplateLiteral(ejs) {
394
+ let hasCodeBlocks = false;
395
+ if (/<%((?!=|-)[\s\S]*?)%>/.test(ejs)) {
396
+ hasCodeBlocks = true;
397
+ }
398
+ let template = ejs.replace(/<%=\s*([^%]+?)\s*%>/g, "${$1}").replace(/<%-\s*([^%]+?)\s*%>/g, "${$1}").replace(/<%[\s\S]*?%>/g, "/* EJS code block - manual conversion needed */");
399
+ template = template.replace(/`/g, "\\`");
400
+ return { template, hasCodeBlocks };
401
+ }
402
+ function extractEjsVariables(ejs) {
403
+ const variables = /* @__PURE__ */ new Set();
404
+ const skipTokens = ["h", "locals", "helpers", "if", "else", "for", "while", "true", "false", "null", "undefined"];
405
+ const regex = /<%[=-]\s*([^%]+?)\s*%>/g;
406
+ let match;
407
+ while ((match = regex.exec(ejs)) !== null) {
408
+ const expression = match[1];
409
+ if (!expression) continue;
410
+ const tokenMatches = expression.matchAll(/\b(\w+)\b/g);
411
+ for (const tokenMatch of tokenMatches) {
412
+ const token = tokenMatch[1];
413
+ if (!token) continue;
414
+ if (skipTokens.includes(token)) continue;
415
+ const tokenEndIndex = tokenMatch.index + tokenMatch[0].length;
416
+ const afterToken = expression.slice(tokenEndIndex).trimStart();
417
+ if (!afterToken.startsWith("(")) {
418
+ variables.add(token);
419
+ }
420
+ }
421
+ }
422
+ return Array.from(variables);
423
+ }
424
+ function parsePromptJs(content) {
425
+ const prompts = [];
426
+ const objectRegex = /\{[^{}]*\}/g;
427
+ let objMatch;
428
+ while ((objMatch = objectRegex.exec(content)) !== null) {
429
+ const promptStr = objMatch[0];
430
+ const typeMatch = promptStr.match(/type\s*:\s*['"](\w+)['"]/);
431
+ const nameMatch = promptStr.match(/name\s*:\s*['"](\w+)['"]/);
432
+ const type = typeMatch?.[1];
433
+ const name = nameMatch?.[1];
434
+ if (type && name) {
435
+ const messageMatch = promptStr.match(/message\s*:\s*['"]([^'"]+)['"]/);
436
+ const message = messageMatch?.[1];
437
+ const initialMatch = promptStr.match(/initial\s*:\s*['"]?([^'",}]+)['"]?/);
438
+ const initial = initialMatch?.[1]?.trim();
439
+ prompts.push({
440
+ type,
441
+ name,
442
+ message,
443
+ initial
444
+ });
445
+ }
446
+ }
447
+ return prompts;
448
+ }
449
+ function promptsToZod2(prompts) {
450
+ if (prompts.length === 0) {
451
+ return "z.object({})";
452
+ }
453
+ const fields = prompts.map((prompt) => {
454
+ let zodType = PROMPT_TYPE_MAP2[prompt.type] || "z.string()";
455
+ if (prompt.message) {
456
+ zodType += `.describe('${escapeStringLiteral2(prompt.message)}')`;
457
+ }
458
+ if (prompt.initial !== void 0) {
459
+ if (Array.isArray(prompt.initial)) {
460
+ const items = prompt.initial.map(
461
+ (item) => typeof item === "string" ? `'${escapeStringLiteral2(item)}'` : String(item)
462
+ );
463
+ zodType += `.default([${items.join(", ")}])`;
464
+ } else if (typeof prompt.initial === "string") {
465
+ zodType += `.default('${escapeStringLiteral2(prompt.initial)}')`;
466
+ } else if (typeof prompt.initial === "number") {
467
+ zodType += `.default(${prompt.initial})`;
468
+ } else if (typeof prompt.initial === "boolean") {
469
+ zodType += `.default(${prompt.initial})`;
470
+ }
471
+ }
472
+ return ` ${prompt.name}: ${zodType}`;
473
+ });
474
+ return `z.object({
475
+ ${fields.join(",\n")}
476
+ })`;
477
+ }
478
+ async function scanHygenGenerators(templatesDir) {
479
+ const generators = /* @__PURE__ */ new Map();
480
+ if (!await pathExists2(templatesDir)) {
481
+ return generators;
482
+ }
483
+ try {
484
+ const entries = await readdir(templatesDir, { withFileTypes: true });
485
+ for (const entry of entries) {
486
+ if (entry.isDirectory()) {
487
+ const generatorName = entry.name;
488
+ const generatorPath = join2(templatesDir, generatorName);
489
+ try {
490
+ const actionEntries = await readdir(generatorPath, { withFileTypes: true });
491
+ const actions = actionEntries.filter((e) => e.isDirectory()).map((e) => e.name);
492
+ if (actions.length > 0) {
493
+ generators.set(generatorName, actions);
494
+ }
495
+ } catch (err) {
496
+ console.warn(`Warning: Failed to scan generator '${generatorName}' at ${generatorPath}: ${toErrorMessage(err)}`);
497
+ }
498
+ }
499
+ }
500
+ } catch (err) {
501
+ console.warn(`Warning: Failed to scan templates directory: ${toErrorMessage(err)}`);
502
+ }
503
+ return generators;
504
+ }
505
+ function generateAtomicGenerator2(parsed) {
506
+ const zodSchema = promptsToZod2(parsed.prompts);
507
+ const allVariables = /* @__PURE__ */ new Set();
508
+ for (const prompt of parsed.prompts) {
509
+ allVariables.add(prompt.name);
510
+ }
511
+ for (const template of parsed.templates) {
512
+ const vars = extractEjsVariables(template.body);
513
+ for (const v of vars) {
514
+ allVariables.add(v);
515
+ }
516
+ }
517
+ const paramNames = Array.from(allVariables);
518
+ const destructure = paramNames.length > 0 ? `{ ${paramNames.join(", ")} }` : "_params";
519
+ const templateOutputs = [];
520
+ const convertedBodies = [];
521
+ for (const template of parsed.templates) {
522
+ const { template: convertedBody, hasCodeBlocks } = convertEjsToTemplateLiteral(template.body);
523
+ const outputPath = template.frontmatter["to"] ? convertEjsToTemplateLiteral(template.frontmatter["to"]).template : template.filename;
524
+ templateOutputs.push(` // Output: ${outputPath}`);
525
+ if (hasCodeBlocks) {
526
+ templateOutputs.push(` // WARNING: Contains EJS code blocks that need manual conversion`);
527
+ }
528
+ convertedBodies.push(`// --- ${template.filename} ---
529
+ ${convertedBody}`);
530
+ }
531
+ if (templateOutputs.length === 0) {
532
+ templateOutputs.push(" // No templates found");
533
+ }
534
+ const combinedBodies = convertedBodies.length > 0 ? convertedBodies.join("\n\n") : "// No templates found";
535
+ const templateFn = `(${destructure}) => {
536
+ ${templateOutputs.join("\n")}
537
+ return \`
538
+ ${combinedBodies}
539
+ \`;
540
+ }`;
541
+ const safeName = escapeStringLiteral2(parsed.name);
542
+ const safeAction = escapeStringLiteral2(parsed.action);
543
+ const safeDescription = escapeStringLiteral2(parsed.description || `Generator for ${parsed.name}/${parsed.action}`);
544
+ return `/**
545
+ * Atomic generator: ${safeName}/${safeAction}
546
+ *
547
+ * Imported from Hygen generator.
548
+ *
549
+ * @module generators/${safeName}-${safeAction}
550
+ */
551
+
552
+ import { z } from 'atomism';
553
+
554
+ /**
555
+ * Generator parameters schema.
556
+ *
557
+ * Converted from Hygen prompts:
558
+ ${parsed.prompts.map((p) => ` * - ${sanitizeJsDocContent(p.name)}: ${sanitizeJsDocContent(p.type)}${p.message ? ` (${sanitizeJsDocContent(p.message)})` : ""}`).join("\n")}
559
+ */
560
+ export const paramsSchema = ${zodSchema};
561
+
562
+ export type Params = z.infer<typeof paramsSchema>;
563
+
564
+ /**
565
+ * Template function.
566
+ *
567
+ * Converted from Hygen templates:
568
+ ${parsed.templates.map((t) => ` * - ${sanitizeJsDocContent(t.filename)}: ${sanitizeJsDocContent(t.frontmatter["to"] || "inline")}`).join("\n")}
569
+ */
570
+ export const template = ${templateFn};
571
+
572
+ /**
573
+ * Generator metadata.
574
+ */
575
+ export const generator = {
576
+ name: '${safeName}-${safeAction}',
577
+ description: '${safeDescription}',
578
+ params: paramsSchema,
579
+ template,
580
+ language: 'typescript' as const,
581
+ };
582
+
583
+ export default generator;
584
+ `;
585
+ }
586
+ async function importHygenGenerator(options) {
587
+ const {
588
+ projectRoot,
589
+ generatorName,
590
+ actionName,
591
+ outputDir = "generators",
592
+ overwrite = false
593
+ } = options;
594
+ const result = {
595
+ success: false,
596
+ mappings: [],
597
+ warnings: [],
598
+ errors: []
599
+ };
600
+ const templatesDir = join2(projectRoot, "_templates");
601
+ if (!await pathExists2(templatesDir)) {
602
+ result.errors.push("No _templates directory found in project root");
603
+ return result;
604
+ }
605
+ const generators = await scanHygenGenerators(templatesDir);
606
+ if (!generators.has(generatorName)) {
607
+ result.errors.push(
608
+ `Generator '${generatorName}' not found. Available: ${Array.from(generators.keys()).join(", ") || "none"}`
609
+ );
610
+ return result;
611
+ }
612
+ const actions = generators.get(generatorName);
613
+ let targetAction = actionName;
614
+ if (!targetAction) {
615
+ if (actions.length === 1) {
616
+ targetAction = actions[0];
617
+ } else {
618
+ result.errors.push(
619
+ `Multiple actions available for '${generatorName}'. Specify one: ${actions.join(", ")}`
620
+ );
621
+ return result;
622
+ }
623
+ }
624
+ if (!actions.includes(targetAction)) {
625
+ result.errors.push(
626
+ `Action '${targetAction}' not found for generator '${generatorName}'. Available: ${actions.join(", ")}`
627
+ );
628
+ return result;
629
+ }
630
+ const actionDir = join2(templatesDir, generatorName, targetAction);
631
+ const templates = [];
632
+ const prompts = [];
633
+ try {
634
+ const files = await readdir(actionDir);
635
+ for (const file of files) {
636
+ const filePath = join2(actionDir, file);
637
+ if (file === "prompt.js" || file === "prompt.cjs" || file === "prompt.mjs") {
638
+ const content = await readFile2(filePath, "utf-8");
639
+ const parsedPrompts = parsePromptJs(content);
640
+ prompts.push(...parsedPrompts);
641
+ for (const prompt of parsedPrompts) {
642
+ result.mappings.push({
643
+ source: `prompt:${prompt.name}`,
644
+ target: `params.${prompt.name}`,
645
+ type: "prompt",
646
+ notes: `${prompt.type} \u2192 ${PROMPT_TYPE_MAP2[prompt.type] || "z.string()"}`
647
+ });
648
+ }
649
+ } else if (file.endsWith(".ejs.t") || file.endsWith(".t")) {
650
+ const content = await readFile2(filePath, "utf-8");
651
+ const { frontmatter, body } = parseFrontmatter(content);
652
+ templates.push({
653
+ filename: file,
654
+ frontmatter,
655
+ body,
656
+ outputPath: frontmatter["to"]
657
+ });
658
+ result.mappings.push({
659
+ source: `template:${file}`,
660
+ target: frontmatter["to"] || "inline",
661
+ type: "template",
662
+ notes: frontmatter["to"] ? `to: ${frontmatter["to"]}` : void 0
663
+ });
664
+ if (/<%((?!=|-)[\s\S]*?)%>/.test(body)) {
665
+ result.warnings.push(
666
+ `Template '${file}' contains EJS code blocks that need manual conversion`
667
+ );
668
+ }
669
+ }
670
+ }
671
+ } catch (err) {
672
+ result.errors.push(`Failed to read templates: ${toErrorMessage(err)}`);
673
+ return result;
674
+ }
675
+ const parsed = {
676
+ name: generatorName,
677
+ action: targetAction,
678
+ prompts,
679
+ templates
680
+ };
681
+ const atomicGenerator = generateAtomicGenerator2(parsed);
682
+ const outputName = `${generatorName}-${targetAction}`;
683
+ const safeOutputName = basename2(outputName);
684
+ if (safeOutputName !== outputName || outputName.includes("..")) {
685
+ result.errors.push(
686
+ `Invalid generator name '${outputName}': must be a simple filename without path components`
687
+ );
688
+ return result;
689
+ }
690
+ const outputPath = join2(projectRoot, outputDir);
691
+ try {
692
+ await mkdir2(outputPath, { recursive: true });
693
+ } catch (err) {
694
+ result.errors.push(`Failed to create output directory: ${toErrorMessage(err)}`);
695
+ return result;
696
+ }
697
+ const generatorPath = join2(outputPath, `${safeOutputName}.ts`);
698
+ if (await pathExists2(generatorPath) && !overwrite) {
699
+ result.errors.push(
700
+ `Generator file already exists: ${generatorPath}. Use --overwrite to replace.`
701
+ );
702
+ return result;
703
+ }
704
+ try {
705
+ await writeFile2(generatorPath, atomicGenerator, "utf-8");
706
+ } catch (err) {
707
+ result.errors.push(`Failed to write generator: ${toErrorMessage(err)}`);
708
+ return result;
709
+ }
710
+ if (prompts.length === 0) {
711
+ result.warnings.push("No prompts found - generator will have empty params");
712
+ }
713
+ if (templates.length === 0) {
714
+ result.warnings.push("No templates found - template function is placeholder");
715
+ }
716
+ result.success = true;
717
+ result.generatorPath = generatorPath;
718
+ return result;
719
+ }
720
+
721
+ // src/importer/yeoman.ts
722
+ import { join as join3, basename as basename3 } from "path";
723
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir as readdir2 } from "fs/promises";
724
+
725
+ // src/importer/utils.ts
726
+ import { access as access3 } from "fs/promises";
727
+ async function pathExists3(path) {
728
+ try {
729
+ await access3(path);
730
+ return true;
731
+ } catch {
732
+ return false;
733
+ }
734
+ }
735
+ function escapeStringLiteral3(str) {
736
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
737
+ }
738
+ function sanitizeJsDocContent2(str) {
739
+ return str.replace(/\*\//g, "*\\/").replace(/\/\*/g, "/\\*");
740
+ }
741
+
742
+ // src/importer/yeoman.ts
743
+ var PROMPT_TYPE_MAP3 = {
744
+ input: "z.string()",
745
+ number: "z.number()",
746
+ confirm: "z.boolean()",
747
+ list: "z.string()",
748
+ rawlist: "z.string()",
749
+ expand: "z.string()",
750
+ checkbox: "z.array(z.string())",
751
+ password: "z.string()",
752
+ editor: "z.string()"
753
+ };
754
+ function parseYeomanPrompts(content) {
755
+ const prompts = [];
756
+ const warnings = [];
757
+ const promptArrayMatch = content.match(/this\.prompt\s*\(\s*\[([\s\S]*?)\]\s*\)/);
758
+ if (!promptArrayMatch) {
759
+ return { prompts, warnings };
760
+ }
761
+ const promptArray = promptArrayMatch[1];
762
+ const objectRegex = /\{[^{}]*\}/g;
763
+ let objMatch;
764
+ while ((objMatch = objectRegex.exec(promptArray)) !== null) {
765
+ const promptStr = objMatch[0];
766
+ const typeMatch = promptStr.match(/type\s*:\s*['"](\w+)['"]/);
767
+ const nameMatch = promptStr.match(/name\s*:\s*['"](\w+)['"]/);
768
+ const type = typeMatch?.[1];
769
+ const name = nameMatch?.[1];
770
+ if (type && name) {
771
+ const messageMatch = promptStr.match(/message\s*:\s*['"]([^'"]+)['"]/);
772
+ const defaultMatch = promptStr.match(/default\s*:\s*(?:'([^']*)'|"([^"]*)"|(\w+))/);
773
+ prompts.push({
774
+ type,
775
+ name,
776
+ message: messageMatch?.[1],
777
+ default: (defaultMatch?.[1] ?? defaultMatch?.[2] ?? defaultMatch?.[3])?.trim()
778
+ });
779
+ }
780
+ }
781
+ const simpleObjectCount = (promptArray.match(/\{[^{}]*\}/g) || []).length;
782
+ const nestedBraceCount = (promptArray.match(/\{/g) || []).length;
783
+ if (nestedBraceCount > simpleObjectCount) {
784
+ warnings.push(
785
+ "Some prompts with nested objects (when/filter/validate functions) may not have been fully parsed. Manual review recommended."
786
+ );
787
+ }
788
+ return { prompts, warnings };
789
+ }
790
+ function parseYeomanMethods(content) {
791
+ const methods = [];
792
+ const lifecycleMethods = [
793
+ "initializing",
794
+ "prompting",
795
+ "configuring",
796
+ "default",
797
+ "writing",
798
+ "conflicts",
799
+ "install",
800
+ "end"
801
+ ];
802
+ for (const method of lifecycleMethods) {
803
+ const patterns = [
804
+ new RegExp(`${method}\\s*\\(`),
805
+ // method()
806
+ new RegExp(`${method}\\s*:\\s*function`),
807
+ // method: function
808
+ new RegExp(`${method}\\s*:\\s*async`),
809
+ // method: async
810
+ new RegExp(`async\\s+${method}\\s*\\(`)
811
+ // async method()
812
+ ];
813
+ if (patterns.some((p) => p.test(content))) {
814
+ methods.push(method);
815
+ }
816
+ }
817
+ return methods;
818
+ }
819
+ function promptsToZod3(prompts) {
820
+ if (prompts.length === 0) {
821
+ return "z.object({})";
822
+ }
823
+ const fields = prompts.map((prompt) => {
824
+ let zodType = PROMPT_TYPE_MAP3[prompt.type] || "z.string()";
825
+ if (prompt.message) {
826
+ zodType += `.describe('${escapeStringLiteral3(prompt.message)}')`;
827
+ }
828
+ if (prompt.default !== void 0) {
829
+ if (Array.isArray(prompt.default)) {
830
+ const items = prompt.default.map(
831
+ (item) => typeof item === "string" ? `'${escapeStringLiteral3(item)}'` : String(item)
832
+ );
833
+ zodType += `.default([${items.join(", ")}])`;
834
+ } else if (typeof prompt.default === "string") {
835
+ zodType += `.default('${escapeStringLiteral3(prompt.default)}')`;
836
+ } else if (typeof prompt.default === "number") {
837
+ zodType += `.default(${prompt.default})`;
838
+ } else if (typeof prompt.default === "boolean") {
839
+ zodType += `.default(${prompt.default})`;
840
+ }
841
+ }
842
+ return ` ${prompt.name}: ${zodType}`;
843
+ });
844
+ return `z.object({
845
+ ${fields.join(",\n")}
846
+ })`;
847
+ }
848
+ async function scanYeomanGenerators(projectRoot) {
849
+ const generators = /* @__PURE__ */ new Map();
850
+ const generatorsDir = join3(projectRoot, "generators");
851
+ if (await pathExists3(generatorsDir)) {
852
+ try {
853
+ const entries = await readdir2(generatorsDir, { withFileTypes: true });
854
+ for (const entry of entries) {
855
+ if (entry.isDirectory()) {
856
+ const indexPath = join3(generatorsDir, entry.name, "index.js");
857
+ if (await pathExists3(indexPath)) {
858
+ generators.set(entry.name, indexPath);
859
+ }
860
+ }
861
+ }
862
+ } catch (err) {
863
+ console.warn(`Warning: Failed to scan generators directory: ${toErrorMessage(err)}`);
864
+ }
865
+ }
866
+ const nodeModulesDir = join3(projectRoot, "node_modules");
867
+ if (await pathExists3(nodeModulesDir)) {
868
+ try {
869
+ const entries = await readdir2(nodeModulesDir, { withFileTypes: true });
870
+ for (const entry of entries) {
871
+ if (entry.isDirectory() && entry.name.startsWith("generator-")) {
872
+ const genName = entry.name.replace("generator-", "");
873
+ const indexPath = join3(nodeModulesDir, entry.name, "generators", "app", "index.js");
874
+ if (await pathExists3(indexPath)) {
875
+ generators.set(genName, indexPath);
876
+ }
877
+ }
878
+ }
879
+ } catch (err) {
880
+ console.warn(`Warning: Failed to scan node_modules: ${toErrorMessage(err)}`);
881
+ }
882
+ }
883
+ return generators;
884
+ }
885
+ function generateAtomicGenerator3(parsed) {
886
+ const zodSchema = promptsToZod3(parsed.prompts);
887
+ const paramNames = parsed.prompts.map((p) => p.name);
888
+ const destructure = paramNames.length > 0 ? `{ ${paramNames.join(", ")} }` : "_";
889
+ const safeName = escapeStringLiteral3(parsed.name);
890
+ const safeDescription = escapeStringLiteral3(parsed.description || `Generator for ${parsed.name}`);
891
+ return `/**
892
+ * Atomic generator: ${safeName}
893
+ *
894
+ * Imported from Yeoman generator.
895
+ *
896
+ * @module generators/${safeName}
897
+ */
898
+
899
+ import { z } from 'atomism';
900
+
901
+ /**
902
+ * Generator parameters schema.
903
+ *
904
+ * Converted from Yeoman prompts:
905
+ ${parsed.prompts.map((p) => ` * - ${sanitizeJsDocContent2(p.name)}: ${sanitizeJsDocContent2(p.type)}${p.message ? ` (${sanitizeJsDocContent2(p.message)})` : ""}`).join("\n")}
906
+ */
907
+ export const paramsSchema = ${zodSchema};
908
+
909
+ export type Params = z.infer<typeof paramsSchema>;
910
+
911
+ /**
912
+ * Template function.
913
+ *
914
+ * Original Yeoman methods: ${parsed.methods.join(", ") || "none detected"}
915
+ */
916
+ export const template = (${destructure}: Params) => {
917
+ // TODO: Implement template based on Yeoman generator
918
+ // Original lifecycle methods: ${parsed.methods.join(", ") || "none"}
919
+ return \`
920
+ // Generated from Yeoman generator: ${safeName}
921
+ // Parameters: ${paramNames.join(", ") || "none"}
922
+ \`;
923
+ };
924
+
925
+ /**
926
+ * Generator metadata.
927
+ */
928
+ export const generator = {
929
+ name: '${safeName}',
930
+ description: '${safeDescription}',
931
+ params: paramsSchema,
932
+ template,
933
+ language: 'typescript' as const,
934
+ };
935
+
936
+ export default generator;
937
+ `;
938
+ }
939
+ async function importYeomanGenerator(options) {
940
+ const {
941
+ projectRoot,
942
+ generatorName,
943
+ outputDir = "generators",
944
+ overwrite = false
945
+ } = options;
946
+ const result = {
947
+ success: false,
948
+ mappings: [],
949
+ warnings: [],
950
+ errors: []
951
+ };
952
+ const safeGeneratorName = basename3(generatorName);
953
+ if (safeGeneratorName !== generatorName || generatorName.includes("..")) {
954
+ result.errors.push(
955
+ `Invalid generator name '${generatorName}': must be a simple name without path components`
956
+ );
957
+ return result;
958
+ }
959
+ const generators = await scanYeomanGenerators(projectRoot);
960
+ if (!generators.has(generatorName)) {
961
+ result.errors.push(
962
+ `Generator '${generatorName}' not found. Available: ${Array.from(generators.keys()).join(", ") || "none"}`
963
+ );
964
+ return result;
965
+ }
966
+ const generatorPath = generators.get(generatorName);
967
+ let content;
968
+ try {
969
+ content = await readFile3(generatorPath, "utf-8");
970
+ } catch (err) {
971
+ result.errors.push(`Failed to read generator: ${toErrorMessage(err)}`);
972
+ return result;
973
+ }
974
+ const { prompts, warnings: promptWarnings } = parseYeomanPrompts(content);
975
+ const methods = parseYeomanMethods(content);
976
+ result.warnings.push(...promptWarnings);
977
+ for (const prompt of prompts) {
978
+ result.mappings.push({
979
+ source: `prompt:${prompt.name}`,
980
+ target: `params.${prompt.name}`,
981
+ type: "prompt",
982
+ notes: `${prompt.type} \u2192 ${PROMPT_TYPE_MAP3[prompt.type] || "z.string()"}`
983
+ });
984
+ }
985
+ for (const method of methods) {
986
+ result.mappings.push({
987
+ source: `method:${method}`,
988
+ target: "template function",
989
+ type: "method",
990
+ notes: "Lifecycle method needs manual conversion"
991
+ });
992
+ }
993
+ const parsed = {
994
+ name: generatorName,
995
+ prompts,
996
+ methods
997
+ };
998
+ const atomicGenerator = generateAtomicGenerator3(parsed);
999
+ const outputPath = join3(projectRoot, outputDir);
1000
+ try {
1001
+ await mkdir3(outputPath, { recursive: true });
1002
+ } catch (err) {
1003
+ result.errors.push(`Failed to create output directory: ${toErrorMessage(err)}`);
1004
+ return result;
1005
+ }
1006
+ const outputFilePath = join3(outputPath, `${safeGeneratorName}.ts`);
1007
+ if (await pathExists3(outputFilePath) && !overwrite) {
1008
+ result.errors.push(
1009
+ `Generator file already exists: ${outputFilePath}. Use --overwrite to replace.`
1010
+ );
1011
+ return result;
1012
+ }
1013
+ try {
1014
+ await writeFile3(outputFilePath, atomicGenerator, "utf-8");
1015
+ } catch (err) {
1016
+ result.errors.push(`Failed to write generator: ${toErrorMessage(err)}`);
1017
+ return result;
1018
+ }
1019
+ if (prompts.length === 0) {
1020
+ result.warnings.push("No prompts found - generator will have empty params");
1021
+ }
1022
+ if (methods.length > 0) {
1023
+ result.warnings.push(
1024
+ `Yeoman lifecycle methods (${methods.join(", ")}) need manual conversion to template function`
1025
+ );
1026
+ }
1027
+ result.success = true;
1028
+ result.generatorPath = outputFilePath;
1029
+ return result;
1030
+ }
1031
+
1032
+ // src/importer/cookiecutter.ts
1033
+ import { join as join4, basename as basename4, relative } from "path";
1034
+ import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir as readdir3 } from "fs/promises";
1035
+ function parseCookiecutterJson(content) {
1036
+ const variables = [];
1037
+ let config;
1038
+ try {
1039
+ config = JSON.parse(content);
1040
+ } catch {
1041
+ return variables;
1042
+ }
1043
+ for (const [key, value] of Object.entries(config)) {
1044
+ if (key.startsWith("_")) continue;
1045
+ const variable = {
1046
+ name: key
1047
+ };
1048
+ if (Array.isArray(value)) {
1049
+ variable.choices = value.map(String);
1050
+ variable.default = value[0];
1051
+ } else {
1052
+ variable.default = value;
1053
+ }
1054
+ variables.push(variable);
1055
+ }
1056
+ return variables;
1057
+ }
1058
+ async function findTemplateFiles(dir, baseDir, warnings) {
1059
+ const files = [];
1060
+ try {
1061
+ const entries = await readdir3(dir, { withFileTypes: true });
1062
+ for (const entry of entries) {
1063
+ const fullPath = join4(dir, entry.name);
1064
+ if (entry.isDirectory()) {
1065
+ if (entry.name === "__pycache__" || entry.name === ".git") continue;
1066
+ const subFiles = await findTemplateFiles(fullPath, baseDir, warnings);
1067
+ files.push(...subFiles);
1068
+ } else {
1069
+ files.push(relative(baseDir, fullPath));
1070
+ }
1071
+ }
1072
+ } catch (err) {
1073
+ const message = `Failed to scan directory ${dir}: ${toErrorMessage(err)}`;
1074
+ if (warnings) {
1075
+ warnings.push(message);
1076
+ }
1077
+ }
1078
+ return files;
1079
+ }
1080
+ function variablesToZod(variables) {
1081
+ if (variables.length === 0) {
1082
+ return "z.object({})";
1083
+ }
1084
+ const fields = variables.map((variable) => {
1085
+ let zodType;
1086
+ if (variable.choices && variable.choices.length > 0) {
1087
+ const choices = variable.choices.map((c) => `'${escapeStringLiteral3(c)}'`);
1088
+ zodType = `z.enum([${choices.join(", ")}])`;
1089
+ } else if (typeof variable.default === "boolean") {
1090
+ zodType = "z.boolean()";
1091
+ } else if (typeof variable.default === "number") {
1092
+ zodType = "z.number()";
1093
+ } else {
1094
+ zodType = "z.string()";
1095
+ }
1096
+ if (variable.default !== void 0 && !variable.choices) {
1097
+ if (typeof variable.default === "string") {
1098
+ zodType += `.default('${escapeStringLiteral3(variable.default)}')`;
1099
+ } else if (typeof variable.default === "number" || typeof variable.default === "boolean") {
1100
+ zodType += `.default(${variable.default})`;
1101
+ }
1102
+ }
1103
+ return ` ${variable.name}: ${zodType}`;
1104
+ });
1105
+ return `z.object({
1106
+ ${fields.join(",\n")}
1107
+ })`;
1108
+ }
1109
+ function generateAtomicGenerator4(parsed) {
1110
+ const zodSchema = variablesToZod(parsed.variables);
1111
+ const paramNames = parsed.variables.map((v) => v.name);
1112
+ const destructure = paramNames.length > 0 ? `{ ${paramNames.join(", ")} }` : "_";
1113
+ const safeName = escapeStringLiteral3(parsed.name);
1114
+ const safeDescription = escapeStringLiteral3(parsed.description || `Generator for ${parsed.name}`);
1115
+ return `/**
1116
+ * Atomic generator: ${safeName}
1117
+ *
1118
+ * Imported from Cookiecutter template.
1119
+ *
1120
+ * @module generators/${safeName}
1121
+ */
1122
+
1123
+ import { z } from 'atomism';
1124
+
1125
+ /**
1126
+ * Generator parameters schema.
1127
+ *
1128
+ * Converted from cookiecutter.json:
1129
+ ${parsed.variables.map((v) => ` * - ${sanitizeJsDocContent2(v.name)}: ${v.choices ? `enum(${v.choices.slice(0, 3).join(", ")}${v.choices.length > 3 ? "..." : ""})` : typeof v.default}`).join("\n")}
1130
+ */
1131
+ export const paramsSchema = ${zodSchema};
1132
+
1133
+ export type Params = z.infer<typeof paramsSchema>;
1134
+
1135
+ /**
1136
+ * Template function.
1137
+ *
1138
+ * Template files: ${parsed.templateFiles.length}
1139
+ ${parsed.templateFiles.slice(0, 5).map((f) => ` * - ${sanitizeJsDocContent2(f)}`).join("\n")}${parsed.templateFiles.length > 5 ? "\n * - ..." : ""}
1140
+ */
1141
+ export const template = (${destructure}: Params) => {
1142
+ // TODO: Implement template based on Cookiecutter files
1143
+ // Original template files: ${parsed.templateFiles.length}
1144
+ return \`
1145
+ // Generated from Cookiecutter template: ${safeName}
1146
+ // Parameters: ${paramNames.join(", ") || "none"}
1147
+ \`;
1148
+ };
1149
+
1150
+ /**
1151
+ * Generator metadata.
1152
+ */
1153
+ export const generator = {
1154
+ name: '${safeName}',
1155
+ description: '${safeDescription}',
1156
+ params: paramsSchema,
1157
+ template,
1158
+ language: 'typescript' as const,
1159
+ };
1160
+
1161
+ export default generator;
1162
+ `;
1163
+ }
1164
+ async function importCookiecutterTemplate(options) {
1165
+ const {
1166
+ projectRoot,
1167
+ templatePath,
1168
+ outputDir = "generators",
1169
+ overwrite = false
1170
+ } = options;
1171
+ const result = {
1172
+ success: false,
1173
+ mappings: [],
1174
+ warnings: [],
1175
+ errors: []
1176
+ };
1177
+ const fullTemplatePath = join4(projectRoot, templatePath);
1178
+ const cookiecutterJsonPath = join4(fullTemplatePath, "cookiecutter.json");
1179
+ if (!await pathExists3(cookiecutterJsonPath)) {
1180
+ result.errors.push(`No cookiecutter.json found at ${templatePath}`);
1181
+ return result;
1182
+ }
1183
+ let cookiecutterContent;
1184
+ try {
1185
+ cookiecutterContent = await readFile4(cookiecutterJsonPath, "utf-8");
1186
+ } catch (err) {
1187
+ result.errors.push(`Failed to read cookiecutter.json: ${toErrorMessage(err)}`);
1188
+ return result;
1189
+ }
1190
+ const variables = parseCookiecutterJson(cookiecutterContent);
1191
+ for (const variable of variables) {
1192
+ result.mappings.push({
1193
+ source: `variable:${variable.name}`,
1194
+ target: `params.${variable.name}`,
1195
+ type: "variable",
1196
+ notes: variable.choices ? `enum with ${variable.choices.length} choices` : variable.default !== void 0 ? `default: ${variable.default}` : "no default"
1197
+ });
1198
+ }
1199
+ const templateFiles = [];
1200
+ try {
1201
+ const entries = await readdir3(fullTemplatePath, { withFileTypes: true });
1202
+ for (const entry of entries) {
1203
+ if (entry.isDirectory() && entry.name.includes("{{")) {
1204
+ const templateDir = join4(fullTemplatePath, entry.name);
1205
+ const files = await findTemplateFiles(templateDir, templateDir, result.warnings);
1206
+ templateFiles.push(...files);
1207
+ }
1208
+ }
1209
+ } catch (err) {
1210
+ result.warnings.push(`Could not scan template files: ${toErrorMessage(err)}`);
1211
+ }
1212
+ for (const file of templateFiles) {
1213
+ result.mappings.push({
1214
+ source: `template:${file}`,
1215
+ target: "template output",
1216
+ type: "template"
1217
+ });
1218
+ }
1219
+ const templateName = basename4(templatePath).replace(/^cookiecutter-/, "");
1220
+ const safeTemplateName = basename4(templateName);
1221
+ if (safeTemplateName !== templateName || templateName.includes("..")) {
1222
+ result.errors.push(
1223
+ `Invalid template name '${templateName}': must be a simple name without path components`
1224
+ );
1225
+ return result;
1226
+ }
1227
+ const parsed = {
1228
+ name: templateName,
1229
+ variables,
1230
+ templateFiles
1231
+ };
1232
+ const atomicGenerator = generateAtomicGenerator4(parsed);
1233
+ const outputPath = join4(projectRoot, outputDir);
1234
+ try {
1235
+ await mkdir4(outputPath, { recursive: true });
1236
+ } catch (err) {
1237
+ result.errors.push(`Failed to create output directory: ${toErrorMessage(err)}`);
1238
+ return result;
1239
+ }
1240
+ const outputFilePath = join4(outputPath, `${safeTemplateName}.ts`);
1241
+ if (await pathExists3(outputFilePath) && !overwrite) {
1242
+ result.errors.push(
1243
+ `Generator file already exists: ${outputFilePath}. Use --overwrite to replace.`
1244
+ );
1245
+ return result;
1246
+ }
1247
+ try {
1248
+ await writeFile4(outputFilePath, atomicGenerator, "utf-8");
1249
+ } catch (err) {
1250
+ result.errors.push(`Failed to write generator: ${toErrorMessage(err)}`);
1251
+ return result;
1252
+ }
1253
+ if (variables.length === 0) {
1254
+ result.warnings.push("No variables found in cookiecutter.json - generator will have empty params");
1255
+ }
1256
+ if (templateFiles.length > 0) {
1257
+ result.warnings.push(
1258
+ `Jinja2 templates (${templateFiles.length} files) need manual conversion to template literals`
1259
+ );
1260
+ }
1261
+ result.success = true;
1262
+ result.generatorPath = outputFilePath;
1263
+ return result;
1264
+ }
1265
+
1266
+ // src/importer/script.ts
1267
+ import { join as join5, basename as basename5, extname } from "path";
1268
+ import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
1269
+ function detectLanguage(filename) {
1270
+ const ext = extname(filename).toLowerCase();
1271
+ const languageMap = {
1272
+ ".sh": "shell",
1273
+ ".bash": "shell",
1274
+ ".zsh": "shell",
1275
+ ".js": "javascript",
1276
+ ".mjs": "javascript",
1277
+ ".cjs": "javascript",
1278
+ ".ts": "typescript",
1279
+ ".tsx": "typescript",
1280
+ ".py": "python"
1281
+ };
1282
+ return languageMap[ext] || "unknown";
1283
+ }
1284
+ function extractShellVariables(content) {
1285
+ const variables = [];
1286
+ const seen = /* @__PURE__ */ new Set();
1287
+ const patterns = [
1288
+ /\$\{(\w+)\}/g,
1289
+ // ${VAR}
1290
+ /\$(\w+)/g,
1291
+ // $VAR (but not $$, $?, etc.)
1292
+ /read\s+-p\s+"[^"]*"\s+(\w+)/g
1293
+ // read -p "prompt" VAR
1294
+ ];
1295
+ for (const pattern of patterns) {
1296
+ let match;
1297
+ while ((match = pattern.exec(content)) !== null) {
1298
+ const name = match[1];
1299
+ if (name && !seen.has(name) && !/^\d+$/.test(name)) {
1300
+ if (!["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "?", "!", "#", "@", "*", "-", "$"].includes(name)) {
1301
+ seen.add(name);
1302
+ variables.push({
1303
+ name,
1304
+ source: "shell variable",
1305
+ type: "string"
1306
+ });
1307
+ }
1308
+ }
1309
+ }
1310
+ }
1311
+ return variables;
1312
+ }
1313
+ function extractJsVariables(content) {
1314
+ const variables = [];
1315
+ const seen = /* @__PURE__ */ new Set();
1316
+ const argvPattern = /process\.argv\[(\d+)\]/g;
1317
+ let match;
1318
+ while ((match = argvPattern.exec(content)) !== null) {
1319
+ const index = parseInt(match[1], 10);
1320
+ if (index >= 2) {
1321
+ const name = `arg${index - 1}`;
1322
+ if (!seen.has(name)) {
1323
+ seen.add(name);
1324
+ variables.push({
1325
+ name,
1326
+ source: `process.argv[${index}]`,
1327
+ type: "string"
1328
+ });
1329
+ }
1330
+ }
1331
+ }
1332
+ const promptPattern = /(?:name|key)\s*:\s*['"](\w+)['"]/g;
1333
+ while ((match = promptPattern.exec(content)) !== null) {
1334
+ const name = match[1];
1335
+ if (name && !seen.has(name)) {
1336
+ seen.add(name);
1337
+ variables.push({
1338
+ name,
1339
+ source: "prompt definition",
1340
+ type: "string"
1341
+ });
1342
+ }
1343
+ }
1344
+ const envPattern = /process\.env\.(\w+)/g;
1345
+ while ((match = envPattern.exec(content)) !== null) {
1346
+ const name = match[1];
1347
+ if (name && !seen.has(name)) {
1348
+ seen.add(name);
1349
+ variables.push({
1350
+ name,
1351
+ source: "environment variable",
1352
+ type: "string"
1353
+ });
1354
+ }
1355
+ }
1356
+ return variables;
1357
+ }
1358
+ function extractPythonVariables(content) {
1359
+ const variables = [];
1360
+ const seen = /* @__PURE__ */ new Set();
1361
+ const argparsePattern = /add_argument\s*\(\s*['"]--?(\w+)['"]/g;
1362
+ let match;
1363
+ while ((match = argparsePattern.exec(content)) !== null) {
1364
+ const name = match[1];
1365
+ if (name && !seen.has(name)) {
1366
+ seen.add(name);
1367
+ variables.push({
1368
+ name,
1369
+ source: "argparse argument",
1370
+ type: "string"
1371
+ });
1372
+ }
1373
+ }
1374
+ const clickPattern = /@click\.option\s*\(\s*['"]--(\w+)['"]/g;
1375
+ while ((match = clickPattern.exec(content)) !== null) {
1376
+ const name = match[1];
1377
+ if (name && !seen.has(name)) {
1378
+ seen.add(name);
1379
+ variables.push({
1380
+ name,
1381
+ source: "click option",
1382
+ type: "string"
1383
+ });
1384
+ }
1385
+ }
1386
+ const inputPattern = /(\w+)\s*=\s*input\s*\(/g;
1387
+ while ((match = inputPattern.exec(content)) !== null) {
1388
+ const name = match[1];
1389
+ if (name && !seen.has(name)) {
1390
+ seen.add(name);
1391
+ variables.push({
1392
+ name,
1393
+ source: "input() call",
1394
+ type: "string"
1395
+ });
1396
+ }
1397
+ }
1398
+ return variables;
1399
+ }
1400
+ function extractVariables(content, language) {
1401
+ switch (language) {
1402
+ case "shell":
1403
+ return extractShellVariables(content);
1404
+ case "javascript":
1405
+ case "typescript":
1406
+ return extractJsVariables(content);
1407
+ case "python":
1408
+ return extractPythonVariables(content);
1409
+ default:
1410
+ return [];
1411
+ }
1412
+ }
1413
+ function variablesToZod2(variables) {
1414
+ if (variables.length === 0) {
1415
+ return "z.object({})";
1416
+ }
1417
+ const fields = variables.map((variable) => {
1418
+ let zodType;
1419
+ switch (variable.type) {
1420
+ case "boolean":
1421
+ zodType = "z.boolean()";
1422
+ break;
1423
+ case "number":
1424
+ zodType = "z.number()";
1425
+ break;
1426
+ default:
1427
+ zodType = "z.string()";
1428
+ }
1429
+ zodType += `.describe('${escapeStringLiteral3(variable.source)}')`;
1430
+ return ` ${variable.name}: ${zodType}`;
1431
+ });
1432
+ return `z.object({
1433
+ ${fields.join(",\n")}
1434
+ })`;
1435
+ }
1436
+ function generateAtomicGenerator5(parsed) {
1437
+ const zodSchema = variablesToZod2(parsed.variables);
1438
+ const paramNames = parsed.variables.map((v) => v.name);
1439
+ const destructure = paramNames.length > 0 ? `{ ${paramNames.join(", ")} }` : "_";
1440
+ const safeName = escapeStringLiteral3(parsed.name);
1441
+ return `/**
1442
+ * Atomic generator: ${safeName}
1443
+ *
1444
+ * Imported from custom ${parsed.language} script.
1445
+ *
1446
+ * @module generators/${safeName}
1447
+ */
1448
+
1449
+ import { z } from 'atomism';
1450
+
1451
+ /**
1452
+ * Generator parameters schema.
1453
+ *
1454
+ * Detected variables from script:
1455
+ ${parsed.variables.map((v) => ` * - ${sanitizeJsDocContent2(v.name)}: ${sanitizeJsDocContent2(v.source)}`).join("\n") || " * (none detected)"}
1456
+ */
1457
+ export const paramsSchema = ${zodSchema};
1458
+
1459
+ export type Params = z.infer<typeof paramsSchema>;
1460
+
1461
+ /**
1462
+ * Template function.
1463
+ *
1464
+ * Original script language: ${parsed.language}
1465
+ * Manual conversion required for script logic.
1466
+ */
1467
+ export const template = (${destructure}: Params) => {
1468
+ // TODO: Implement template based on original script
1469
+ // Original script: ${safeName}
1470
+ // Language: ${parsed.language}
1471
+ return \`
1472
+ // Generated from custom script: ${safeName}
1473
+ // Parameters: ${paramNames.join(", ") || "none"}
1474
+ \`;
1475
+ };
1476
+
1477
+ /**
1478
+ * Generator metadata.
1479
+ */
1480
+ export const generator = {
1481
+ name: '${safeName}',
1482
+ description: 'Generator imported from custom ${parsed.language} script',
1483
+ params: paramsSchema,
1484
+ template,
1485
+ language: 'typescript' as const,
1486
+ };
1487
+
1488
+ export default generator;
1489
+ `;
1490
+ }
1491
+ async function importScript(options) {
1492
+ const {
1493
+ projectRoot,
1494
+ scriptPath,
1495
+ outputDir = "generators",
1496
+ overwrite = false
1497
+ } = options;
1498
+ const result = {
1499
+ success: false,
1500
+ mappings: [],
1501
+ warnings: [],
1502
+ errors: []
1503
+ };
1504
+ const scriptName = basename5(scriptPath, extname(scriptPath));
1505
+ if (!/^[\w-]+$/.test(scriptName)) {
1506
+ result.errors.push(
1507
+ `Invalid script name '${scriptName}': must contain only alphanumeric characters, dashes, and underscores`
1508
+ );
1509
+ return result;
1510
+ }
1511
+ const fullScriptPath = join5(projectRoot, scriptPath);
1512
+ if (!await pathExists3(fullScriptPath)) {
1513
+ result.errors.push(`Script not found: ${scriptPath}`);
1514
+ return result;
1515
+ }
1516
+ let content;
1517
+ try {
1518
+ content = await readFile5(fullScriptPath, "utf-8");
1519
+ } catch (err) {
1520
+ result.errors.push(`Failed to read script: ${toErrorMessage(err)}`);
1521
+ return result;
1522
+ }
1523
+ const language = detectLanguage(scriptPath);
1524
+ if (language === "unknown") {
1525
+ result.warnings.push(`Unknown script language for ${scriptPath}, treating as generic`);
1526
+ }
1527
+ const variables = extractVariables(content, language);
1528
+ for (const variable of variables) {
1529
+ result.mappings.push({
1530
+ source: `${variable.source}:${variable.name}`,
1531
+ target: `params.${variable.name}`,
1532
+ type: "variable",
1533
+ notes: `Detected from ${language} script`
1534
+ });
1535
+ }
1536
+ const parsed = {
1537
+ name: scriptName,
1538
+ language,
1539
+ variables,
1540
+ content
1541
+ };
1542
+ const atomicGenerator = generateAtomicGenerator5(parsed);
1543
+ const outputPath = join5(projectRoot, outputDir);
1544
+ try {
1545
+ await mkdir5(outputPath, { recursive: true });
1546
+ } catch (err) {
1547
+ result.errors.push(`Failed to create output directory: ${toErrorMessage(err)}`);
1548
+ return result;
1549
+ }
1550
+ const outputFilePath = join5(outputPath, `${scriptName}.ts`);
1551
+ if (await pathExists3(outputFilePath) && !overwrite) {
1552
+ result.errors.push(
1553
+ `Generator file already exists: ${outputFilePath}. Use --overwrite to replace.`
1554
+ );
1555
+ return result;
1556
+ }
1557
+ try {
1558
+ await writeFile5(outputFilePath, atomicGenerator, "utf-8");
1559
+ } catch (err) {
1560
+ result.errors.push(`Failed to write generator: ${toErrorMessage(err)}`);
1561
+ return result;
1562
+ }
1563
+ if (variables.length === 0) {
1564
+ result.warnings.push("No variables detected - generator will have empty params. Review the original script to add parameters.");
1565
+ }
1566
+ result.warnings.push(
1567
+ `Script logic needs manual conversion to template function. Original ${language} script preserved for reference.`
1568
+ );
1569
+ result.success = true;
1570
+ result.generatorPath = outputFilePath;
1571
+ return result;
1572
+ }
1573
+
1574
+ // src/commands/import-generator.ts
1575
+ async function importGeneratorCommand(options) {
1576
+ const projectRoot = process.cwd();
1577
+ if (options.plop) {
1578
+ await importFromPlop(projectRoot, options.plop, options);
1579
+ } else if (options.hygen) {
1580
+ await importFromHygen(projectRoot, options.hygen, options);
1581
+ } else if (options.yeoman) {
1582
+ await importFromYeoman(projectRoot, options.yeoman, options);
1583
+ } else if (options.cookiecutter) {
1584
+ await importFromCookiecutter(projectRoot, options.cookiecutter, options);
1585
+ } else if (options.script) {
1586
+ await importFromScript(projectRoot, options.script, options);
1587
+ } else {
1588
+ if (options.json) {
1589
+ console.log(
1590
+ JSON.stringify({
1591
+ success: false,
1592
+ error: "Must specify a generator to import: --plop, --hygen, --yeoman, --cookiecutter, or --script"
1593
+ })
1594
+ );
1595
+ } else {
1596
+ console.error("Error: Must specify a generator to import");
1597
+ console.error("");
1598
+ console.error("Options:");
1599
+ console.error(" --plop <name> Import Plop generator");
1600
+ console.error(" --hygen <name> Import Hygen generator");
1601
+ console.error(" --yeoman <name> Import Yeoman generator");
1602
+ console.error(" --cookiecutter <path> Import Cookiecutter template");
1603
+ console.error(" --script <path> Import custom script");
1604
+ }
1605
+ process.exit(1);
1606
+ }
1607
+ }
1608
+ async function importFromPlop(projectRoot, generatorName, options) {
1609
+ const result = await importPlopGenerator({
1610
+ projectRoot,
1611
+ generatorName,
1612
+ outputDir: options.output,
1613
+ overwrite: options.overwrite
1614
+ });
1615
+ if (options.json) {
1616
+ console.log(JSON.stringify(result));
1617
+ if (!result.success) {
1618
+ process.exit(1);
1619
+ }
1620
+ return;
1621
+ }
1622
+ if (!result.success) {
1623
+ console.error("Import failed:");
1624
+ for (const error of result.errors) {
1625
+ console.error(` \u2717 ${error}`);
1626
+ }
1627
+ process.exit(1);
1628
+ }
1629
+ console.log(`\u2713 Imported Plop generator '${generatorName}'`);
1630
+ console.log("");
1631
+ console.log(`Output: ${result.generatorPath}`);
1632
+ console.log("");
1633
+ if (result.mappings.length > 0) {
1634
+ console.log("Mappings:");
1635
+ for (const mapping of result.mappings) {
1636
+ const icon = mapping.type === "prompt" ? "\u{1F4DD}" : mapping.type === "action" ? "\u26A1" : "\u{1F4C4}";
1637
+ console.log(` ${icon} ${mapping.source} \u2192 ${mapping.target}`);
1638
+ if (mapping.notes) {
1639
+ console.log(` ${mapping.notes}`);
1640
+ }
1641
+ }
1642
+ console.log("");
1643
+ }
1644
+ if (result.warnings.length > 0) {
1645
+ console.log("Warnings (manual review needed):");
1646
+ for (const warning of result.warnings) {
1647
+ console.log(` \u26A0 ${warning}`);
1648
+ }
1649
+ console.log("");
1650
+ }
1651
+ console.log("Next steps:");
1652
+ console.log(` 1. Review the generated file: ${result.generatorPath}`);
1653
+ console.log(" 2. Update the template function with actual logic");
1654
+ console.log(" 3. Test the generator");
1655
+ }
1656
+ async function importFromHygen(projectRoot, generatorSpec, options) {
1657
+ const parts = generatorSpec.split("/");
1658
+ if (parts.length > 2 || parts.some((p) => !p.trim())) {
1659
+ const errorMsg = `Invalid format '${generatorSpec}'. Expected 'generator' or 'generator/action'.`;
1660
+ if (options.json) {
1661
+ console.log(JSON.stringify({ success: false, error: errorMsg }));
1662
+ } else {
1663
+ console.error(`Error: ${errorMsg}`);
1664
+ }
1665
+ process.exit(1);
1666
+ }
1667
+ const generatorName = parts[0];
1668
+ const actionName = parts[1];
1669
+ const result = await importHygenGenerator({
1670
+ projectRoot,
1671
+ generatorName,
1672
+ actionName,
1673
+ outputDir: options.output,
1674
+ overwrite: options.overwrite
1675
+ });
1676
+ if (options.json) {
1677
+ console.log(JSON.stringify(result));
1678
+ if (!result.success) {
1679
+ process.exit(1);
1680
+ }
1681
+ return;
1682
+ }
1683
+ if (!result.success) {
1684
+ console.error("Import failed:");
1685
+ for (const error of result.errors) {
1686
+ console.error(` \u2717 ${error}`);
1687
+ }
1688
+ process.exit(1);
1689
+ }
1690
+ console.log(`\u2713 Imported Hygen generator '${generatorSpec}'`);
1691
+ console.log("");
1692
+ console.log(`Output: ${result.generatorPath}`);
1693
+ console.log("");
1694
+ if (result.mappings.length > 0) {
1695
+ console.log("Mappings:");
1696
+ for (const mapping of result.mappings) {
1697
+ const icon = mapping.type === "prompt" ? "\u{1F4DD}" : mapping.type === "template" ? "\u{1F4C4}" : "\u2699\uFE0F";
1698
+ console.log(` ${icon} ${mapping.source} \u2192 ${mapping.target}`);
1699
+ if (mapping.notes) {
1700
+ console.log(` ${mapping.notes}`);
1701
+ }
1702
+ }
1703
+ console.log("");
1704
+ }
1705
+ if (result.warnings.length > 0) {
1706
+ console.log("Warnings (manual review needed):");
1707
+ for (const warning of result.warnings) {
1708
+ console.log(` \u26A0 ${warning}`);
1709
+ }
1710
+ console.log("");
1711
+ }
1712
+ console.log("Next steps:");
1713
+ console.log(` 1. Review the generated file: ${result.generatorPath}`);
1714
+ console.log(" 2. Update the template function with actual logic");
1715
+ console.log(" 3. Test the generator");
1716
+ }
1717
+ async function importFromYeoman(projectRoot, generatorName, options) {
1718
+ const result = await importYeomanGenerator({
1719
+ projectRoot,
1720
+ generatorName,
1721
+ outputDir: options.output,
1722
+ overwrite: options.overwrite
1723
+ });
1724
+ if (options.json) {
1725
+ console.log(JSON.stringify(result));
1726
+ if (!result.success) {
1727
+ process.exit(1);
1728
+ }
1729
+ return;
1730
+ }
1731
+ if (!result.success) {
1732
+ console.error("Import failed:");
1733
+ for (const error of result.errors) {
1734
+ console.error(` \u2717 ${error}`);
1735
+ }
1736
+ process.exit(1);
1737
+ }
1738
+ console.log(`\u2713 Imported Yeoman generator '${generatorName}'`);
1739
+ console.log("");
1740
+ console.log(`Output: ${result.generatorPath}`);
1741
+ console.log("");
1742
+ if (result.mappings.length > 0) {
1743
+ console.log("Mappings:");
1744
+ for (const mapping of result.mappings) {
1745
+ const icon = mapping.type === "prompt" ? "\u{1F4DD}" : mapping.type === "method" ? "\u2699\uFE0F" : "\u{1F4C4}";
1746
+ console.log(` ${icon} ${mapping.source} \u2192 ${mapping.target}`);
1747
+ if (mapping.notes) {
1748
+ console.log(` ${mapping.notes}`);
1749
+ }
1750
+ }
1751
+ console.log("");
1752
+ }
1753
+ if (result.warnings.length > 0) {
1754
+ console.log("Warnings (manual review needed):");
1755
+ for (const warning of result.warnings) {
1756
+ console.log(` \u26A0 ${warning}`);
1757
+ }
1758
+ console.log("");
1759
+ }
1760
+ console.log("Next steps:");
1761
+ console.log(` 1. Review the generated file: ${result.generatorPath}`);
1762
+ console.log(" 2. Update the template function with actual logic");
1763
+ console.log(" 3. Test the generator");
1764
+ }
1765
+ async function importFromCookiecutter(projectRoot, templatePath, options) {
1766
+ const result = await importCookiecutterTemplate({
1767
+ projectRoot,
1768
+ templatePath,
1769
+ outputDir: options.output,
1770
+ overwrite: options.overwrite
1771
+ });
1772
+ if (options.json) {
1773
+ console.log(JSON.stringify(result));
1774
+ if (!result.success) {
1775
+ process.exit(1);
1776
+ }
1777
+ return;
1778
+ }
1779
+ if (!result.success) {
1780
+ console.error("Import failed:");
1781
+ for (const error of result.errors) {
1782
+ console.error(` \u2717 ${error}`);
1783
+ }
1784
+ process.exit(1);
1785
+ }
1786
+ console.log(`\u2713 Imported Cookiecutter template '${templatePath}'`);
1787
+ console.log("");
1788
+ console.log(`Output: ${result.generatorPath}`);
1789
+ console.log("");
1790
+ if (result.mappings.length > 0) {
1791
+ console.log("Mappings:");
1792
+ for (const mapping of result.mappings) {
1793
+ const icon = mapping.type === "variable" ? "\u{1F4DD}" : "\u{1F4C4}";
1794
+ console.log(` ${icon} ${mapping.source} \u2192 ${mapping.target}`);
1795
+ if (mapping.notes) {
1796
+ console.log(` ${mapping.notes}`);
1797
+ }
1798
+ }
1799
+ console.log("");
1800
+ }
1801
+ if (result.warnings.length > 0) {
1802
+ console.log("Warnings (manual review needed):");
1803
+ for (const warning of result.warnings) {
1804
+ console.log(` \u26A0 ${warning}`);
1805
+ }
1806
+ console.log("");
1807
+ }
1808
+ console.log("Next steps:");
1809
+ console.log(` 1. Review the generated file: ${result.generatorPath}`);
1810
+ console.log(" 2. Convert Jinja2 templates to template literals");
1811
+ console.log(" 3. Test the generator");
1812
+ }
1813
+ async function importFromScript(projectRoot, scriptPath, options) {
1814
+ const result = await importScript({
1815
+ projectRoot,
1816
+ scriptPath,
1817
+ outputDir: options.output,
1818
+ overwrite: options.overwrite
1819
+ });
1820
+ if (options.json) {
1821
+ console.log(JSON.stringify(result));
1822
+ if (!result.success) {
1823
+ process.exit(1);
1824
+ }
1825
+ return;
1826
+ }
1827
+ if (!result.success) {
1828
+ console.error("Import failed:");
1829
+ for (const error of result.errors) {
1830
+ console.error(` \u2717 ${error}`);
1831
+ }
1832
+ process.exit(1);
1833
+ }
1834
+ console.log(`\u2713 Imported custom script '${scriptPath}'`);
1835
+ console.log("");
1836
+ console.log(`Output: ${result.generatorPath}`);
1837
+ console.log("");
1838
+ if (result.mappings.length > 0) {
1839
+ console.log("Mappings:");
1840
+ for (const mapping of result.mappings) {
1841
+ const icon = mapping.type === "command" ? "\u2699\uFE0F" : mapping.type === "file" ? "\u{1F4C4}" : "\u{1F4DD}";
1842
+ console.log(` ${icon} ${mapping.source} \u2192 ${mapping.target}`);
1843
+ if (mapping.notes) {
1844
+ console.log(` ${mapping.notes}`);
1845
+ }
1846
+ }
1847
+ console.log("");
1848
+ }
1849
+ if (result.warnings.length > 0) {
1850
+ console.log("Warnings (manual review needed):");
1851
+ for (const warning of result.warnings) {
1852
+ console.log(` \u26A0 ${warning}`);
1853
+ }
1854
+ console.log("");
1855
+ }
1856
+ console.log("Next steps:");
1857
+ console.log(` 1. Review the generated file: ${result.generatorPath}`);
1858
+ console.log(" 2. Convert script logic to template function");
1859
+ console.log(" 3. Test the generator");
1860
+ }
1861
+ export {
1862
+ importGeneratorCommand
1863
+ };
1864
+ //# sourceMappingURL=import-generator-4CKRBMTE.js.map