@synergenius/flow-weaver 0.17.1 → 0.17.2

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 (121) hide show
  1. package/dist/api/index.d.ts +4 -1
  2. package/dist/api/index.js +4 -1
  3. package/dist/api/templates.js +2 -2
  4. package/dist/api/validate.d.ts +2 -2
  5. package/dist/api/validate.js +6 -6
  6. package/dist/api/validation-registry.d.ts +10 -0
  7. package/dist/api/validation-registry.js +10 -0
  8. package/dist/ast/types.d.ts +91 -4
  9. package/dist/built-in-nodes/invoke-workflow.d.ts +1 -1
  10. package/dist/built-in-nodes/invoke-workflow.js +1 -1
  11. package/dist/chevrotain-parser/connect-parser.js +25 -7
  12. package/dist/cli/commands/compile.d.ts +5 -9
  13. package/dist/cli/commands/compile.js +21 -14
  14. package/dist/cli/commands/dev.d.ts +2 -13
  15. package/dist/cli/commands/dev.js +10 -204
  16. package/dist/cli/commands/doctor.js +6 -3
  17. package/dist/cli/commands/export.d.ts +8 -17
  18. package/dist/cli/commands/export.js +8 -17
  19. package/dist/cli/commands/init-personas.d.ts +12 -3
  20. package/dist/cli/commands/init-personas.js +27 -4
  21. package/dist/cli/commands/init.d.ts +2 -2
  22. package/dist/cli/commands/init.js +5 -11
  23. package/dist/cli/flow-weaver.mjs +61463 -60910
  24. package/dist/cli/index.d.ts +1 -0
  25. package/dist/cli/index.js +9 -7
  26. package/dist/cli/templates/index.d.ts +20 -1
  27. package/dist/cli/templates/index.js +66 -15
  28. package/dist/cli/templates/nodes/human-approval.js +2 -3
  29. package/dist/cli/templates/nodes/rag-retriever.js +1 -1
  30. package/dist/constants.d.ts +7 -0
  31. package/dist/constants.js +13 -3
  32. package/dist/context/index.js +13 -3
  33. package/dist/deployment/config/loader.js +2 -1
  34. package/dist/deployment/core/adapters.d.ts +1 -25
  35. package/dist/deployment/core/adapters.js +0 -95
  36. package/dist/deployment/core/formatters.d.ts +0 -15
  37. package/dist/deployment/core/formatters.js +0 -24
  38. package/dist/deployment/index.d.ts +7 -5
  39. package/dist/deployment/index.js +8 -5
  40. package/dist/deployment/types.d.ts +2 -45
  41. package/dist/diagram/html-viewer.js +65 -32
  42. package/dist/diagram/renderer.js +9 -6
  43. package/dist/diagram/theme.js +4 -0
  44. package/dist/diagram/types.d.ts +2 -0
  45. package/dist/doc-metadata/extractors/annotations.js +5 -5
  46. package/dist/doc-metadata/extractors/cli-commands.js +1 -1
  47. package/dist/doc-metadata/extractors/mcp-tools.js +1 -2
  48. package/dist/docs/index.d.ts +28 -1
  49. package/dist/docs/index.js +95 -28
  50. package/dist/export/index.d.ts +2 -3
  51. package/dist/{deployment/targets/cicd-base.d.ts → extensions/cicd/base-target.d.ts} +35 -36
  52. package/dist/{deployment/targets/cicd-base.js → extensions/cicd/base-target.js} +97 -57
  53. package/dist/{validation/cicd-detection.d.ts → extensions/cicd/detection.d.ts} +2 -2
  54. package/dist/{validation/cicd-detection.js → extensions/cicd/detection.js} +13 -1
  55. package/dist/extensions/cicd/docs/cicd.md +395 -0
  56. package/dist/extensions/cicd/index.d.ts +10 -0
  57. package/dist/extensions/cicd/index.js +10 -0
  58. package/dist/extensions/cicd/register.d.ts +11 -0
  59. package/dist/extensions/cicd/register.js +62 -0
  60. package/dist/extensions/cicd/rules.d.ts +30 -0
  61. package/dist/{validation/cicd-rules.js → extensions/cicd/rules.js} +60 -56
  62. package/dist/extensions/cicd/tag-handler.d.ts +14 -0
  63. package/dist/extensions/cicd/tag-handler.js +488 -0
  64. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-docker.d.ts +1 -1
  65. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-matrix.d.ts +1 -1
  66. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-multi-env.d.ts +1 -1
  67. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-test-deploy.d.ts +1 -1
  68. package/dist/extensions/index.d.ts +12 -0
  69. package/dist/extensions/index.js +12 -0
  70. package/dist/extensions/inngest/dev-mode.d.ts +9 -0
  71. package/dist/extensions/inngest/dev-mode.js +213 -0
  72. package/dist/{generator/inngest.d.ts → extensions/inngest/generator.d.ts} +2 -2
  73. package/dist/{generator/inngest.js → extensions/inngest/generator.js} +4 -4
  74. package/dist/extensions/inngest/index.d.ts +2 -0
  75. package/dist/extensions/inngest/index.js +2 -0
  76. package/dist/extensions/inngest/register.d.ts +6 -0
  77. package/dist/extensions/inngest/register.js +23 -0
  78. package/dist/extensions/inngest/templates/ai-agent-durable.d.ts +8 -0
  79. package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-agent-durable.js +8 -8
  80. package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-pipeline-durable.d.ts +2 -2
  81. package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-pipeline-durable.js +7 -7
  82. package/dist/generated-version.d.ts +1 -1
  83. package/dist/generated-version.js +1 -1
  84. package/dist/generator/compile-target-registry.d.ts +20 -0
  85. package/dist/generator/compile-target-registry.js +20 -0
  86. package/dist/generator/dev-mode-registry.d.ts +27 -0
  87. package/dist/generator/dev-mode-registry.js +20 -0
  88. package/dist/index.d.ts +4 -0
  89. package/dist/index.js +3 -0
  90. package/dist/jsdoc-parser.d.ts +12 -114
  91. package/dist/jsdoc-parser.js +57 -362
  92. package/dist/marketplace/index.d.ts +2 -2
  93. package/dist/marketplace/index.js +1 -1
  94. package/dist/marketplace/registry.d.ts +39 -1
  95. package/dist/marketplace/registry.js +77 -0
  96. package/dist/marketplace/types.d.ts +76 -3
  97. package/dist/mcp/server.d.ts +1 -0
  98. package/dist/mcp/server.js +2 -0
  99. package/dist/mcp/tools-export.js +3 -3
  100. package/dist/mcp/tools-query.js +17 -11
  101. package/dist/mcp/tools-template.js +1 -1
  102. package/dist/parser/tag-registry.d.ts +47 -0
  103. package/dist/parser/tag-registry.js +57 -0
  104. package/dist/parser.d.ts +3 -0
  105. package/dist/parser.js +10 -23
  106. package/dist/validation/rule-registry.d.ts +36 -0
  107. package/dist/validation/rule-registry.js +37 -0
  108. package/dist/validator.js +3 -3
  109. package/docs/reference/concepts.md +2 -1
  110. package/docs/reference/deployment.md +21 -0
  111. package/docs/reference/jsdoc-grammar.md +242 -1
  112. package/docs/reference/scaffold.md +0 -6
  113. package/package.json +9 -1
  114. package/dist/cli/templates/workflows/ai-agent-durable.d.ts +0 -8
  115. package/dist/export/templates.d.ts +0 -24
  116. package/dist/export/templates.js +0 -186
  117. package/dist/validation/cicd-rules.d.ts +0 -62
  118. /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-docker.js +0 -0
  119. /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-matrix.js +0 -0
  120. /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-multi-env.js +0 -0
  121. /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-test-deploy.js +0 -0
