@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.
- package/dist/api/index.d.ts +4 -1
- package/dist/api/index.js +4 -1
- package/dist/api/templates.js +2 -2
- package/dist/api/validate.d.ts +2 -2
- package/dist/api/validate.js +6 -6
- package/dist/api/validation-registry.d.ts +10 -0
- package/dist/api/validation-registry.js +10 -0
- package/dist/ast/types.d.ts +91 -4
- package/dist/built-in-nodes/invoke-workflow.d.ts +1 -1
- package/dist/built-in-nodes/invoke-workflow.js +1 -1
- package/dist/chevrotain-parser/connect-parser.js +25 -7
- package/dist/cli/commands/compile.d.ts +5 -9
- package/dist/cli/commands/compile.js +21 -14
- package/dist/cli/commands/dev.d.ts +2 -13
- package/dist/cli/commands/dev.js +10 -204
- package/dist/cli/commands/doctor.js +6 -3
- package/dist/cli/commands/export.d.ts +8 -17
- package/dist/cli/commands/export.js +8 -17
- package/dist/cli/commands/init-personas.d.ts +17 -6
- package/dist/cli/commands/init-personas.js +73 -24
- package/dist/cli/commands/init.d.ts +5 -2
- package/dist/cli/commands/init.js +73 -42
- package/dist/cli/commands/mcp-setup.d.ts +7 -0
- package/dist/cli/commands/mcp-setup.js +16 -1
- package/dist/cli/flow-weaver.mjs +71002 -70385
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +9 -7
- package/dist/cli/templates/index.d.ts +20 -1
- package/dist/cli/templates/index.js +66 -15
- package/dist/cli/templates/nodes/human-approval.js +2 -3
- package/dist/cli/templates/nodes/rag-retriever.js +1 -1
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +13 -3
- package/dist/context/index.js +13 -3
- package/dist/deployment/config/loader.js +2 -1
- package/dist/deployment/core/adapters.d.ts +1 -25
- package/dist/deployment/core/adapters.js +0 -95
- package/dist/deployment/core/formatters.d.ts +0 -15
- package/dist/deployment/core/formatters.js +0 -24
- package/dist/deployment/index.d.ts +7 -5
- package/dist/deployment/index.js +8 -5
- package/dist/deployment/types.d.ts +2 -45
- package/dist/diagram/html-viewer.js +65 -32
- package/dist/diagram/renderer.js +9 -6
- package/dist/diagram/theme.js +4 -0
- package/dist/diagram/types.d.ts +2 -0
- package/dist/doc-metadata/extractors/annotations.js +5 -5
- package/dist/doc-metadata/extractors/cli-commands.js +1 -1
- package/dist/doc-metadata/extractors/mcp-tools.js +1 -2
- package/dist/docs/index.d.ts +28 -1
- package/dist/docs/index.js +95 -28
- package/dist/export/index.d.ts +2 -3
- package/dist/{deployment/targets/cicd-base.d.ts → extensions/cicd/base-target.d.ts} +35 -36
- package/dist/{deployment/targets/cicd-base.js → extensions/cicd/base-target.js} +97 -57
- package/dist/{validation/cicd-detection.d.ts → extensions/cicd/detection.d.ts} +2 -2
- package/dist/{validation/cicd-detection.js → extensions/cicd/detection.js} +13 -1
- package/dist/extensions/cicd/docs/cicd.md +395 -0
- package/dist/extensions/cicd/index.d.ts +10 -0
- package/dist/extensions/cicd/index.js +10 -0
- package/dist/extensions/cicd/register.d.ts +11 -0
- package/dist/extensions/cicd/register.js +62 -0
- package/dist/extensions/cicd/rules.d.ts +30 -0
- package/dist/{validation/cicd-rules.js → extensions/cicd/rules.js} +60 -56
- package/dist/extensions/cicd/tag-handler.d.ts +14 -0
- package/dist/extensions/cicd/tag-handler.js +488 -0
- package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-docker.d.ts +1 -1
- package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-matrix.d.ts +1 -1
- package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-multi-env.d.ts +1 -1
- package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-test-deploy.d.ts +1 -1
- package/dist/extensions/index.d.ts +12 -0
- package/dist/extensions/index.js +12 -0
- package/dist/extensions/inngest/dev-mode.d.ts +9 -0
- package/dist/extensions/inngest/dev-mode.js +213 -0
- package/dist/{generator/inngest.d.ts → extensions/inngest/generator.d.ts} +2 -2
- package/dist/{generator/inngest.js → extensions/inngest/generator.js} +4 -4
- package/dist/extensions/inngest/index.d.ts +2 -0
- package/dist/extensions/inngest/index.js +2 -0
- package/dist/extensions/inngest/register.d.ts +6 -0
- package/dist/extensions/inngest/register.js +23 -0
- package/dist/extensions/inngest/templates/ai-agent-durable.d.ts +8 -0
- package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-agent-durable.js +8 -8
- package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-pipeline-durable.d.ts +2 -2
- package/dist/{cli/templates/workflows → extensions/inngest/templates}/ai-pipeline-durable.js +7 -7
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/generator/compile-target-registry.d.ts +20 -0
- package/dist/generator/compile-target-registry.js +20 -0
- package/dist/generator/dev-mode-registry.d.ts +27 -0
- package/dist/generator/dev-mode-registry.js +20 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/jsdoc-parser.d.ts +12 -114
- package/dist/jsdoc-parser.js +57 -362
- package/dist/marketplace/index.d.ts +2 -2
- package/dist/marketplace/index.js +1 -1
- package/dist/marketplace/registry.d.ts +39 -1
- package/dist/marketplace/registry.js +77 -0
- package/dist/marketplace/types.d.ts +76 -3
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/tools-export.js +3 -3
- package/dist/mcp/tools-query.js +17 -11
- package/dist/mcp/tools-template.js +1 -1
- package/dist/parser/tag-registry.d.ts +47 -0
- package/dist/parser/tag-registry.js +57 -0
- package/dist/parser.d.ts +3 -0
- package/dist/parser.js +10 -23
- package/dist/validation/rule-registry.d.ts +36 -0
- package/dist/validation/rule-registry.js +37 -0
- package/dist/validator.js +3 -3
- package/docs/reference/concepts.md +2 -1
- package/docs/reference/deployment.md +21 -0
- package/docs/reference/jsdoc-grammar.md +242 -1
- package/docs/reference/scaffold.md +0 -6
- package/package.json +9 -1
- package/dist/cli/templates/workflows/ai-agent-durable.d.ts +0 -8
- package/dist/export/templates.d.ts +0 -24
- package/dist/export/templates.js +0 -186
- package/dist/validation/cicd-rules.d.ts +0 -62
- /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-docker.js +0 -0
- /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-matrix.js +0 -0
- /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-multi-env.js +0 -0
- /package/dist/{cli/templates/workflows → extensions/cicd/templates}/cicd-test-deploy.js +0 -0
package/dist/cli/commands/dev.js
CHANGED
|
@@ -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 {
|
|
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
|
-
//
|
|
336
|
-
if (options.target
|
|
337
|
-
|
|
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
|
-
|
|
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
|
-
|
|
610
|
-
|
|
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 (
|
|
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
|
|
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
|
|
35
|
-
* flow-weaver export workflow.ts --target
|
|
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
|
|
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
|
|
50
|
-
* flow-weaver export workflows.ts --target
|
|
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
|
|
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
|
|
15
|
-
* flow-weaver export workflow.ts --target
|
|
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
|
|
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
|
|
30
|
-
* flow-weaver export workflows.ts --target
|
|
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
|
|
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
|
|
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
|
|
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' | '
|
|
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<
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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('
|
|
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('"
|
|
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('
|
|
321
|
+
logger.log(` ${logger.bold('When you want to see the structure')}`);
|
|
291
322
|
logger.newline();
|
|
292
|
-
logger.log(`
|
|
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
|
|
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
|
|
302
|
-
logger.log(` flow-weaver
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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?:
|
|
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.
|