@synergenius/flow-weaver 0.17.0 → 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 (123) 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 +17 -6
  20. package/dist/cli/commands/init-personas.js +73 -24
  21. package/dist/cli/commands/init.d.ts +5 -2
  22. package/dist/cli/commands/init.js +73 -42
  23. package/dist/cli/commands/mcp-setup.d.ts +7 -0
  24. package/dist/cli/commands/mcp-setup.js +16 -1
  25. package/dist/cli/flow-weaver.mjs +71002 -70385
  26. package/dist/cli/index.d.ts +1 -0
  27. package/dist/cli/index.js +9 -7
  28. package/dist/cli/templates/index.d.ts +20 -1
  29. package/dist/cli/templates/index.js +66 -15
  30. package/dist/cli/templates/nodes/human-approval.js +2 -3
  31. package/dist/cli/templates/nodes/rag-retriever.js +1 -1
  32. package/dist/constants.d.ts +7 -0
  33. package/dist/constants.js +13 -3
  34. package/dist/context/index.js +13 -3
  35. package/dist/deployment/config/loader.js +2 -1
  36. package/dist/deployment/core/adapters.d.ts +1 -25
  37. package/dist/deployment/core/adapters.js +0 -95
  38. package/dist/deployment/core/formatters.d.ts +0 -15
  39. package/dist/deployment/core/formatters.js +0 -24
  40. package/dist/deployment/index.d.ts +7 -5
  41. package/dist/deployment/index.js +8 -5
  42. package/dist/deployment/types.d.ts +2 -45
  43. package/dist/diagram/html-viewer.js +65 -32
  44. package/dist/diagram/renderer.js +9 -6
  45. package/dist/diagram/theme.js +4 -0
  46. package/dist/diagram/types.d.ts +2 -0
  47. package/dist/doc-metadata/extractors/annotations.js +5 -5
  48. package/dist/doc-metadata/extractors/cli-commands.js +1 -1
  49. package/dist/doc-metadata/extractors/mcp-tools.js +1 -2
  50. package/dist/docs/index.d.ts +28 -1
  51. package/dist/docs/index.js +95 -28
  52. package/dist/export/index.d.ts +2 -3
  53. package/dist/{deployment/targets/cicd-base.d.ts → extensions/cicd/base-target.d.ts} +35 -36
  54. package/dist/{deployment/targets/cicd-base.js → extensions/cicd/base-target.js} +97 -57
  55. package/dist/{validation/cicd-detection.d.ts → extensions/cicd/detection.d.ts} +2 -2
  56. package/dist/{validation/cicd-detection.js → extensions/cicd/detection.js} +13 -1
  57. package/dist/extensions/cicd/docs/cicd.md +395 -0
  58. package/dist/extensions/cicd/index.d.ts +10 -0
  59. package/dist/extensions/cicd/index.js +10 -0
  60. package/dist/extensions/cicd/register.d.ts +11 -0
  61. package/dist/extensions/cicd/register.js +62 -0
  62. package/dist/extensions/cicd/rules.d.ts +30 -0
  63. package/dist/{validation/cicd-rules.js → extensions/cicd/rules.js} +60 -56
  64. package/dist/extensions/cicd/tag-handler.d.ts +14 -0
  65. package/dist/extensions/cicd/tag-handler.js +488 -0
  66. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-docker.d.ts +1 -1
  67. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-matrix.d.ts +1 -1
  68. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-multi-env.d.ts +1 -1
  69. package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-test-deploy.d.ts +1 -1
  70. package/dist/extensions/index.d.ts +12 -0
  71. package/dist/extensions/index.js +12 -0
  72. package/dist/extensions/inngest/dev-mode.d.ts +9 -0
  73. package/dist/extensions/inngest/dev-mode.js +213 -0
  74. package/dist/{generator/inngest.d.ts → extensions/inngest/generator.d.ts} +2 -2
  75. package/dist/{generator/inngest.js → extensions/inngest/generator.js} +4 -4
  76. package/dist/extensions/inngest/index.d.ts +2 -0
  77. package/dist/extensions/inngest/index.js +2 -0
  78. package/dist/extensions/inngest/register.d.ts +6 -0
  79. package/dist/extensions/inngest/register.js +23 -0
  80. package/dist/extensions/inngest/templates/ai-agent-durable.d.ts +8 -0
  81. package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-agent-durable.js +8 -8
  82. package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-pipeline-durable.d.ts +2 -2
  83. package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-pipeline-durable.js +7 -7
  84. package/dist/generated-version.d.ts +1 -1
  85. package/dist/generated-version.js +1 -1
  86. package/dist/generator/compile-target-registry.d.ts +20 -0
  87. package/dist/generator/compile-target-registry.js +20 -0
  88. package/dist/generator/dev-mode-registry.d.ts +27 -0
  89. package/dist/generator/dev-mode-registry.js +20 -0
  90. package/dist/index.d.ts +4 -0
  91. package/dist/index.js +3 -0
  92. package/dist/jsdoc-parser.d.ts +12 -114
  93. package/dist/jsdoc-parser.js +57 -362
  94. package/dist/marketplace/index.d.ts +2 -2
  95. package/dist/marketplace/index.js +1 -1
  96. package/dist/marketplace/registry.d.ts +39 -1
  97. package/dist/marketplace/registry.js +77 -0
  98. package/dist/marketplace/types.d.ts +76 -3
  99. package/dist/mcp/server.d.ts +1 -0
  100. package/dist/mcp/server.js +2 -0
  101. package/dist/mcp/tools-export.js +3 -3
  102. package/dist/mcp/tools-query.js +17 -11
  103. package/dist/mcp/tools-template.js +1 -1
  104. package/dist/parser/tag-registry.d.ts +47 -0
  105. package/dist/parser/tag-registry.js +57 -0
  106. package/dist/parser.d.ts +3 -0
  107. package/dist/parser.js +10 -23
  108. package/dist/validation/rule-registry.d.ts +36 -0
  109. package/dist/validation/rule-registry.js +37 -0
  110. package/dist/validator.js +3 -3
  111. package/docs/reference/concepts.md +2 -1
  112. package/docs/reference/deployment.md +21 -0
  113. package/docs/reference/jsdoc-grammar.md +242 -1
  114. package/docs/reference/scaffold.md +0 -6
  115. package/package.json +9 -1
  116. package/dist/cli/templates/workflows/ai-agent-durable.d.ts +0 -8
  117. package/dist/export/templates.d.ts +0 -24
  118. package/dist/export/templates.js +0 -186
  119. package/dist/validation/cicd-rules.d.ts +0 -62
  120. /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-docker.js +0 -0
  121. /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-matrix.js +0 -0
  122. /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-multi-env.js +0 -0
  123. /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
  };