@@ -3,14 +3,13 @@
3
3
  */
4
4
  import * as path from 'path';
5
5
  import * as fs from 'fs';
6
- import * as os from 'os';
7
6
  import { glob } from 'glob';
8
- import { spawn } from 'child_process';
9
- import { compileCommand, compileInngestTarget } from './compile.js';
7
+ import { compileCommand } from './compile.js';
10
8
  import { executeWorkflowFromFile } from '../../mcp/workflow-executor.js';
11
9
  import { logger } from '../utils/logger.js';
12
10
  import { getErrorMessage } from '../../utils/error-utils.js';
13
11
  import { getFriendlyError } from '../../friendly-errors.js';
12
+ import { devModeRegistry } from '../../generator/dev-mode-registry.js';
14
13
  function timestamp() {
15
14
  const now = new Date();
16
15
  const h = String(now.getHours()).padStart(2, '0');
@@ -124,204 +123,6 @@ async function compileAndRun(filePath, params, options) {
124
123
  }
125
124
  }
126
125
  // ---------------------------------------------------------------------------
127
- // Inngest Dev Mode
128
- // ---------------------------------------------------------------------------
129
- /**
130
- * Check that required packages are installed.
131
- */
132
- function checkDependency(pkg, cwd) {
133
- try {
134
- require.resolve(pkg, { paths: [cwd] });
135
- return true;
136
- }
137
- catch {
138
- return false;
139
- }
140
- }
141
- /**
142
- * Generate the dev server entry file for Inngest.
143
- */
144
- function generateDevServerEntry(inngestOutputPath, framework, port) {
145
- const relImport = `./${path.basename(inngestOutputPath).replace(/\.ts$/, '.js')}`;
146
- if (framework === 'express') {
147
- return `import express from 'express';
148
- import { handler } from '${relImport}';
149
-
150
- const app = express();
151
- app.use(express.json());
152
- app.use('/api/inngest', handler);
153
- app.listen(${port}, () => {
154
- console.log('Inngest dev server running on http://localhost:${port}');
155
- console.log('Inngest endpoint: http://localhost:${port}/api/inngest');
156
- console.log('');
157
- console.log('Connect Inngest Dev Server:');
158
- console.log(' npx inngest-cli@latest dev -u http://localhost:${port}/api/inngest');
159
- });
160
- `;
161
- }
162
- if (framework === 'hono') {
163
- return `import { Hono } from 'hono';
164
- import { serve } from '@hono/node-server';
165
- import { handler } from '${relImport}';
166
-
167
- const app = new Hono();
168
- app.route('/api/inngest', handler);
169
-
170
- serve({ fetch: app.fetch, port: ${port} }, () => {
171
- console.log('Inngest dev server running on http://localhost:${port}');
172
- console.log('Inngest endpoint: http://localhost:${port}/api/inngest');
173
- console.log('');
174
- console.log('Connect Inngest Dev Server:');
175
- console.log(' npx inngest-cli@latest dev -u http://localhost:${port}/api/inngest');
176
- });
177
- `;
178
- }
179
- // Default: express (most common for dev)
180
- return generateDevServerEntry(inngestOutputPath, 'express', port);
181
- }
182
- /**
183
- * Compile workflow to Inngest and start a local dev server.
184
- */
185
- async function runInngestDevMode(filePath, options) {
186
- const framework = options.framework ?? 'express';
187
- const port = options.port ?? 3000;
188
- const cwd = path.dirname(filePath);
189
- // Check dependencies
190
- const missingDeps = [];
191
- if (!checkDependency('inngest', cwd))
192
- missingDeps.push('inngest');
193
- if (framework === 'express' && !checkDependency('express', cwd))
194
- missingDeps.push('express');
195
- if (framework === 'hono') {
196
- if (!checkDependency('hono', cwd))
197
- missingDeps.push('hono');
198
- if (!checkDependency('@hono/node-server', cwd))
199
- missingDeps.push('@hono/node-server');
200
- }
201
- if (missingDeps.length > 0) {
202
- throw new Error(`Missing dependencies: ${missingDeps.join(', ')}. Install them with: npm install ${missingDeps.join(' ')}`);
203
- }
204
- // Set up temp directory for generated files
205
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fw-inngest-dev-'));
206
- const inngestOutputPath = path.join(tmpDir, path.basename(filePath).replace(/\.ts$/, '.inngest.ts'));
207
- let serverProcess = null;
208
- const compileInngest = async () => {
209
- try {
210
- await compileInngestTarget(filePath, {
211
- production: false,
212
- workflowName: options.workflow,
213
- serve: true,
214
- framework: framework,
215
- typedEvents: true,
216
- });
217
- // compileInngestTarget writes to filePath.replace(.ts, .inngest.ts)
218
- // Copy it to our temp dir, then remove the source-adjacent file
219
- const sourceOutput = filePath.replace(/\.ts$/, '.inngest.ts');
220
- if (fs.existsSync(sourceOutput)) {
221
- fs.copyFileSync(sourceOutput, inngestOutputPath);
222
- try {
223
- fs.unlinkSync(sourceOutput);
224
- }
225
- catch { /* ignore */ }
226
- }
227
- return true;
228
- }
229
- catch (error) {
230
- logger.error(`Inngest compilation failed: ${getErrorMessage(error)}`);
231
- return false;
232
- }
233
- };
234
- const startServer = () => {
235
- // Generate dev server entry
236
- const entryPath = path.join(tmpDir, 'dev-server.ts');
237
- const entryCode = generateDevServerEntry(inngestOutputPath, framework, port);
238
- fs.writeFileSync(entryPath, entryCode, 'utf8');
239
- // Spawn tsx to run the server
240
- serverProcess = spawn('npx', ['tsx', entryPath], {
241
- cwd: path.dirname(filePath),
242
- stdio: 'inherit',
243
- shell: true,
244
- });
245
- serverProcess.on('error', (err) => {
246
- logger.error(`Server process error: ${err.message}`);
247
- });
248
- serverProcess.on('exit', (code) => {
249
- if (code !== null && code !== 0) {
250
- logger.error(`Server exited with code ${code}`);
251
- }
252
- serverProcess = null;
253
- });
254
- };
255
- const stopServer = () => {
256
- if (serverProcess && !serverProcess.killed) {
257
- serverProcess.kill();
258
- serverProcess = null;
259
- }
260
- };
261
- const restartServer = async () => {
262
- stopServer();
263
- const ok = await compileInngest();
264
- if (ok) {
265
- startServer();
266
- }
267
- };
268
- // Header
269
- logger.section('Inngest Dev Mode');
270
- logger.info(`File: ${path.basename(filePath)}`);
271
- logger.info(`Framework: ${framework}`);
272
- logger.info(`Port: ${port}`);
273
- logger.newline();
274
- // Initial compile + start
275
- const ok = await compileInngest();
276
- if (!ok) {
277
- if (options.once)
278
- return;
279
- logger.info('Fix the errors above, then save the file to retry.');
280
- }
281
- else {
282
- if (options.once)
283
- return;
284
- startServer();
285
- }
286
- // Watch for changes
287
- logger.newline();
288
- logger.success('Watching for file changes... (Ctrl+C to stop)');
289
- const files = await glob(path.resolve(filePath), { absolute: true });
290
- const chokidar = await import('chokidar');
291
- const watcher = chokidar.watch(files, {
292
- persistent: true,
293
- ignoreInitial: true,
294
- });
295
- watcher.on('change', async (file) => {
296
- cycleSeparator(file);
297
- logger.info('Recompiling and restarting server...');
298
- await restartServer();
299
- });
300
- // Cleanup
301
- const sourceOutput = filePath.replace(/\.ts$/, '.inngest.ts');
302
- const cleanup = () => {
303
- logger.newline();
304
- logger.info('Stopping Inngest dev mode...');
305
- stopServer();
306
- watcher.close();
307
- // Clean up temp files and source-adjacent .inngest.ts
308
- try {
309
- fs.rmSync(tmpDir, { recursive: true, force: true });
310
- }
311
- catch { /* ignore */ }
312
- try {
313
- fs.unlinkSync(sourceOutput);
314
- }
315
- catch { /* ignore */ }
316
- process.exit(0);
317
- };
318
- process.on('SIGINT', cleanup);
319
- if (process.platform !== 'win32')
320
- process.on('SIGTERM', cleanup);
321
- // Keep process alive
322
- await new Promise(() => { });
323
- }
324
- // ---------------------------------------------------------------------------
325
126
  // Main Command
