@synergenius/flow-weaver 0.15.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,15 @@ import { getFriendlyError } from '../../friendly-errors.js';
14
14
  import { detectProjectModuleFormat } from './doctor.js';
15
15
  import { generateInngestFunction } from '../../generator/inngest.js';
16
16
  import { AnnotationParser } from '../../parser.js';
17
+ /** Show path relative to cwd for cleaner output */
18
+ function displayPath(filePath) {
19
+ const rel = path.relative(process.cwd(), filePath);
20
+ // Use relative if it's shorter and doesn't escape cwd
21
+ if (rel && !rel.startsWith('..') && rel.length < filePath.length) {
22
+ return rel;
23
+ }
24
+ return filePath;
25
+ }
17
26
  /**
18
27
  * Resolve the module format to use for compilation.
19
28
  * If 'auto' or not specified, detect from the project's package.json.
@@ -35,181 +44,181 @@ export async function compileCommand(input, options = {}) {
35
44
  // Resolve module format (auto-detect if not specified)
36
45
  const cwd = process.cwd();
37
46
  const moduleFormat = resolveModuleFormat(format, cwd);
47
+ // If input is a directory, expand to all .ts files recursively
48
+ let pattern = input;
38
49
  try {
39
- // If input is a directory, expand to all .ts files recursively
40
- let pattern = input;
50
+ if (fs.existsSync(input) && fs.statSync(input).isDirectory()) {
51
+ pattern = path.join(input, '**/*.ts');
52
+ }
53
+ }
54
+ catch {
55
+ // Not a valid path, use as glob pattern
56
+ }
57
+ // Find files matching the pattern, filter to actual files only
58
+ const allFiles = await glob(pattern, { absolute: true });
59
+ const files = allFiles.filter((f) => {
41
60
  try {
42
- if (fs.existsSync(input) && fs.statSync(input).isDirectory()) {
43
- pattern = path.join(input, '**/*.ts');
44
- }
61
+ return fs.statSync(f).isFile();
45
62
  }
46
63
  catch {
47
- // Not a valid path, use as glob pattern
64
+ return false;
48
65
  }
49
- // Find files matching the pattern, filter to actual files only
50
- const allFiles = await glob(pattern, { absolute: true });
51
- const files = allFiles.filter((f) => {
52
- try {
53
- return fs.statSync(f).isFile();
54
- }
55
- catch {
56
- return false;
57
- }
58
- });
59
- if (files.length === 0) {
60
- throw new Error(`No files found matching pattern: ${input}`);
61
- }
62
- logger.section('Compiling Workflows');
66
+ });
67
+ if (files.length === 0) {
68
+ throw new Error(`No files found matching pattern: ${input}`);
69
+ }
70
+ const totalTimer = logger.timer();
71
+ logger.section('Compiling Workflows');
72
+ if (verbose) {
63
73
  logger.info(`Found ${files.length} file(s)`);
64
74
  logger.info(`Module format: ${moduleFormat.toUpperCase()}`);
65
75
  if (sourceMap) {
66
76
  logger.info('Source maps: enabled');
67
77
  }
68
- if (verbose) {
69
- files.forEach((file) => logger.info(` - ${file}`));
70
- }
78
+ files.forEach((file) => logger.info(` ${displayPath(file)}`));
71
79
  logger.newline();
72
- let successCount = 0;
73
- let errorCount = 0;
74
- for (let i = 0; i < files.length; i++) {
75
- const file = files[i];
76
- const fileName = path.basename(file);
77
- logger.progress(i + 1, files.length, fileName);
78
- try {
79
- // Skip files without @flowWeaver annotations before parsing
80
- const rawSource = fs.readFileSync(file, 'utf8');
81
- if (!rawSource.includes('@flowWeaver')) {
82
- if (verbose) {
83
- logger.info(` Skipped ${fileName} (no @flowWeaver annotations)`);
84
- }
85
- continue;
86
- }
87
- // Parse the workflow
88
- const parseResult = await parseWorkflow(file, { workflowName });
89
- if (parseResult.warnings.length > 0) {
90
- parseResult.warnings.forEach((w) => logger.warn(` ${w}`));
80
+ }
81
+ let successCount = 0;
82
+ let errorCount = 0;
83
+ for (let i = 0; i < files.length; i++) {
84
+ const file = files[i];
85
+ const fileName = path.basename(file);
86
+ const fileTimer = logger.timer();
87
+ try {
88
+ // Skip files without @flowWeaver annotations before parsing
89
+ const rawSource = fs.readFileSync(file, 'utf8');
90
+ if (!rawSource.includes('@flowWeaver')) {
91
+ if (verbose) {
92
+ logger.info(` ${logger.dim('skip')} ${fileName} ${logger.dim('(no @flowWeaver annotations)')}`);
91
93
  }
92
- if (parseResult.errors.length > 0) {
93
- // Skip non-workflow files silently (only error is "No workflows found")
94
- const isNonWorkflowFile = parseResult.errors.length === 1 &&
95
- typeof parseResult.errors[0] === 'string' &&
96
- parseResult.errors[0].startsWith('No workflows found');
97
- if (isNonWorkflowFile) {
98
- if (verbose) {
99
- logger.info(` Skipped ${fileName} (no workflow)`);
100
- }
101
- continue;
94
+ continue;
95
+ }
96
+ // Parse the workflow
97
+ const parseResult = await parseWorkflow(file, { workflowName });
98
+ if (parseResult.warnings.length > 0 && verbose) {
99
+ logger.warn(`Parse warnings in ${fileName}:`);
100
+ parseResult.warnings.forEach((w) => logger.warn(` ${w}`));
101
+ }
102
+ if (parseResult.errors.length > 0) {
103
+ // Skip non-workflow files silently (only error is "No workflows found")
104
+ const isNonWorkflowFile = parseResult.errors.length === 1 &&
105
+ typeof parseResult.errors[0] === 'string' &&
106
+ parseResult.errors[0].startsWith('No workflows found');
107
+ if (isNonWorkflowFile) {
108
+ if (verbose) {
109
+ logger.info(` ${logger.dim('skip')} ${fileName} ${logger.dim('(no workflow)')}`);
102
110
  }
103
- logger.error(`Failed to parse ${fileName}:`);
104
- parseResult.errors.forEach((err) => logger.error(` ${err}`));
105
- errorCount++;
106
111
  continue;
107
112
  }
108
- // Validate the AST (especially for --strict mode)
109
- if (strict) {
110
- const validation = validator.validate(parseResult.ast, { strictMode: true });
111
- if (validation.errors.length > 0) {
112
- logger.error(`Validation errors in ${fileName}:`);
113
- validation.errors.forEach((err) => {
114
- const friendly = getFriendlyError(err);
115
- if (friendly) {
116
- const loc = err.location ? `[line ${err.location.line}] ` : '';
117
- logger.error(` ${loc}${friendly.title}: ${friendly.explanation}`);
118
- logger.info(` How to fix: ${friendly.fix}`);
119
- if (err.docUrl) {
120
- logger.info(` See: ${err.docUrl}`);
121
- }
122
- }
123
- else {
124
- let msg = ` - ${err.message}`;
125
- if (err.node) {
126
- msg += ` (node: ${err.node})`;
127
- }
128
- logger.error(msg);
129
- if (err.docUrl) {
130
- logger.info(` See: ${err.docUrl}`);
131
- }
113
+ logger.error(` ${fileName}`);
114
+ parseResult.errors.forEach((err) => logger.error(` ${err}`));
115
+ errorCount++;
116
+ continue;
117
+ }
118
+ // Validate the AST (especially for --strict mode)
119
+ if (strict) {
120
+ const validation = validator.validate(parseResult.ast, { strictMode: true });
121
+ if (validation.errors.length > 0) {
122
+ logger.error(` ${fileName}`);
123
+ validation.errors.forEach((err) => {
124
+ const friendly = getFriendlyError(err);
125
+ if (friendly) {
126
+ const loc = err.location ? `[line ${err.location.line}] ` : '';
127
+ logger.error(` ${loc}${friendly.title}: ${friendly.explanation}`);
128
+ logger.warn(` How to fix: ${friendly.fix}`);
129
+ if (err.docUrl) {
130
+ logger.warn(` See: ${err.docUrl}`);
132
131
  }
133
- });
134
- errorCount++;
135
- continue;
136
- }
137
- if (validation.warnings.length > 0 && verbose) {
138
- validation.warnings.forEach((warn) => {
139
- const friendly = getFriendlyError(warn);
140
- if (friendly) {
141
- const loc = warn.location ? `[line ${warn.location.line}] ` : '';
142
- logger.warn(` ${loc}${friendly.title}: ${friendly.explanation}`);
143
- logger.info(` How to fix: ${friendly.fix}`);
132
+ }
133
+ else {
134
+ let msg = ` ${err.message}`;
135
+ if (err.node) {
136
+ msg += ` (node: ${err.node})`;
144
137
  }
145
- else {
146
- logger.warn(` ${warn.message}`);
138
+ logger.error(msg);
139
+ if (err.docUrl) {
140
+ logger.warn(` See: ${err.docUrl}`);
147
141
  }
148
- });
149
- }
142
+ }
143
+ });
144
+ errorCount++;
145
+ continue;
150
146
  }
151
- // Read original source
152
- const sourceCode = fs.readFileSync(file, 'utf8');
153
- // Generate code in-place (preserves types, interfaces, etc.)
154
- const result = generateInPlace(sourceCode, parseResult.ast, { production, moduleFormat, inlineRuntime, sourceFile: file, skipParamReturns: clean });
155
- // Write back to original file (skip in dry-run mode)
156
- if (!dryRun) {
157
- fs.writeFileSync(file, result.code, 'utf8');
158
- // Generate source map if requested
159
- if (sourceMap) {
160
- const mapResult = generateCode(parseResult.ast, {
161
- production,
162
- sourceMap: true,
163
- moduleFormat,
164
- });
165
- if (mapResult.sourceMap) {
166
- const mapPath = file + '.map';
167
- fs.writeFileSync(mapPath, mapResult.sourceMap, 'utf8');
168
- // Append sourceMappingURL comment to the compiled file
169
- const sourceMappingComment = `\n//# sourceMappingURL=${path.basename(mapPath)}\n`;
170
- if (!result.code.includes('//# sourceMappingURL=')) {
171
- fs.appendFileSync(file, sourceMappingComment, 'utf8');
172
- }
173
- if (verbose) {
174
- logger.info(` Source map: ${mapPath}`);
175
- }
147
+ if (validation.warnings.length > 0 && verbose) {
148
+ validation.warnings.forEach((warn) => {
149
+ const friendly = getFriendlyError(warn);
150
+ if (friendly) {
151
+ const loc = warn.location ? `[line ${warn.location.line}] ` : '';
152
+ logger.warn(` ${loc}${friendly.title}: ${friendly.explanation}`);
153
+ logger.warn(` How to fix: ${friendly.fix}`);
176
154
  }
177
- }
155
+ else {
156
+ logger.warn(` ${warn.message}`);
157
+ }
158
+ });
178
159
  }
179
- if (dryRun) {
180
- if (result.hasChanges) {
181
- logger.success(` Would compile: ${file}`);
182
- }
183
- else if (verbose) {
184
- logger.info(` No changes: ${file}`);
160
+ }
161
+ // Read original source
162
+ const sourceCode = fs.readFileSync(file, 'utf8');
163
+ // Generate code in-place (preserves types, interfaces, etc.)
164
+ const result = generateInPlace(sourceCode, parseResult.ast, { production, moduleFormat, inlineRuntime, sourceFile: file, skipParamReturns: clean });
165
+ // Write back to original file (skip in dry-run mode)
166
+ if (!dryRun) {
167
+ fs.writeFileSync(file, result.code, 'utf8');
168
+ // Generate source map if requested
169
+ if (sourceMap) {
170
+ const mapResult = generateCode(parseResult.ast, {
171
+ production,
172
+ sourceMap: true,
173
+ moduleFormat,
174
+ });
175
+ if (mapResult.sourceMap) {
176
+ const mapPath = file + '.map';
177
+ fs.writeFileSync(mapPath, mapResult.sourceMap, 'utf8');
178
+ const sourceMappingComment = `\n//# sourceMappingURL=${path.basename(mapPath)}\n`;
179
+ if (!result.code.includes('//# sourceMappingURL=')) {
180
+ fs.appendFileSync(file, sourceMappingComment, 'utf8');
181
+ }
182
+ if (verbose) {
183
+ logger.info(` source map: ${displayPath(mapPath)}`);
184
+ }
185
185
  }
186
186
  }
187
- else if (result.hasChanges) {
188
- logger.success(` Compiled: ${file}`);
187
+ }
188
+ const timing = logger.dim(fileTimer.elapsed());
189
+ const filePrint = displayPath(file);
190
+ if (dryRun) {
191
+ if (result.hasChanges) {
192
+ logger.success(`${filePrint} ${timing} ${logger.dim('(dry run)')}`);
189
193
  }
190
- else if (verbose) {
191
- logger.info(` No changes: ${file}`);
194
+ else {
195
+ logger.log(` ${filePrint} ${logger.dim('(no changes, dry run)')}`);
192
196
  }
193
- successCount++;
194
197
  }
195
- catch (error) {
196
- logger.error(`Failed to compile ${fileName}: ${getErrorMessage(error)}`);
197
- errorCount++;
198
+ else if (result.hasChanges) {
199
+ logger.success(`${filePrint} ${timing}`);
198
200
  }
201
+ else if (verbose) {
202
+ logger.info(` ${filePrint} ${logger.dim('no changes')}`);
203
+ }
204
+ successCount++;
199
205
  }
200
- // Summary
201
- logger.newline();
202
- logger.section('Summary');
203
- logger.success(`${successCount} file(s) compiled successfully`);
204
- if (errorCount > 0) {
205
- throw new Error(`${errorCount} file(s) failed to compile`);
206
+ catch (error) {
207
+ logger.error(` ${fileName}: ${getErrorMessage(error)}`);
208
+ errorCount++;
206
209
  }
207
210
  }
208
- catch (error) {
209
- if (error instanceof Error && error.message.includes('file(s) failed')) {
210
- throw error;
211
- }
212
- throw new Error(`Compilation failed: ${getErrorMessage(error)}`);
211
+ // Summary
212
+ const elapsed = totalTimer.elapsed();
213
+ const formatNote = verbose ? '' : ` (${moduleFormat.toUpperCase()})`;
214
+ const dryRunNote = dryRun ? ' (dry run)' : '';
215
+ logger.newline();
216
+ if (errorCount > 0) {
217
+ logger.log(` ${successCount} compiled, ${errorCount} failed${formatNote} in ${elapsed}${dryRunNote}`);
218
+ throw new Error(`${errorCount} file(s) failed to compile`);
219
+ }
220
+ else {
221
+ logger.log(` ${successCount} file${successCount !== 1 ? 's' : ''} compiled${formatNote} in ${elapsed}${dryRunNote}`);
213
222
  }
214
223
  }
215
224
  /**
@@ -218,10 +227,10 @@ export async function compileCommand(input, options = {}) {
218
227
  export async function compileInngestTarget(input, options) {
219
228
  const filePath = path.resolve(input);
220
229
  if (!fs.existsSync(filePath)) {
221
- throw new Error(`File not found: ${filePath}`);
230
+ throw new Error(`File not found: ${displayPath(filePath)}`);
222
231
  }
223
232
  logger.section('Compiling to Inngest');
224
- logger.info(`Input: ${filePath}`);
233
+ logger.info(`Input: ${displayPath(filePath)}`);
225
234
  logger.info(`Target: Inngest (per-node step.run)`);
226
235
  logger.newline();
227
236
  const parser = new AnnotationParser();
@@ -262,7 +271,7 @@ export async function compileInngestTarget(input, options) {
262
271
  });
263
272
  const outputPath = filePath.replace(/\.ts$/, '.inngest.ts');
264
273
  if (options.dryRun) {
265
- logger.success(`Would generate: ${outputPath}`);
274
+ logger.success(`Would generate: ${displayPath(outputPath)}`);
266
275
  logger.newline();
267
276
  logger.section('Preview');
268
277
  const lines = code.split('\n');
@@ -274,7 +283,7 @@ export async function compileInngestTarget(input, options) {
274
283
  }
275
284
  else {
276
285
  fs.writeFileSync(outputPath, code, 'utf8');
277
- logger.success(`Compiled: ${outputPath}`);
286
+ logger.success(`Compiled: ${displayPath(outputPath)}`);
278
287
  }
279
288
  logger.newline();
280
289
  logger.section('Summary');
@@ -16,7 +16,15 @@ function timestamp() {
16
16
  const h = String(now.getHours()).padStart(2, '0');
17
17
  const m = String(now.getMinutes()).padStart(2, '0');
18
18
  const s = String(now.getSeconds()).padStart(2, '0');
19
- return `[${h}:${m}:${s}]`;
19
+ return `${h}:${m}:${s}`;
20
+ }
21
+ function cycleSeparator(file) {
22
+ const ts = timestamp();
23
+ const pad = '─'.repeat(40);
24
+ logger.log(`\n ${logger.dim(`─── ${ts} ${pad}`)}`);
25
+ if (file) {
26
+ logger.log(` ${logger.dim('File changed:')} ${path.basename(file)}`);
27
+ }
20
28
  }
21
29
  /**
22
30
  * Parse params from --params or --params-file.
@@ -56,9 +64,10 @@ async function compileAndRun(filePath, params, options) {
56
64
  clean: options.clean,
57
65
  };
58
66
  try {
67
+ const ct = logger.timer();
59
68
  await compileCommand(filePath, compileOpts);
60
69
  if (!options.json) {
61
- logger.success('Compiled successfully');
70
+ logger.success(`Compiled in ${ct.elapsed()}`);
62
71
  }
63
72
  }
64
73
  catch (error) {
@@ -70,7 +79,7 @@ async function compileAndRun(filePath, params, options) {
70
79
  const friendly = getFriendlyError(err);
71
80
  if (friendly) {
72
81
  logger.error(` ${friendly.title}: ${friendly.explanation}`);
73
- logger.info(` How to fix: ${friendly.fix}`);
82
+ logger.warn(` How to fix: ${friendly.fix}`);
74
83
  }
75
84
  else {
76
85
  logger.error(` - ${err.message}`);
@@ -99,8 +108,6 @@ async function compileAndRun(filePath, params, options) {
99
108
  }
100
109
  else {
101
110
  logger.success(`Workflow "${result.functionName}" completed in ${result.executionTime}ms`);
102
- logger.newline();
103
- logger.section('Result');
104
111
  logger.log(JSON.stringify(result.result, null, 2));
105
112
  }
106
113
  return true;
@@ -286,10 +293,8 @@ async function runInngestDevMode(filePath, options) {
286
293
  ignoreInitial: true,
287
294
  });
288
295
  watcher.on('change', async (file) => {
289
- logger.newline();
290
- logger.info(`${timestamp()} File changed: ${path.basename(file)}`);
296
+ cycleSeparator(file);
291
297
  logger.info('Recompiling and restarting server...');
292
- logger.newline();
293
298
  await restartServer();
294
299
  });
295
300
  // Cleanup
@@ -360,9 +365,7 @@ export async function devCommand(input, options = {}) {
360
365
  });
361
366
  watcher.on('change', async (file) => {
362
367
  if (!options.json) {
363
- logger.newline();
364
- logger.info(`${timestamp()} File changed: ${path.basename(file)}`);
365
- logger.newline();
368
+ cycleSeparator(file);
366
369
  }
367
370
  await compileAndRun(filePath, params, options);
368
371
  });
@@ -40,7 +40,12 @@ export async function diffCommand(file1, file2, options = {}) {
40
40
  const diff = WorkflowDiffer.compare(result1.ast, result2.ast);
41
41
  // Output based on format
42
42
  if (diff.identical) {
43
- logger.success('Workflows are identical');
43
+ if (format === 'json') {
44
+ console.log(JSON.stringify({ identical: true }));
45
+ }
46
+ else {
47
+ logger.success('Workflows are identical');
48
+ }
44
49
  }
45
50
  else {
46
51
  // eslint-disable-next-line no-console
@@ -669,37 +669,28 @@ export async function doctorCommand(options = {}) {
669
669
  }
670
670
  else {
671
671
  logger.section('Flow Weaver Doctor');
672
- logger.info(`Checking project at ${cwd}`);
673
- logger.newline();
674
- for (const check of report.checks) {
675
- switch (check.status) {
676
- case 'pass':
677
- logger.success(check.message);
678
- break;
679
- case 'warn':
680
- logger.warn(check.message);
681
- if (check.fix) {
682
- logger.log(` Fix: ${check.fix}`);
683
- }
684
- break;
685
- case 'fail':
686
- logger.error(check.message);
687
- if (check.fix) {
688
- logger.log(` Fix: ${check.fix}`);
689
- }
690
- break;
672
+ const statusIcon = (s) => s === 'pass' ? logger.highlight('pass') : s === 'warn' ? '⚠ warn' : '✗ fail';
673
+ const rows = report.checks.map((check) => [
674
+ check.name,
675
+ check.message,
676
+ statusIcon(check.status),
677
+ ]);
678
+ logger.table(rows);
679
+ // Show fixes for non-passing checks
680
+ const fixable = report.checks.filter((c) => c.status !== 'pass' && c.fix);
681
+ if (fixable.length > 0) {
682
+ logger.newline();
683
+ for (const check of fixable) {
684
+ logger.log(` ${logger.dim(check.name + ':')} ${check.fix}`);
691
685
  }
692
686
  }
693
687
  logger.newline();
694
- logger.section('Module Format');
695
- logger.info(` Detected: ${report.moduleFormat.format.toUpperCase()} (${report.moduleFormat.details})`);
696
- logger.newline();
697
- logger.section('Summary');
688
+ logger.log(` Module format: ${report.moduleFormat.format.toUpperCase()} ${logger.dim(`(${report.moduleFormat.details})`)}`);
698
689
  const parts = [];
699
690
  if (report.summary.pass > 0)
700
691
  parts.push(`${report.summary.pass} passed`);
701
692
  if (report.summary.warn > 0)
702
- parts.push(`${report.summary.warn} warning(s)`);
693
+ parts.push(`${report.summary.warn} warnings`);
703
694
  if (report.summary.fail > 0)
704
695
  parts.push(`${report.summary.fail} failed`);
705
696
  logger.log(` ${parts.join(', ')}`);
@@ -709,7 +700,7 @@ export async function doctorCommand(options = {}) {
709
700
  }
710
701
  else {
711
702
  logger.newline();
712
- logger.error('Some checks failed. Please fix the issues above.');
703
+ logger.error('Fix the issues above to continue.');
713
704
  }
714
705
  }
715
706
  if (!report.ok) {
@@ -39,6 +39,7 @@ export async function exportCommand(input, options) {
39
39
  throw new Error('--target is required. Install a target pack (e.g. npm install flowweaver-pack-lambda)');
40
40
  }
41
41
  const isDryRun = options.dryRun ?? false;
42
+ const t = logger.timer();
42
43
  const isMulti = options.multi ?? false;
43
44
  const workflowsList = options.workflows
44
45
  ? options.workflows.split(',').map((w) => w.trim())
@@ -98,6 +99,7 @@ export async function exportCommand(input, options) {
98
99
  else {
99
100
  logger.success(`Exported workflow "${result.workflow}" for ${result.target}`);
100
101
  }
102
+ logger.info(`done in ${t.elapsed()}`);
101
103
  }
102
104
  logger.newline();
103
105
  logger.section(isDryRun ? 'Files That Would Be Generated' : 'Generated Files');
@@ -6,7 +6,9 @@ import { generateGrammarDiagrams, getAllGrammars, serializedToEBNF, } from '../.
6
6
  import { logger } from '../utils/logger.js';
7
7
  import { getErrorMessage } from '../../utils/error-utils.js';
8
8
  export async function grammarCommand(options = {}) {
9
- const { format = 'html', output } = options;
9
+ // Default to ebnf on TTY (readable in terminal), html when writing to file
10
+ const defaultFormat = options.output ? 'html' : (process.stdout.isTTY ? 'ebnf' : 'html');
11
+ const { format = defaultFormat, output } = options;
10
12
  try {
11
13
  let content;
12
14
  if (format === 'ebnf') {
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Persona-specific logic for the init command.
3
+ * Types, use-case mappings, template selection, and post-scaffold output.
4
+ */
5
+ export type PersonaId = 'nocode' | 'vibecoder' | 'lowcode' | 'expert';
6
+ export type UseCaseId = 'data' | 'ai' | 'api' | 'automation' | 'cicd' | 'minimal';
7
+ export declare const PERSONA_CHOICES: {
8
+ value: PersonaId;
9
+ name: string;
10
+ description: string;
11
+ }[];
12
+ /** One-liner printed after persona selection to set expectations. null = skip. */
13
+ export declare const PERSONA_CONFIRMATIONS: Record<PersonaId, string | null>;
14
+ export declare const USE_CASE_CHOICES: {
15
+ value: UseCaseId;
16
+ name: string;
17
+ description: string;
18
+ }[];
19
+ export interface UseCaseMapping {
20
+ default: string;
21
+ all: string[];
22
+ }
23
+ export declare const USE_CASE_TEMPLATES: Record<UseCaseId, UseCaseMapping>;
24
+ /**
25
+ * Select the template for a given persona and use-case.
26
+ * For lowcode, returns the full list when the category has multiple templates.
27
+ * For nocode/vibecoder, always returns the single default.
28
+ */
29
+ export declare function selectTemplateForPersona(persona: PersonaId, useCase: UseCaseId): {
30
+ template: string;
31
+ choices?: string[];
32
+ };
33
+ /**
34
+ * Build the template sub-choice list for inquirer, when lowcode needs to pick
35
+ * from multiple templates within a use-case category.
36
+ */
37
+ export declare function getTemplateSubChoices(templateIds: string[]): Array<{
38
+ value: string;
39
+ name: string;
40
+ description: string;
41
+ }>;
42
+ /**
43
+ * Extract @path annotations from generated workflow code and format as a
44
+ * visual flow line. Falls back to extracting @node labels if no @path found.
45
+ */
46
+ export declare function extractWorkflowPreview(workflowCode: string): string | null;
47
+ export declare const FILE_DESCRIPTIONS: Record<string, string>;
48
+ export declare function generateReadme(projectName: string, persona: PersonaId, _template: string): string;
49
+ export declare function generateExampleWorkflow(projectName: string): string;
50
+ export interface PrintNextStepsOptions {
51
+ projectName: string;
52
+ persona: PersonaId;
53
+ template: string;
54
+ displayDir: string | null;
55
+ installSkipped: boolean;
56
+ workflowCode: string | null;
57
+ workflowFile: string;
58
+ mcpConfigured?: string[];
59
+ /** When true, skip persona-specific guidance (the agent handles it) */
60
+ agentLaunched?: boolean;
61
+ }
62
+ export declare function printNextSteps(opts: PrintNextStepsOptions): void;
63
+ /** Maps each persona to the fw_context preset used for agent knowledge bootstrap. */
64
+ export declare const AGENT_CONTEXT_PRESETS: Record<PersonaId, string>;
65
+ /**
66
+ * Generate the initial prompt for a CLI agent (Claude Code, Codex).
67
+ * Interpolates project name and template into the persona-specific template.
68
+ */
69
+ export declare function generateAgentPrompt(projectName: string, persona: PersonaId, template: string): string;
70
+ /**
71
+ * Generate a shorter prompt suitable for pasting into a GUI editor.
72
+ * Persona-aware but more concise than the full agent prompt.
73
+ */
74
+ export declare function generateEditorPrompt(projectName: string, persona: PersonaId, template: string): string;
75
+ /**
76
+ * Generate the full content for a PROJECT_SETUP.md file.
77
+ * Includes the agent prompt plus project context.
78
+ */
79
+ export declare function generateSetupPromptFile(projectName: string, persona: PersonaId, template: string, filesCreated: string[]): string;
80
+ /**
81
+ * Print a copyable prompt in a bordered box.
82
+ * Long lines are word-wrapped to fit within the box.
83
+ */
84
+ export declare function printCopyablePrompt(prompt: string): void;
85
+ /** Default for the "Launch agent?" confirm prompt per persona */
86
+ export declare const AGENT_LAUNCH_DEFAULTS: Record<PersonaId, boolean>;
87
+ //# sourceMappingURL=init-personas.d.ts.map