@@ -58,6 +67,8 @@ export interface PrintNextStepsOptions {
58
67
  mcpConfigured?: string[];
59
68
  /** When true, skip persona-specific guidance (the agent handles it) */
60
69
  agentLaunched?: boolean;
70
+ /** When true, the workflow was auto-compiled and npm start works immediately */
71
+ compiled?: boolean;
61
72
  }
62
73
  export declare function printNextSteps(opts: PrintNextStepsOptions): void;
63
74
  /** Maps each persona to the fw_context preset used for agent knowledge bootstrap. */
@@ -66,17 +77,17 @@ export declare const AGENT_CONTEXT_PRESETS: Record<PersonaId, string>;
66
77
  * Generate the initial prompt for a CLI agent (Claude Code, Codex).
67
78
  * Interpolates project name and template into the persona-specific template.
68
79
  */
69
- export declare function generateAgentPrompt(projectName: string, persona: PersonaId, template: string): string;
80
+ export declare function generateAgentPrompt(projectName: string, persona: PersonaId, template: string, useCaseDescription?: string): string;
70
81
  /**
71
82
  * Generate a shorter prompt suitable for pasting into a GUI editor.
72
83
  * Persona-aware but more concise than the full agent prompt.
73
84
  */
74
- export declare function generateEditorPrompt(projectName: string, persona: PersonaId, template: string): string;
85
+ export declare function generateEditorPrompt(projectName: string, persona: PersonaId, template: string, useCaseDescription?: string): string;
75
86
  /**
76
87
  * Generate the full content for a PROJECT_SETUP.md file.
77
88
  * Includes the agent prompt plus project context.
78
89
  */