326
127
  // ---------------------------------------------------------------------------
327
128
  /**
@@ -332,9 +133,14 @@ export async function devCommand(input, options = {}) {
332
133
  if (!fs.existsSync(filePath)) {
333
134
  throw new Error(`File not found: ${filePath}`);
334
135
  }
335
- // Route to Inngest dev mode if target is inngest
336
- if (options.target === 'inngest') {
337
- return runInngestDevMode(filePath, options);
136
+ // Delegate to a registered dev mode provider if one exists for the target
137
+ if (options.target) {
138
+ const provider = devModeRegistry.get(options.target);
139
+ if (provider) {
140
+ return provider.run(filePath, options);
141
+ }
142
+ const available = devModeRegistry.getNames();
143
+ throw new Error(`Unknown dev target "${options.target}". ${available.length ? `Available: ${available.join(', ')}` : 'No dev mode providers registered. Install a pack that provides one.'}`);
338
144
  }
339
145
  const params = parseParams(options);
340
146
  if (!options.json) {
@@ -455,7 +455,8 @@ export function checkTsxAvailable(cwd) {
455
455
  }
456
456
  // ── Config health checks ─────────────────────────────────────────────────────
457
457
  const VALID_FILE_TYPES = ['ts', 'tsx', 'js', 'jsx'];
458
- const VALID_TARGETS = ['lambda', 'vercel', 'cloudflare'];
458
+ // Target validation is skipped — valid targets are discovered dynamically
459
+ // from installed packs at export time.
459
460
  function readYaml(filePath) {
460
461
  try {
461
462
  const content = fs.readFileSync(filePath, 'utf8');
@@ -606,8 +607,10 @@ export function checkDeploymentProfiles(cwd) {
606
607
  continue;
607
608
  }
608
609
  const config = data;
609
- if (config.target && !VALID_TARGETS.includes(config.target)) {
610
- invalid.push(`${profile} (invalid target "${config.target}" must be: ${VALID_TARGETS.join(', ')})`);
610
+ // Target names are validated at export time via the target registry.
611
+ // We only check that the value is a non-empty string here.
612
+ if (config.target && typeof config.target !== 'string') {
613
+ invalid.push(`${profile} (target must be a string)`);
611
614
  }
612
615
  }
613
616
  if (invalid.length > 0) {
@@ -2,7 +2,7 @@
2
2
  * Export command - generate serverless function handlers for deployment
3
3
  */