79
- export declare function generateSetupPromptFile(projectName: string, persona: PersonaId, template: string, filesCreated: string[]): string;
90
+ export declare function generateSetupPromptFile(projectName: string, persona: PersonaId, template: string, filesCreated: string[], useCaseDescription?: string): string;
80
91
  /**
81
92
  * Print a copyable prompt in a bordered box.
82
93
  * Long lines are word-wrapped to fit within the box.
@@ -2,8 +2,9 @@
2
2
  * Persona-specific logic for the init command.
3
3
  * Types, use-case mappings, template selection, and post-scaffold output.
4
4
  */
5
+ import { execSync } from 'child_process';
5
6
  import { logger } from '../utils/logger.js';
6
- import { workflowTemplates } from '../templates/index.js';
7
+ import { getAllWorkflowTemplates } from '../templates/index.js';
7
8
  import { buildContext } from '../../context/index.js';
8
9
  // ── Prompt choices ───────────────────────────────────────────────────────────
9
10
  export const PERSONA_CHOICES = [
@@ -24,7 +25,6 @@ export const USE_CASE_CHOICES = [
24
25
  { value: 'ai', name: 'AI agent', description: 'LLM with tools, reasoning, or retrieval' },
25
26
  { value: 'api', name: 'API / webhook', description: 'HTTP endpoints and integrations' },
26
27
  { value: 'automation', name: 'Automation', description: 'Conditional logic, error handling, routing' },
27
- { value: 'cicd', name: 'CI/CD pipeline', description: 'Test, build, and deploy workflows' },
28
28
  { value: 'minimal', name: 'Something else', description: 'Start with a minimal template' },
29
29
  ];
30
30
  export const USE_CASE_TEMPLATES = {
@@ -32,9 +32,33 @@ export const USE_CASE_TEMPLATES = {
32
32
  ai: { default: 'ai-agent', all: ['ai-agent', 'ai-react', 'ai-rag', 'ai-chat'] },
33
33
  api: { default: 'webhook', all: ['webhook'] },
34
34
  automation: { default: 'conditional', all: ['conditional', 'error-handler'] },
35
- cicd: { default: 'cicd-test-deploy', all: ['cicd-test-deploy', 'cicd-docker', 'cicd-multi-env', 'cicd-matrix'] },
36
35
  minimal: { default: 'sequential', all: ['sequential'] },
37
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
+ }
38
62
  /**
39
63
  * Select the template for a given persona and use-case.
40
64
  * For lowcode, returns the full list when the category has multiple templates.
@@ -56,7 +80,7 @@ export function selectTemplateForPersona(persona, useCase) {
56
80
  */
57
81
  export function getTemplateSubChoices(templateIds) {
58
82
  return templateIds.map((id) => {
59
- const tmpl = workflowTemplates.find((t) => t.id === id);
83
+ const tmpl = getAllWorkflowTemplates().find((t) => t.id === id);
60
84
  return {
61
85
  value: id,
62
86
  name: id,
@@ -215,7 +239,7 @@ export function ${fnName}(
215
239
  `;
216
240
  }
217
241
  export function printNextSteps(opts) {
218
- const { projectName, persona, displayDir, installSkipped, workflowCode, workflowFile, agentLaunched } = opts;
242
+ const { projectName, persona, displayDir, installSkipped, workflowCode, workflowFile, agentLaunched, compiled } = opts;
219
243
  // Workflow preview
220
244
  if (workflowCode) {
221
245
  const preview = extractWorkflowPreview(workflowCode);
@@ -247,7 +271,13 @@ export function printNextSteps(opts) {
247
271
  if (installSkipped) {
248
272
  logger.log(' npm install');
249
273
  }
250
- logger.log(' npm run dev');
274
+ if (compiled) {
275
+ logger.log(` npm start${' '.repeat(14)}${logger.dim('Run your compiled workflow')}`);
276
+ logger.log(` npm run dev${' '.repeat(12)}${logger.dim('Recompile + run (after editing)')}`);
277
+ }
278
+ else {
279
+ logger.log(' npm run dev');
280
+ }
251
281
  // Persona-specific guidance (skip if agent was launched, it handles this)
252
282
  if (!agentLaunched) {
253
283
  if (persona === 'nocode') {
@@ -282,27 +312,26 @@ function printNocodeGuidance(_projectName) {
282
312
  }
283
313
  function printVibecoderGuidance() {
284
314
  logger.newline();
285
- logger.log(' With your AI editor connected, try:');
315
+ logger.log(` ${logger.bold('Describe what you want, AI handles the code.')}`);
286
316
  logger.newline();
287
317
  logger.log(` ${logger.dim('"Add a retry loop when the model call fails"')}`);
288
- logger.log(` ${logger.dim('"Add a tool that searches a knowledge base"')}`);
318
+ logger.log(` ${logger.dim('"Connect this to a Postgres database"')}`);
319
+ logger.log(` ${logger.dim('"Show me a diagram of the current workflow"')}`);
289
320
  logger.newline();
290
- logger.log(` ${logger.bold('Commands you\'ll use')}`);
321
+ logger.log(` ${logger.bold('When you want to see the structure')}`);
291
322
  logger.newline();
292
- logger.log(` flow-weaver diagram src/*.ts ${logger.dim('See a visual diagram')}`);
293
- logger.log(` flow-weaver validate src/*.ts ${logger.dim('Check for errors')}`);
294
- logger.log(` flow-weaver templates ${logger.dim('Browse all templates')}`);
323
+ logger.log(` npm run diagram ${logger.dim('Visual diagram of your workflow')}`);
295
324
  }
296
325
  function printLowcodeGuidance() {
297
326
  logger.newline();
298
- logger.log(` ${logger.bold('Explore and learn')}`);
327
+ logger.log(` ${logger.bold('Explore and customize')}`);
299
328
  logger.newline();
300
329
  logger.log(` flow-weaver templates ${logger.dim('List all 16 workflow templates')}`);
301
- logger.log(` flow-weaver create workflow ... ${logger.dim('Add another workflow')}`);
302
- logger.log(` flow-weaver describe src/*.ts ${logger.dim('See workflow structure')}`);
303
- logger.log(` flow-weaver docs annotations ${logger.dim('Read the annotation reference')}`);
330
+ logger.log(` flow-weaver describe src/*.ts ${logger.dim('See the workflow structure')}`);
331
+ logger.log(` flow-weaver docs annotations ${logger.dim('Annotation reference')}`);
304
332
  logger.newline();
305
333
  logger.log(` Your project includes an example in ${logger.highlight('examples/')} to study.`);
334
+ logger.log(` With MCP connected, AI can help modify nodes and connections.`);
306
335
  }
307
336
  function printExpertGuidance() {
308
337
  logger.newline();
@@ -370,26 +399,32 @@ Then ask what I'd like to build.`,
370
399
  * Generate the initial prompt for a CLI agent (Claude Code, Codex).
371
400
  * Interpolates project name and template into the persona-specific template.
372
401
  */
373
- export function generateAgentPrompt(projectName, persona, template) {
374
- return AGENT_PROMPTS[persona]
402
+ export function generateAgentPrompt(projectName, persona, template, useCaseDescription) {
403
+ let prompt = AGENT_PROMPTS[persona]
375
404
  .replace(/\{name\}/g, projectName)
376
405
  .replace(/\{template\}/g, template);
406
+ if (useCaseDescription) {
407
+ // Insert the user's description after the template mention line
408
+ prompt = prompt.replace(/(using the .+ template\.?\n)/, `$1The user wants to build: ${useCaseDescription}\n`);
409
+ }
410
+ return prompt;
377
411
  }
378
412
  /**
379
413
  * Generate a shorter prompt suitable for pasting into a GUI editor.
380
414
  * Persona-aware but more concise than the full agent prompt.
381
415
  */
382
- export function generateEditorPrompt(projectName, persona, template) {
416
+ export function generateEditorPrompt(projectName, persona, template, useCaseDescription) {
383
417
  const preset = AGENT_CONTEXT_PRESETS[persona];
384
418
  const bootstrap = `Start by calling fw_context(preset="${preset}", profile="assistant") to learn Flow Weaver.`;
419
+ const desc = useCaseDescription ? ` I want to build: ${useCaseDescription}.` : '';
385
420
  if (persona === 'nocode') {
386
- return `${bootstrap}\nThis is a Flow Weaver project called "${projectName}" using the ${template} template. Show me the workflow diagram, walk me through what each step does in plain language, then ask me what I want to build. Keep it simple, no code.`;
421
+ return `${bootstrap}\nThis is a Flow Weaver project called "${projectName}" using the ${template} template.${desc} Show me the workflow diagram, walk me through what each step does in plain language, then ask me what I want to build. Keep it simple, no code.`;
387
422
  }
388
423
  if (persona === 'vibecoder') {
389
- return `${bootstrap}\nThis is a Flow Weaver project called "${projectName}" using the ${template} template. Show me the workflow diagram, then let's iterate on it together. I'll describe what I want and you handle the implementation.`;
424
+ return `${bootstrap}\nThis is a Flow Weaver project called "${projectName}" using the ${template} template.${desc} Show me the workflow diagram, then let's iterate on it together. I'll describe what I want and you handle the implementation.`;
390
425
  }
391
426
  if (persona === 'lowcode') {
392
- return `${bootstrap}\nThis is a Flow Weaver project called "${projectName}" using the ${template} template. Show me the workflow diagram and explain the template, then help me customize it for my use case.`;
427
+ return `${bootstrap}\nThis is a Flow Weaver project called "${projectName}" using the ${template} template.${desc} Show me the workflow diagram and explain the template, then help me customize it for my use case.`;
393
428
  }
394
429
  return `${bootstrap}\nFlow Weaver project "${projectName}" (template: ${template}). Show the workflow diagram and implementation status.`;
395
430
  }
@@ -397,8 +432,8 @@ export function generateEditorPrompt(projectName, persona, template) {
397
432
  * Generate the full content for a PROJECT_SETUP.md file.
398
433
  * Includes the agent prompt plus project context.
399
434
  */
400
- export function generateSetupPromptFile(projectName, persona, template, filesCreated) {
401
- const prompt = generateAgentPrompt(projectName, persona, template);
435
+ export function generateSetupPromptFile(projectName, persona, template, filesCreated, useCaseDescription) {
436
+ const prompt = generateAgentPrompt(projectName, persona, template, useCaseDescription);
402
437
  // Embed Flow Weaver knowledge directly so the file is self-contained.
403
438
  // GUI editors may not have MCP tools configured when first reading this file.
404
439
  const contextResult = buildContext({ preset: 'core', profile: 'assistant' });
@@ -481,6 +516,20 @@ export function printCopyablePrompt(prompt) {
481
516
  logger.log(` │ ${padded} │`);
482
517
  }
483
518
  logger.log(` ${'└' + '─'.repeat(width) + '┘'}`);
519
+ // Auto-copy to clipboard (best-effort)
520
+ try {
521
+ const clipCmd = process.platform === 'darwin' ? 'pbcopy'
522
+ : process.platform === 'linux' ? 'xclip -selection clipboard'
523
+ : null;
524
+ if (clipCmd) {
525
+ execSync(clipCmd, { input: prompt, stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000 });
526
+ logger.newline();
527
+ logger.success('Copied to clipboard');
528
+ }
529
+ }
530
+ catch {
531
+ // Clipboard not available, box is still there
532
+ }
484
533
  }
485
534
  /** Default for the "Launch agent?" confirm prompt per persona */
486
535
  export const AGENT_LAUNCH_DEFAULTS = {
@@ -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,9 @@ export interface InitConfig {
28
28
  git: boolean;
29
29
  force: boolean;
30
30
  persona: PersonaId;
31
- useCase?: UseCaseId;
31
+ useCase?: string;
32
+ /** Free-text description when user picked "Something else" */
33
+ useCaseDescription?: string;
32
34
  mcp: boolean;
33
35
  }
34
36
  export interface InitReport {
@@ -76,6 +78,7 @@ interface AgentHandoffOptions {
76
78
  cliTools: ToolId[];
77
79
  guiTools: ToolId[];
78
80
  filesCreated: string[];
81
+ useCaseDescription?: string;
79
82
  }
80
83
  /**
81
84
  * After init + MCP setup, offer to launch a CLI agent or generate a prompt for GUI editors.