4
4
  export interface ExportOptions {
5
- /** Target platform (lambda, vercel, cloudflare, inngest) */
5
+ /** Target platform (provided by installed packs) */
6
6
  target: string;
7
7
  /** Output directory */
8
8
  output: string;
@@ -20,7 +20,7 @@ export interface ExportOptions {
20
20
  workflows?: string;
21
21
  /** Include API documentation routes (/docs and /openapi.json) */
22
22
  docs?: boolean;
23
- /** Use deep generator with per-node Inngest steps for durability (inngest target only) */
23
+ /** Use deep generator with per-node durable steps */
24
24
  durableSteps?: boolean;
25
25
  }
26
26
  /**
@@ -31,26 +31,17 @@ export interface ExportOptions {
31
31
  *
32
32
  * @example
33
33
  * ```bash
34
- * # Export for Vercel
35
- * flow-weaver export workflow.ts --target vercel --output api/
36
- *
37
- * # Export for AWS Lambda
38
- * flow-weaver export workflow.ts --target lambda --output dist/lambda/
39
- *
40
- * # Export for Cloudflare Workers
41
- * flow-weaver export workflow.ts --target cloudflare --output workers/
34
+ * # Export for a target (install the corresponding pack first)
35
+ * flow-weaver export workflow.ts --target <target> --output dist/
42
36
  *
43
37
  * # Export specific workflow from multi-workflow file
44
- * flow-weaver export multi-workflow.ts --target vercel --output api/ --workflow calculate
45
- *
46
- * # Export all workflows as a single service (multi-workflow mode)
47
- * flow-weaver export workflows.ts --target lambda --output dist/ --multi
38
+ * flow-weaver export multi-workflow.ts --target <target> --output api/ --workflow calculate
48
39
  *
49
- * # Export specific workflows from a file as a service
50
- * flow-weaver export workflows.ts --target lambda --output dist/ --multi --workflows "workflowA,workflowB"
40
+ * # Export all workflows as a single service
41
+ * flow-weaver export workflows.ts --target <target> --output dist/ --multi
51
42
  *
52
43
  * # Export with API documentation routes
53
- * flow-weaver export workflow.ts --target lambda --output dist/ --docs
44
+ * flow-weaver export workflow.ts --target <target> --output dist/ --docs
54
45
  * ```
55
46
  */
56
47
  export declare function exportCommand(input: string, options: ExportOptions): Promise<void>;
@@ -11,32 +11,23 @@ import { logger } from '../utils/logger.js';
11
11
  *
12
12
  * @example
13
13
  * ```bash
14
- * # Export for Vercel
15
- * flow-weaver export workflow.ts --target vercel --output api/
16
- *
17
- * # Export for AWS Lambda
18
- * flow-weaver export workflow.ts --target lambda --output dist/lambda/
19
- *
20
- * # Export for Cloudflare Workers
21
- * flow-weaver export workflow.ts --target cloudflare --output workers/
14
+ * # Export for a target (install the corresponding pack first)
15
+ * flow-weaver export workflow.ts --target <target> --output dist/
22
16
  *
23
17
  * # Export specific workflow from multi-workflow file
24
- * flow-weaver export multi-workflow.ts --target vercel --output api/ --workflow calculate
25
- *
26
- * # Export all workflows as a single service (multi-workflow mode)
27
- * flow-weaver export workflows.ts --target lambda --output dist/ --multi
18
+ * flow-weaver export multi-workflow.ts --target <target> --output api/ --workflow calculate
28
19
  *
29
- * # Export specific workflows from a file as a service
30
- * flow-weaver export workflows.ts --target lambda --output dist/ --multi --workflows "workflowA,workflowB"
20
+ * # Export all workflows as a single service
21
+ * flow-weaver export workflows.ts --target <target> --output dist/ --multi
31
22
  *
32
23
  * # Export with API documentation routes
33
- * flow-weaver export workflow.ts --target lambda --output dist/ --docs
24
+ * flow-weaver export workflow.ts --target <target> --output dist/ --docs
34
25
  * ```
35
26
  */
36
27
  export async function exportCommand(input, options) {
37
28
  // Validate target is provided
38
29
  if (!options.target) {
39
- throw new Error('--target is required. Install a target pack (e.g. npm install flowweaver-pack-lambda)');
30
+ throw new Error('--target is required. Install a target pack first.');
40
31
  }
41
32
  const isDryRun = options.dryRun ?? false;
42
33
  const t = logger.timer();
@@ -64,7 +55,7 @@ export async function exportCommand(input, options) {
64
55
  logger.info('Include docs: Yes (/docs and /openapi.json routes)');
65
56
  }
66
57
  if (options.durableSteps) {
67
- logger.info('Durable steps: Yes (per-node step.run for Inngest)');
58
+ logger.info('Durable steps: Yes');
68
59
  }
69
60
  if (isDryRun) {
70
61
  logger.info('Mode: DRY RUN (no files will be written)');
@@ -3,7 +3,7 @@
3
3
  * Types, use-case mappings, template selection, and post-scaffold output.
4
4
  */
5
5
  export type PersonaId = 'nocode' | 'vibecoder' | 'lowcode' | 'expert';
6
- export type UseCaseId = 'data' | 'ai' | 'api' | 'automation' | 'cicd' | 'minimal';
6
+ export type UseCaseId = 'data' | 'ai' | 'api' | 'automation' | 'minimal';
7
7
  export declare const PERSONA_CHOICES: {
8
8
  value: PersonaId;
9
9
  name: string;
@@ -20,13 +20,22 @@ export interface UseCaseMapping {
20
20
  default: string;
21
21
  all: string[];
22
22
  }
23
- export declare const USE_CASE_TEMPLATES: Record<UseCaseId, UseCaseMapping>;
23
+ export declare const USE_CASE_TEMPLATES: Record<string, UseCaseMapping>;
24
+ /**
25
+ * Register a use case contributed by a pack.
26
+ * Adds the use case choice and template mapping so it appears in fw init.
27
+ */
28
+ export declare function registerPackUseCase(useCase: {
29
+ id: string;
30
+ name: string;
31
+ description: string;
32
+ }, templates: string[]): void;
24
33
  /**
25
34
  * Select the template for a given persona and use-case.
26
35
  * For lowcode, returns the full list when the category has multiple templates.
27
36
  * For nocode/vibecoder, always returns the single default.
28
37
  */
29
- export declare function selectTemplateForPersona(persona: PersonaId, useCase: UseCaseId): {
38
+ export declare function selectTemplateForPersona(persona: PersonaId, useCase: UseCaseId | string): {
30
39
  template: string;
31
40
  choices?: string[];
32
41
  };
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { execSync } from 'child_process';
6
6
  import { logger } from '../utils/logger.js';
7
- import { workflowTemplates } from '../templates/index.js';
7
+ import { getAllWorkflowTemplates } from '../templates/index.js';
8
8
  import { buildContext } from '../../context/index.js';
9
9
  // ── Prompt choices ───────────────────────────────────────────────────────────
10
10
  export const PERSONA_CHOICES = [
@@ -25,7 +25,6 @@ export const USE_CASE_CHOICES = [
25
25
  { value: 'ai', name: 'AI agent', description: 'LLM with tools, reasoning, or retrieval' },
26
26
  { value: 'api', name: 'API / webhook', description: 'HTTP endpoints and integrations' },
27
27
  { value: 'automation', name: 'Automation', description: 'Conditional logic, error handling, routing' },
28
- { value: 'cicd', name: 'CI/CD pipeline', description: 'Test, build, and deploy workflows' },
29
28
  { value: 'minimal', name: 'Something else', description: 'Start with a minimal template' },
30
29
  ];
31
30
  export const USE_CASE_TEMPLATES = {
@@ -33,9 +32,33 @@ export const USE_CASE_TEMPLATES = {
33
32
  ai: { default: 'ai-agent', all: ['ai-agent', 'ai-react', 'ai-rag', 'ai-chat'] },
34
33
  api: { default: 'webhook', all: ['webhook'] },
35
34
  automation: { default: 'conditional', all: ['conditional', 'error-handler'] },
36
- cicd: { default: 'cicd-test-deploy', all: ['cicd-test-deploy', 'cicd-docker', 'cicd-multi-env', 'cicd-matrix'] },
37
35
  minimal: { default: 'sequential', all: ['sequential'] },
38
36
  };
37
+ /**
38
+ * Register a use case contributed by a pack.
39
+ * Adds the use case choice and template mapping so it appears in fw init.
40
+ */
41
+ export function registerPackUseCase(useCase, templates) {
42
+ // Add to choices if not already present
43
+ if (!USE_CASE_CHOICES.some((c) => c.value === useCase.id)) {
44
+ // Insert before "minimal" (last entry)
45
+ const minimalIdx = USE_CASE_CHOICES.findIndex((c) => c.value === 'minimal');
46
+ const entry = { value: useCase.id, name: useCase.name, description: useCase.description };
47
+ if (minimalIdx >= 0) {
48
+ USE_CASE_CHOICES.splice(minimalIdx, 0, entry);
49
+ }
50
+ else {
51
+ USE_CASE_CHOICES.push(entry);
52
+ }
53
+ }
54
+ // Add template mapping
55
+ if (!USE_CASE_TEMPLATES[useCase.id] && templates.length > 0) {
56
+ USE_CASE_TEMPLATES[useCase.id] = {
57
+ default: templates[0],
58
+ all: templates,
59
+ };
60
+ }
61
+ }
39
62
  /**
40
63
  * Select the template for a given persona and use-case.
41
64
  * For lowcode, returns the full list when the category has multiple templates.
@@ -57,7 +80,7 @@ export function selectTemplateForPersona(persona, useCase) {
57
80
  */
58
81
  export function getTemplateSubChoices(templateIds) {
59
82
  return templateIds.map((id) => {
60
- const tmpl = workflowTemplates.find((t) => t.id === id);
83
+ const tmpl = getAllWorkflowTemplates().find((t) => t.id === id);
61
84
  return {
62
85
  value: id,
63
86
  name: id,
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { ToolId } from './mcp-setup.js';
6
6
  import type { TModuleFormat } from '../../ast/types.js';
7
- import type { PersonaId, UseCaseId } from './init-personas.js';
7
+ import type { PersonaId } from './init-personas.js';
8
8
  export interface InitOptions {
9
9
  name?: string;
10
10
  template?: string;
@@ -28,7 +28,7 @@ export interface InitConfig {
28
28
  git: boolean;
29
29
  force: boolean;
30
30
  persona: PersonaId;
31
- useCase?: UseCaseId;
31
+ useCase?: string;
32
32
  /** Free-text description when user picked "Something else" */
33
33
  useCaseDescription?: string;
34
34
  mcp: boolean;
@@ -10,7 +10,7 @@ import input from '@inquirer/input';
10
10
  import select, { Separator } from '@inquirer/select';
11
11
  import confirm from '@inquirer/confirm';
12
12
  import { ExitPromptError } from '@inquirer/core';
13
- import { getWorkflowTemplate, workflowTemplates } from '../templates/index.js';
13
+ import { getWorkflowTemplate, getAllWorkflowTemplates } from '../templates/index.js';
14
14
  import { logger } from '../utils/logger.js';
15
15
  import { compileCommand } from './compile.js';
16
16
  import { runMcpSetupFromInit, CLI_TOOL_BINARY, detectCliTools } from './mcp-setup.js';
@@ -37,9 +37,11 @@ export function toWorkflowName(projectName) {
37
37
  export function isNonInteractive() {
38
38
  return !process.stdin.isTTY;
39
39
  }
40
- const VALID_TEMPLATES = workflowTemplates.map((t) => t.id);
40
+ // Dynamic: includes core templates plus any registered by extensions
41
+ const VALID_TEMPLATES = getAllWorkflowTemplates().map((t) => t.id);
41
42
  const VALID_PERSONAS = ['nocode', 'vibecoder', 'lowcode', 'expert'];
42
- const VALID_USE_CASES = ['data', 'ai', 'api', 'automation', 'cicd', 'minimal'];
43
+ // Dynamic: includes core use cases plus any registered by extensions
44
+ const VALID_USE_CASES = USE_CASE_CHOICES.map((c) => c.value);
43
45
  // ── Config resolution (prompts) ──────────────────────────────────────────────
44
46
  export async function resolveInitConfig(dirArg, options) {
45
47
  const skipPrompts = options.yes || isNonInteractive();
@@ -124,18 +126,10 @@ export async function resolveInitConfig(dirArg, options) {
124
126
  { value: 'ai-react', name: 'ai-react', description: 'ReAct pattern' },
125
127
  { value: 'ai-rag', name: 'ai-rag', description: 'Retrieval-Augmented Generation' },
126
128
  { value: 'ai-chat', name: 'ai-chat', description: 'Conversational AI' },
127
- new Separator('── Durable ──'),
128
- { value: 'ai-agent-durable', name: 'ai-agent-durable', description: 'Durable agent with approval gate' },
129
- { value: 'ai-pipeline-durable', name: 'ai-pipeline-durable', description: 'Durable data pipeline' },
130
129
  new Separator('── Integration ──'),
131
130
  { value: 'webhook', name: 'webhook', description: 'HTTP webhook handler' },
132
131
  new Separator('── Utility ──'),
133
132
  { value: 'error-handler', name: 'error-handler', description: 'Error handling and recovery' },
134
- new Separator('── CI/CD ──'),
135
- { value: 'cicd-test-deploy', name: 'cicd-test-deploy', description: 'Test and deploy pipeline' },
136
- { value: 'cicd-docker', name: 'cicd-docker', description: 'Docker build and push' },
137
- { value: 'cicd-multi-env', name: 'cicd-multi-env', description: 'Multi-environment deploy' },
138
- { value: 'cicd-matrix', name: 'cicd-matrix', description: 'Matrix build strategy' },
139
133
  ],
140
134
  default: 'sequential',
141
135
  });