@agentlang/cli 0.11.9 → 0.12.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.
package/out/main.js CHANGED
@@ -1,4 +1,3 @@
1
- import chalk from 'chalk';
2
1
  import { Command } from 'commander';
3
2
  import { NodeFileSystem } from 'langium/node';
4
3
  import * as path from 'node:path';
@@ -9,6 +8,10 @@ import { initializeProject } from './utils/projectInitializer.js';
9
8
  import { existsSync, readFileSync } from 'node:fs';
10
9
  import { fileURLToPath } from 'node:url';
11
10
  import { dirname, join } from 'node:path';
11
+ import { renderToString } from 'ink';
12
+ import React from 'react';
13
+ import Help from './ui/components/Help.js';
14
+ import { ui, ansi } from './ui/index.js';
12
15
  const __filename = fileURLToPath(import.meta.url);
13
16
  const __dirname = dirname(__filename);
14
17
  let agPath = 'agentlang';
@@ -58,8 +61,7 @@ function getDefaultRepoUrl(appName) {
58
61
  }
59
62
  async function promptAndPushRepository(git, appName) {
60
63
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
61
- // eslint-disable-next-line no-console
62
- console.log(chalk.dim('Skipping git push prompt (non-interactive terminal).'));
64
+ ui.dim('Skipping git push prompt (non-interactive terminal).');
63
65
  return;
64
66
  }
65
67
  const rl = createInterface({
@@ -67,14 +69,14 @@ async function promptAndPushRepository(git, appName) {
67
69
  output: process.stdout,
68
70
  });
69
71
  try {
70
- const pushAnswer = (await rl.question(chalk.cyan('Would you like to push this repo now? (y/N) ')))
72
+ const pushAnswer = (await rl.question(ansi.cyan('Would you like to push this repo now? (y/N) ')))
71
73
  .trim()
72
74
  .toLowerCase();
73
75
  if (pushAnswer !== 'y' && pushAnswer !== 'yes') {
74
76
  return;
75
77
  }
76
78
  const defaultRepoUrl = getDefaultRepoUrl(appName);
77
- const repoUrlInputPromise = rl.question(chalk.cyan('Repository URL: '));
79
+ const repoUrlInputPromise = rl.question(ansi.cyan('Repository URL: '));
78
80
  rl.write(defaultRepoUrl);
79
81
  const repoUrlInput = await repoUrlInputPromise;
80
82
  const repoUrl = repoUrlInput.trim() || defaultRepoUrl;
@@ -88,12 +90,10 @@ async function promptAndPushRepository(git, appName) {
88
90
  }
89
91
  const currentBranch = (await git.branch()).current || 'main';
90
92
  await git.push(['-u', 'origin', currentBranch]);
91
- // eslint-disable-next-line no-console
92
- console.log(`${chalk.green('✓')} Pushed to ${chalk.cyan(repoUrl)}`);
93
+ ui.step('✓', 'Pushed to', repoUrl);
93
94
  }
94
95
  catch (error) {
95
- // eslint-disable-next-line no-console
96
- console.log(chalk.yellow(`⚠️ Skipped pushing repository: ${error instanceof Error ? error.message : String(error)}`));
96
+ ui.warn(`Skipped pushing repository: ${error instanceof Error ? error.message : String(error)}`);
97
97
  }
98
98
  finally {
99
99
  rl.close();
@@ -103,33 +103,41 @@ async function promptAndPushRepository(git, appName) {
103
103
  export const initCommand = async (appName, options) => {
104
104
  const currentDir = process.cwd();
105
105
  const targetDir = join(currentDir, appName);
106
+ ui.blank();
107
+ ui.banner('Initialize App');
108
+ ui.blank();
109
+ ui.label('App', appName, 'cyan');
110
+ ui.label('Location', targetDir);
111
+ ui.blank();
106
112
  try {
107
113
  await initializeProject(targetDir, appName, {
108
114
  prompt: options === null || options === void 0 ? void 0 : options.prompt,
109
- silent: false, // Maintain logs for CLI
115
+ silent: false,
110
116
  });
111
- // Change to the app directory (for CLI context)
112
117
  try {
113
118
  process.chdir(targetDir);
114
- // eslint-disable-next-line no-console
115
- console.log(chalk.cyan(`\n📂 Changed directory to ${chalk.bold(appName)}`));
116
119
  }
117
120
  catch (_a) {
118
121
  // Ignore if can't change directory
119
122
  }
120
- // eslint-disable-next-line no-console
121
- console.log(chalk.green('\n✨ Successfully initialized Agentlang application!'));
122
- // eslint-disable-next-line no-console
123
- console.log(chalk.dim('\nNext steps:'));
124
- // eslint-disable-next-line no-console
125
- console.log(chalk.dim(' 1. Add your application logic to src/core.al'));
126
- // eslint-disable-next-line no-console
127
- console.log(chalk.dim(' 2. Run your app with: ') + chalk.cyan('agent run'));
128
- // eslint-disable-next-line no-console
129
- console.log(chalk.dim(' 3. Or start Studio UI with: ') + chalk.cyan('agent studio'));
123
+ ui.blank();
124
+ ui.divider(50);
125
+ ui.success(`${appName} initialized successfully!`);
126
+ ui.blank();
127
+ ui.dim('Next steps:');
128
+ ui.dim(' 1. Add your application logic to src/core.al');
129
+ ui.row([
130
+ { text: ' 2. Run your app with: ', dimColor: true },
131
+ { text: 'agent run', color: 'cyan' },
132
+ ]);
133
+ ui.row([
134
+ { text: ' 3. Or start Studio UI with: ', dimColor: true },
135
+ { text: 'agent studio', color: 'cyan' },
136
+ ]);
137
+ ui.divider(50);
138
+ ui.blank();
130
139
  // Handle interactive git push
131
140
  const git = simpleGit(targetDir);
132
- // Check if git is initialized (initializeProject does it, but let's be safe)
133
141
  if (await git.checkIsRepo()) {
134
142
  await promptAndPushRepository(git, appName);
135
143
  }
@@ -138,121 +146,36 @@ export const initCommand = async (appName, options) => {
138
146
  }
139
147
  }
140
148
  catch (error) {
141
- // eslint-disable-next-line no-console
142
- console.error(chalk.red('❌ Error initializing application:'), error instanceof Error ? error.message : error);
149
+ ui.error(`Error initializing application: ${error instanceof Error ? error.message : String(error)}`);
143
150
  process.exit(1);
144
151
  }
145
152
  };
146
- // Custom help formatter
147
- function customHelp() {
148
- const gradient = [chalk.hex('#00D9FF'), chalk.hex('#00C4E6'), chalk.hex('#00AFCC'), chalk.hex('#009AB3')];
149
- const header = `
150
- ${gradient[0]('█████╗')} ${gradient[1](' ██████╗')} ${gradient[2]('███████╗')}${gradient[3]('███╗ ██╗')}${gradient[0]('████████╗')}
151
- ${gradient[0]('██╔══██╗')}${gradient[1]('██╔════╝')} ${gradient[2]('██╔════╝')}${gradient[3]('████╗ ██║')}${gradient[0]('╚══██╔══╝')}
152
- ${gradient[0]('███████║')}${gradient[1]('██║ ███╗')}${gradient[2]('█████╗')} ${gradient[3]('██╔██╗ ██║')}${gradient[0](' ██║')}
153
- ${gradient[0]('██╔══██║')}${gradient[1]('██║ ██║')}${gradient[2]('██╔══╝')} ${gradient[3]('██║╚██╗██║')}${gradient[0](' ██║')}
154
- ${gradient[0]('██║ ██║')}${gradient[1]('╚██████╔╝')}${gradient[2]('███████╗')}${gradient[3]('██║ ╚████║')}${gradient[0](' ██║')}
155
- ${gradient[0]('╚═╝ ╚═╝')} ${gradient[1]('╚═════╝')} ${gradient[2]('╚══════╝')}${gradient[3]('╚═╝ ╚═══╝')}${gradient[0](' ╚═╝')}
156
-
157
- ${chalk.bold.white('Agentlang CLI')} ${chalk.dim(`v${packageVersion}`)}
158
- ${chalk.dim('CLI for all things Agentlang')}
159
- `;
160
- const usage = `
161
- ${chalk.bold.white('USAGE')}
162
- ${chalk.dim('$')} ${chalk.cyan('agent')} ${chalk.yellow('<command>')} ${chalk.dim('[options]')}
163
-
164
- ${chalk.bold.white('COMMANDS')}
165
-
166
- ${chalk.cyan.bold('init')} ${chalk.dim('<appname>')}
167
- ${chalk.white('▸')} Initialize a new Agentlang application
168
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
169
- ${chalk.yellow('OPTIONS')}
170
- ${chalk.cyan('-p, --prompt')} ${chalk.dim('<description>')} Description or prompt for the application
171
-
172
- ${chalk.cyan.bold('run')} ${chalk.dim('[file]')}
173
- ${chalk.white('▸')} Load and execute an Agentlang module
174
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
175
- ${chalk.yellow('OPTIONS')}
176
- ${chalk.cyan('-c, --config')} ${chalk.dim('<file>')} Configuration file path
177
-
178
- ${chalk.cyan.bold('repl')} ${chalk.dim('[directory]')}
179
- ${chalk.white('▸')} Start interactive REPL environment
180
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
181
- ${chalk.yellow('OPTIONS')}
182
- ${chalk.cyan('-w, --watch')} Watch files and reload automatically
183
- ${chalk.cyan('-q, --quiet')} Suppress startup messages
184
-
185
- ${chalk.cyan.bold('doc')} ${chalk.dim('[file]')}
186
- ${chalk.white('▸')} Generate API documentation (Swagger/OpenAPI)
187
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
188
- ${chalk.yellow('OPTIONS')}
189
- ${chalk.cyan('-h, --outputHtml')} ${chalk.dim('<file>')} Generate HTML documentation
190
- ${chalk.cyan('-p, --outputPostman')} ${chalk.dim('<file>')} Generate Postman collection
191
-
192
- ${chalk.cyan.bold('parseAndValidate')} ${chalk.dim('<file>')}
193
- ${chalk.white('▸')} Parse and validate Agentlang source code
194
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
195
- ${chalk.yellow('OPTIONS')}
196
- ${chalk.cyan('-d, --destination')} ${chalk.dim('<dir>')} Output directory
197
-
198
- ${chalk.cyan.bold('ui-gen')} ${chalk.dim('[spec-file]')}
199
- ${chalk.white('▸')} Generate UI from specification ${chalk.dim('(requires Anthropic API key)')}
200
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
201
- ${chalk.yellow('OPTIONS')}
202
- ${chalk.cyan('-d, --directory')} ${chalk.dim('<dir>')} Target directory
203
- ${chalk.cyan('-k, --api-key')} ${chalk.dim('<key>')} Anthropic API key
204
- ${chalk.cyan('-p, --push')} Commit and push to git
205
- ${chalk.cyan('-m, --message')} ${chalk.dim('<text>')} Update instructions
206
-
207
- ${chalk.cyan.bold('fork')} ${chalk.dim('<source> [name]')}
208
- ${chalk.white('▸')} Fork an app from a local directory or git repository
209
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
210
- ${chalk.yellow('OPTIONS')}
211
- ${chalk.cyan('-b, --branch')} ${chalk.dim('<branch>')} Git branch to clone (for git URLs)
212
- ${chalk.cyan('-u, --username')} ${chalk.dim('<username>')} GitHub username for authenticated access
213
- ${chalk.cyan('-t, --token')} ${chalk.dim('<token>')} GitHub token for authenticated access
214
-
215
- ${chalk.cyan.bold('import')} ${chalk.dim('<source> [name]')}
216
- ${chalk.white('▸')} Import an app (alias for fork)
217
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
218
- ${chalk.yellow('OPTIONS')}
219
- ${chalk.cyan('-b, --branch')} ${chalk.dim('<branch>')} Git branch to clone (for git URLs)
220
- ${chalk.cyan('-u, --username')} ${chalk.dim('<username>')} GitHub username for authenticated access
221
- ${chalk.cyan('-t, --token')} ${chalk.dim('<token>')} GitHub token for authenticated access
222
-
223
- ${chalk.cyan.bold('studio')} ${chalk.dim('[path]')}
224
- ${chalk.white('▸')} Start Agentlang Studio with local server
225
- ${chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
226
- ${chalk.yellow('OPTIONS')}
227
- ${chalk.cyan('-p, --port')} ${chalk.dim('<port>')} Port to run Studio server on (default: 4000)
228
-
229
- ${chalk.bold.white('GLOBAL OPTIONS')}
230
- ${chalk.cyan('-h, --help')} Display help information
231
- ${chalk.cyan('-V, --version')} Display version number
232
-
233
- ${chalk.bold.white('LEARN MORE')}
234
- ${chalk.white('Docs')} ${chalk.cyan('https://github.com/agentlang/agentlang-cli')}
235
- ${chalk.white('Issues')} ${chalk.cyan('https://github.com/agentlang/agentlang-cli/issues')}
236
-
237
- ${chalk.dim('Run')} ${chalk.cyan('agent <command> --help')} ${chalk.dim('for detailed command information')}
238
- `;
239
- return header + usage;
240
- }
241
153
  export default function () {
242
154
  const program = new Command();
243
155
  // Configure program
244
156
  program
245
157
  .name('agent')
246
- .description(chalk.gray('CLI for all things Agentlang'))
158
+ .description('CLI for all things Agentlang')
247
159
  .version(packageVersion, '-V, --version', 'Display version number')
248
- .helpOption('-h, --help', 'Show help information')
160
+ .helpOption(false)
249
161
  .helpCommand(false)
250
162
  .configureHelp({
251
163
  sortSubcommands: true,
252
164
  sortOptions: true,
253
165
  });
254
- // Override help display
255
- program.helpInformation = customHelp;
166
+ // Use ink-rendered help via renderToString
167
+ program.helpInformation = () => {
168
+ return renderToString(React.createElement(Help, { version: packageVersion }), {
169
+ columns: process.stdout.columns || 80,
170
+ });
171
+ };
172
+ // Add explicit help flag since we disabled the built-in one
173
+ program.option('-h, --help', 'Show help information');
174
+ program.on('option:help', () => {
175
+ // eslint-disable-next-line no-console
176
+ console.log(program.helpInformation());
177
+ process.exit(0);
178
+ });
256
179
  const fileExtensions = AgentlangLanguageMetaData.fileExtensions.join(', ');
257
180
  program
258
181
  .command('init')
@@ -260,7 +183,7 @@ export default function () {
260
183
  .option('-p, --prompt <description>', 'Description or prompt for the application')
261
184
  .description('Initialize a new Agentlang application')
262
185
  .addHelpText('after', `
263
- ${chalk.bold.white('DESCRIPTION')}
186
+ ${ui.format.boldWhite('DESCRIPTION')}
264
187
  Creates a new Agentlang application with the necessary project structure.
265
188
  This command will create:
266
189
  • package.json with your app name and version
@@ -270,18 +193,18 @@ ${chalk.bold.white('DESCRIPTION')}
270
193
  The command checks if the directory is already initialized by looking for
271
194
  existing package.json or .al files (excluding config.al).
272
195
 
273
- ${chalk.bold.white('EXAMPLES')}
274
- ${chalk.dim('Initialize a new app called CarDealership')}
275
- ${chalk.dim('$')} ${chalk.cyan('agent init CarDealership')}
196
+ ${ui.format.boldWhite('EXAMPLES')}
197
+ ${ui.format.dim('Initialize a new app called CarDealership')}
198
+ ${ui.format.dim('$')} ${ui.format.cyan('agent init CarDealership')}
276
199
 
277
- ${chalk.dim('Initialize a new e-commerce app')}
278
- ${chalk.dim('$')} ${chalk.cyan('agent init MyShop')}
200
+ ${ui.format.dim('Initialize a new e-commerce app')}
201
+ ${ui.format.dim('$')} ${ui.format.cyan('agent init MyShop')}
279
202
 
280
- ${chalk.dim('Initialize with multiple words (use PascalCase)')}
281
- ${chalk.dim('$')} ${chalk.cyan('agent init InventoryManagement')}
203
+ ${ui.format.dim('Initialize with multiple words (use PascalCase)')}
204
+ ${ui.format.dim('$')} ${ui.format.cyan('agent init InventoryManagement')}
282
205
 
283
- ${chalk.dim('Initialize with a description/prompt')}
284
- ${chalk.dim('$')} ${chalk.cyan('agent init ShowroomApp --prompt "a showroom app"')}
206
+ ${ui.format.dim('Initialize with a description/prompt')}
207
+ ${ui.format.dim('$')} ${ui.format.cyan('agent init ShowroomApp --prompt "a showroom app"')}
285
208
  `)
286
209
  .action(initCommand);
287
210
  program
@@ -290,22 +213,22 @@ ${chalk.bold.white('EXAMPLES')}
290
213
  .option('-c, --config <config>', 'Path to configuration file')
291
214
  .description('Load and execute an Agentlang module')
292
215
  .addHelpText('after', `
293
- ${chalk.bold.white('DESCRIPTION')}
216
+ ${ui.format.boldWhite('DESCRIPTION')}
294
217
  Loads and executes an Agentlang module, starting the runtime environment
295
218
  and initializing all configured services, databases, and integrations.
296
219
 
297
- ${chalk.bold.white('EXAMPLES')}
298
- ${chalk.dim('Run module in current directory')}
299
- ${chalk.dim('$')} ${chalk.cyan('agent run')}
220
+ ${ui.format.boldWhite('EXAMPLES')}
221
+ ${ui.format.dim('Run module in current directory')}
222
+ ${ui.format.dim('$')} ${ui.format.cyan('agent run')}
300
223
 
301
- ${chalk.dim('Run specific module file')}
302
- ${chalk.dim('$')} ${chalk.cyan('agent run ./my-app/main.al')}
224
+ ${ui.format.dim('Run specific module file')}
225
+ ${ui.format.dim('$')} ${ui.format.cyan('agent run ./my-app/main.al')}
303
226
 
304
- ${chalk.dim('Run with custom configuration')}
305
- ${chalk.dim('$')} ${chalk.cyan('agent run ./my-app -c config.json')}
227
+ ${ui.format.dim('Run with custom configuration')}
228
+ ${ui.format.dim('$')} ${ui.format.cyan('agent run ./my-app -c config.json')}
306
229
 
307
- ${chalk.dim('Run module from specific directory')}
308
- ${chalk.dim('$')} ${chalk.cyan('agent run ~/projects/erp-system')}
230
+ ${ui.format.dim('Run module from specific directory')}
231
+ ${ui.format.dim('$')} ${ui.format.cyan('agent run ~/projects/erp-system')}
309
232
  `)
310
233
  .action(runModule);
311
234
  program
@@ -315,26 +238,26 @@ ${chalk.bold.white('EXAMPLES')}
315
238
  .option('-q, --quiet', 'Suppress startup messages')
316
239
  .description('Start interactive REPL environment')
317
240
  .addHelpText('after', `
318
- ${chalk.bold.white('DESCRIPTION')}
241
+ ${ui.format.boldWhite('DESCRIPTION')}
319
242
  Starts an interactive Read-Eval-Print Loop (REPL) environment for
320
243
  Agentlang, allowing you to execute code interactively, test functions,
321
244
  and explore your application in real-time.
322
245
 
323
- ${chalk.bold.white('EXAMPLES')}
324
- ${chalk.dim('Start REPL in current directory')}
325
- ${chalk.dim('$')} ${chalk.cyan('agent repl')}
246
+ ${ui.format.boldWhite('EXAMPLES')}
247
+ ${ui.format.dim('Start REPL in current directory')}
248
+ ${ui.format.dim('$')} ${ui.format.cyan('agent repl')}
326
249
 
327
- ${chalk.dim('Start REPL in specific directory')}
328
- ${chalk.dim('$')} ${chalk.cyan('agent repl ./my-app')}
250
+ ${ui.format.dim('Start REPL in specific directory')}
251
+ ${ui.format.dim('$')} ${ui.format.cyan('agent repl ./my-app')}
329
252
 
330
- ${chalk.dim('Start with file watching enabled')}
331
- ${chalk.dim('$')} ${chalk.cyan('agent repl --watch')}
253
+ ${ui.format.dim('Start with file watching enabled')}
254
+ ${ui.format.dim('$')} ${ui.format.cyan('agent repl --watch')}
332
255
 
333
- ${chalk.dim('Start in quiet mode (no startup messages)')}
334
- ${chalk.dim('$')} ${chalk.cyan('agent repl --quiet')}
256
+ ${ui.format.dim('Start in quiet mode (no startup messages)')}
257
+ ${ui.format.dim('$')} ${ui.format.cyan('agent repl --quiet')}
335
258
 
336
- ${chalk.dim('Combine options for development workflow')}
337
- ${chalk.dim('$')} ${chalk.cyan('agent repl . --watch')}
259
+ ${ui.format.dim('Combine options for development workflow')}
260
+ ${ui.format.dim('$')} ${ui.format.cyan('agent repl . --watch')}
338
261
  `)
339
262
  .action(replCommand);
340
263
  program
@@ -344,26 +267,26 @@ ${chalk.bold.white('EXAMPLES')}
344
267
  .option('-p, --outputPostman <outputPostman>', 'Generate Postman collection')
345
268
  .description('Generate API documentation (Swagger/OpenAPI)')
346
269
  .addHelpText('after', `
347
- ${chalk.bold.white('DESCRIPTION')}
270
+ ${ui.format.boldWhite('DESCRIPTION')}
348
271
  Generates comprehensive API documentation from your Agentlang module
349
272
  in Swagger/OpenAPI format. Supports both HTML and Postman collection
350
273
  output formats for easy API exploration and testing.
351
274
 
352
- ${chalk.bold.white('EXAMPLES')}
353
- ${chalk.dim('Generate OpenAPI spec (outputs to console)')}
354
- ${chalk.dim('$')} ${chalk.cyan('agent doc')}
275
+ ${ui.format.boldWhite('EXAMPLES')}
276
+ ${ui.format.dim('Generate OpenAPI spec (outputs to console)')}
277
+ ${ui.format.dim('$')} ${ui.format.cyan('agent doc')}
355
278
 
356
- ${chalk.dim('Generate HTML documentation')}
357
- ${chalk.dim('$')} ${chalk.cyan('agent doc --outputHtml api-docs.html')}
279
+ ${ui.format.dim('Generate HTML documentation')}
280
+ ${ui.format.dim('$')} ${ui.format.cyan('agent doc --outputHtml api-docs.html')}
358
281
 
359
- ${chalk.dim('Generate Postman collection')}
360
- ${chalk.dim('$')} ${chalk.cyan('agent doc --outputPostman collection.json')}
282
+ ${ui.format.dim('Generate Postman collection')}
283
+ ${ui.format.dim('$')} ${ui.format.cyan('agent doc --outputPostman collection.json')}
361
284
 
362
- ${chalk.dim('Generate both HTML and Postman')}
363
- ${chalk.dim('$')} ${chalk.cyan('agent doc -h docs.html -p collection.json')}
285
+ ${ui.format.dim('Generate both HTML and Postman')}
286
+ ${ui.format.dim('$')} ${ui.format.cyan('agent doc -h docs.html -p collection.json')}
364
287
 
365
- ${chalk.dim('Generate docs for specific module')}
366
- ${chalk.dim('$')} ${chalk.cyan('agent doc ./my-api -h api.html')}
288
+ ${ui.format.dim('Generate docs for specific module')}
289
+ ${ui.format.dim('$')} ${ui.format.cyan('agent doc ./my-api -h api.html')}
367
290
  `)
368
291
  .action(generateDoc);
369
292
  program
@@ -372,20 +295,20 @@ ${chalk.bold.white('EXAMPLES')}
372
295
  .option('-d, --destination <dir>', 'Output directory for generated files')
373
296
  .description('Parse and validate Agentlang source code')
374
297
  .addHelpText('after', `
375
- ${chalk.bold.white('DESCRIPTION')}
298
+ ${ui.format.boldWhite('DESCRIPTION')}
376
299
  Parses and validates an Agentlang source file, checking for syntax
377
300
  errors, lexer issues, and semantic validation problems. Useful for
378
301
  CI/CD pipelines and pre-deployment validation.
379
302
 
380
- ${chalk.bold.white('EXAMPLES')}
381
- ${chalk.dim('Validate a source file')}
382
- ${chalk.dim('$')} ${chalk.cyan('agent parseAndValidate ./src/main.al')}
303
+ ${ui.format.boldWhite('EXAMPLES')}
304
+ ${ui.format.dim('Validate a source file')}
305
+ ${ui.format.dim('$')} ${ui.format.cyan('agent parseAndValidate ./src/main.al')}
383
306
 
384
- ${chalk.dim('Parse and validate with output directory')}
385
- ${chalk.dim('$')} ${chalk.cyan('agent parseAndValidate main.al -d ./out')}
307
+ ${ui.format.dim('Parse and validate with output directory')}
308
+ ${ui.format.dim('$')} ${ui.format.cyan('agent parseAndValidate main.al -d ./out')}
386
309
 
387
- ${chalk.dim('Validate in CI/CD pipeline')}
388
- ${chalk.dim('$')} ${chalk.cyan('agent parseAndValidate app.al && npm run deploy')}
310
+ ${ui.format.dim('Validate in CI/CD pipeline')}
311
+ ${ui.format.dim('$')} ${ui.format.cyan('agent parseAndValidate app.al && npm run deploy')}
389
312
  `)
390
313
  .action(parseAndValidate);
391
314
  program
@@ -397,36 +320,36 @@ ${chalk.bold.white('EXAMPLES')}
397
320
  .option('-m, --message <message>', 'User message for incremental updates')
398
321
  .description('Generate UI from specification (requires Anthropic API key)')
399
322
  .addHelpText('after', `
400
- ${chalk.bold.white('DESCRIPTION')}
323
+ ${ui.format.boldWhite('DESCRIPTION')}
401
324
  Generates a complete UI application from a ui-spec.json specification
402
325
  using AI. Supports incremental updates, allowing you to evolve your UI
403
326
  over time with natural language instructions.
404
327
 
405
- ${chalk.yellow.bold('API KEY REQUIRED')}
406
- Set ${chalk.cyan('ANTHROPIC_API_KEY')} environment variable or use ${chalk.cyan('--api-key')} flag
407
- ${chalk.dim('Get your key at: https://console.anthropic.com')}
328
+ ${ui.format.row([{ text: 'API KEY REQUIRED', color: 'yellow', bold: true }])}
329
+ Set ${ui.format.cyan('ANTHROPIC_API_KEY')} environment variable or use ${ui.format.cyan('--api-key')} flag
330
+ ${ui.format.dim('Get your key at: https://console.anthropic.com')}
408
331
 
409
- ${chalk.bold.white('EXAMPLES')}
410
- ${chalk.dim('Generate UI with auto-detected spec')}
411
- ${chalk.dim('$')} ${chalk.cyan('agent ui-gen')}
332
+ ${ui.format.boldWhite('EXAMPLES')}
333
+ ${ui.format.dim('Generate UI with auto-detected spec')}
334
+ ${ui.format.dim('$')} ${ui.format.cyan('agent ui-gen')}
412
335
 
413
- ${chalk.dim('Generate from specific spec file')}
414
- ${chalk.dim('$')} ${chalk.cyan('agent ui-gen ui-spec.json')}
336
+ ${ui.format.dim('Generate from specific spec file')}
337
+ ${ui.format.dim('$')} ${ui.format.cyan('agent ui-gen ui-spec.json')}
415
338
 
416
- ${chalk.dim('Generate and commit to git')}
417
- ${chalk.dim('$')} ${chalk.cyan('agent ui-gen --push')}
339
+ ${ui.format.dim('Generate and commit to git')}
340
+ ${ui.format.dim('$')} ${ui.format.cyan('agent ui-gen --push')}
418
341
 
419
- ${chalk.dim('Generate in specific directory')}
420
- ${chalk.dim('$')} ${chalk.cyan('agent ui-gen -d ./frontend')}
342
+ ${ui.format.dim('Generate in specific directory')}
343
+ ${ui.format.dim('$')} ${ui.format.cyan('agent ui-gen -d ./frontend')}
421
344
 
422
- ${chalk.dim('Update existing UI with changes')}
423
- ${chalk.dim('$')} ${chalk.cyan('agent ui-gen -m "Add dark mode toggle"')}
345
+ ${ui.format.dim('Update existing UI with changes')}
346
+ ${ui.format.dim('$')} ${ui.format.cyan('agent ui-gen -m "Add dark mode toggle"')}
424
347
 
425
- ${chalk.dim('Incremental update with git push')}
426
- ${chalk.dim('$')} ${chalk.cyan('agent ui-gen -m "Fix login validation" -p')}
348
+ ${ui.format.dim('Incremental update with git push')}
349
+ ${ui.format.dim('$')} ${ui.format.cyan('agent ui-gen -m "Fix login validation" -p')}
427
350
 
428
- ${chalk.dim('Use custom API key')}
429
- ${chalk.dim('$')} ${chalk.cyan('agent ui-gen --api-key sk-ant-...')}
351
+ ${ui.format.dim('Use custom API key')}
352
+ ${ui.format.dim('$')} ${ui.format.cyan('agent ui-gen --api-key sk-ant-...')}
430
353
  `)
431
354
  .action(generateUICommand);
432
355
  program
@@ -438,26 +361,26 @@ ${chalk.bold.white('EXAMPLES')}
438
361
  .option('-t, --token <token>', 'GitHub token for authenticated access')
439
362
  .description('Fork an app from a local directory or git repository')
440
363
  .addHelpText('after', `
441
- ${chalk.bold.white('DESCRIPTION')}
364
+ ${ui.format.boldWhite('DESCRIPTION')}
442
365
  Forks an Agentlang application from a source path (local directory or git URL)
443
366
  into the current workspace. The forked app will be initialized with dependencies
444
367
  installed and a fresh git repository.
445
368
 
446
- ${chalk.bold.white('EXAMPLES')}
447
- ${chalk.dim('Fork from local directory')}
448
- ${chalk.dim('$')} ${chalk.cyan('agent fork ./my-app MyForkedApp')}
369
+ ${ui.format.boldWhite('EXAMPLES')}
370
+ ${ui.format.dim('Fork from local directory')}
371
+ ${ui.format.dim('$')} ${ui.format.cyan('agent fork ./my-app MyForkedApp')}
449
372
 
450
- ${chalk.dim('Fork from GitHub repository')}
451
- ${chalk.dim('$')} ${chalk.cyan('agent fork https://github.com/user/repo.git MyApp')}
373
+ ${ui.format.dim('Fork from GitHub repository')}
374
+ ${ui.format.dim('$')} ${ui.format.cyan('agent fork https://github.com/user/repo.git MyApp')}
452
375
 
453
- ${chalk.dim('Fork from GitHub with specific branch')}
454
- ${chalk.dim('$')} ${chalk.cyan('agent fork https://github.com/user/repo.git MyApp --branch develop')}
376
+ ${ui.format.dim('Fork from GitHub with specific branch')}
377
+ ${ui.format.dim('$')} ${ui.format.cyan('agent fork https://github.com/user/repo.git MyApp --branch develop')}
455
378
 
456
- ${chalk.dim('Fork private repository with authentication')}
457
- ${chalk.dim('$')} ${chalk.cyan('agent fork https://github.com/user/repo.git MyApp -u username -t token')}
379
+ ${ui.format.dim('Fork private repository with authentication')}
380
+ ${ui.format.dim('$')} ${ui.format.cyan('agent fork https://github.com/user/repo.git MyApp -u username -t token')}
458
381
 
459
- ${chalk.dim('Fork using git@ URL')}
460
- ${chalk.dim('$')} ${chalk.cyan('agent fork git@github.com:user/repo.git MyApp')}
382
+ ${ui.format.dim('Fork using git@ URL')}
383
+ ${ui.format.dim('$')} ${ui.format.cyan('agent fork git@github.com:user/repo.git MyApp')}
461
384
  `)
462
385
  .action(forkCommand);
463
386
  program
@@ -469,16 +392,16 @@ ${chalk.bold.white('EXAMPLES')}
469
392
  .option('-t, --token <token>', 'GitHub token for authenticated access')
470
393
  .description('Import an app from a local directory or git repository (alias for fork)')
471
394
  .addHelpText('after', `
472
- ${chalk.bold.white('DESCRIPTION')}
395
+ ${ui.format.boldWhite('DESCRIPTION')}
473
396
  Imports an Agentlang application from a source path. This is an alias for the
474
397
  'fork' command and uses the same functionality.
475
398
 
476
- ${chalk.bold.white('EXAMPLES')}
477
- ${chalk.dim('Import from local directory')}
478
- ${chalk.dim('$')} ${chalk.cyan('agent import ./my-app MyImportedApp')}
399
+ ${ui.format.boldWhite('EXAMPLES')}
400
+ ${ui.format.dim('Import from local directory')}
401
+ ${ui.format.dim('$')} ${ui.format.cyan('agent import ./my-app MyImportedApp')}
479
402
 
480
- ${chalk.dim('Import from GitHub repository')}
481
- ${chalk.dim('$')} ${chalk.cyan('agent import https://github.com/user/repo.git MyApp')}
403
+ ${ui.format.dim('Import from GitHub repository')}
404
+ ${ui.format.dim('$')} ${ui.format.cyan('agent import https://github.com/user/repo.git MyApp')}
482
405
  `)
483
406
  .action(forkCommand);
484
407
  program
@@ -488,7 +411,7 @@ ${chalk.bold.white('EXAMPLES')}
488
411
  .option('--server-only', 'Start only the backend server without opening the UI')
489
412
  .description('Start Agentlang Studio with local server')
490
413
  .addHelpText('after', `
491
- ${chalk.bold.white('DESCRIPTION')}
414
+ ${ui.format.boldWhite('DESCRIPTION')}
492
415
  Starts the Agentlang Design Studio locally for your project. This command:
493
416
  • Starts the Agentlang server (via 'agent run')
494
417
  • Serves the Studio UI on a local web server
@@ -497,21 +420,21 @@ ${chalk.bold.white('DESCRIPTION')}
497
420
  The Studio UI allows you to visually edit Agents, Data Models, and Workflows,
498
421
  with changes saved directly to your project files (.al files, package.json, etc.).
499
422
 
500
- ${chalk.bold.white('EXAMPLES')}
501
- ${chalk.dim('Start Studio in current directory')}
502
- ${chalk.dim('$')} ${chalk.cyan('agent studio')}
423
+ ${ui.format.boldWhite('EXAMPLES')}
424
+ ${ui.format.dim('Start Studio in current directory')}
425
+ ${ui.format.dim('$')} ${ui.format.cyan('agent studio')}
503
426
 
504
- ${chalk.dim('Start Studio for specific project')}
505
- ${chalk.dim('$')} ${chalk.cyan('agent studio ./my-project')}
427
+ ${ui.format.dim('Start Studio for specific project')}
428
+ ${ui.format.dim('$')} ${ui.format.cyan('agent studio ./my-project')}
506
429
 
507
- ${chalk.dim('Start Studio on custom port')}
508
- ${chalk.dim('$')} ${chalk.cyan('agent studio --port 5000')}
430
+ ${ui.format.dim('Start Studio on custom port')}
431
+ ${ui.format.dim('$')} ${ui.format.cyan('agent studio --port 5000')}
509
432
 
510
- ${chalk.dim('Start Studio with path and custom port')}
511
- ${chalk.dim('$')} ${chalk.cyan('agent studio ./monitoring -p 5000')}
433
+ ${ui.format.dim('Start Studio with path and custom port')}
434
+ ${ui.format.dim('$')} ${ui.format.cyan('agent studio ./monitoring -p 5000')}
512
435
 
513
- ${chalk.dim('Start only the backend server (for development)')}
514
- ${chalk.dim('$')} ${chalk.cyan('agent studio --server-only')}
436
+ ${ui.format.dim('Start only the backend server (for development)')}
437
+ ${ui.format.dim('$')} ${ui.format.cyan('agent studio --server-only')}
515
438
  `)
516
439
  .action(studioCommand);
517
440
  program.parse(process.argv);
@@ -532,12 +455,10 @@ export const parseAndValidate = async (fileName) => {
532
455
  const parseResult = document.parseResult;
533
456
  // verify no lexer, parser, or general diagnostic errors show up
534
457
  if (parseResult.lexerErrors.length === 0 && parseResult.parserErrors.length === 0) {
535
- // eslint-disable-next-line no-console
536
- console.log(chalk.green(`Parsed and validated ${fileName} successfully!`));
458
+ ui.success(`Parsed and validated ${fileName} successfully!`);
537
459
  }
538
460
  else {
539
- // eslint-disable-next-line no-console
540
- console.log(chalk.red(`Failed to parse and validate ${fileName}!`));
461
+ ui.error(`Failed to parse and validate ${fileName}!`);
541
462
  }
542
463
  };
543
464
  export const runModule = async (fileName) => {
@@ -559,9 +480,8 @@ export const runModule = async (fileName) => {
559
480
  });
560
481
  }
561
482
  catch (err) {
562
- if (isNodeEnv && chalk) {
563
- // eslint-disable-next-line no-console
564
- console.error(chalk.red(String(err)));
483
+ if (isNodeEnv) {
484
+ ui.error(String(err));
565
485
  }
566
486
  else {
567
487
  // eslint-disable-next-line no-console
@@ -587,8 +507,7 @@ export const replCommand = async (directory, options) => {
587
507
  });
588
508
  }
589
509
  catch (error) {
590
- // eslint-disable-next-line no-console
591
- console.log(chalk.red(`Failed to start REPL: ${error instanceof Error ? error.message : String(error)}`));
510
+ ui.error(`Failed to start REPL: ${error instanceof Error ? error.message : String(error)}`);
592
511
  process.exit(1);
593
512
  }
594
513
  };
@@ -601,20 +520,23 @@ export async function internAndRunModule(module, appSpec) {
601
520
  await runPostInitTasks(appSpec);
602
521
  return rm;
603
522
  }
604
- /* eslint-disable no-console */
605
523
  export const generateUICommand = async (specFile, options) => {
606
524
  try {
607
- console.log(chalk.blue('🚀 Agentlang UI Generator\n'));
525
+ ui.blank();
526
+ ui.banner('UI Generator');
527
+ ui.blank();
608
528
  // Get API key from options or environment
609
529
  const apiKey = (options === null || options === void 0 ? void 0 : options.apiKey) || process.env.ANTHROPIC_API_KEY;
610
530
  if (!apiKey) {
611
- console.error(chalk.red('❌ Error: Anthropic API key is required.'));
612
- console.log(chalk.yellow(' Set ANTHROPIC_API_KEY environment variable or use --api-key flag.'));
613
- console.log(chalk.gray('\n Example:'));
614
- console.log(chalk.gray(' $ export ANTHROPIC_API_KEY=sk-ant-...'));
615
- console.log(chalk.gray(' $ agent ui-gen'));
616
- console.log(chalk.gray('\n Or:'));
617
- console.log(chalk.gray(' $ agent ui-gen --api-key sk-ant-...'));
531
+ ui.error('Anthropic API key is required.');
532
+ ui.warn('Set ANTHROPIC_API_KEY environment variable or use --api-key flag.');
533
+ ui.blank();
534
+ ui.gray(' Example:');
535
+ ui.gray(' $ export ANTHROPIC_API_KEY=sk-ant-...');
536
+ ui.gray(' $ agent ui-gen');
537
+ ui.blank();
538
+ ui.gray(' Or:');
539
+ ui.gray(' $ agent ui-gen --api-key sk-ant-...');
618
540
  process.exit(1);
619
541
  }
620
542
  // Set target directory
@@ -623,52 +545,51 @@ export const generateUICommand = async (specFile, options) => {
623
545
  // Auto-detect spec file if not provided
624
546
  let specFilePath;
625
547
  if (!specFile) {
626
- console.log(chalk.cyan('📄 Searching for UI spec file...'));
548
+ ui.dim('Searching for UI spec file...');
627
549
  specFilePath = await findSpecFile(absoluteTargetDir);
628
550
  }
629
551
  else {
630
552
  specFilePath = path.resolve(process.cwd(), specFile);
631
553
  }
632
554
  // Load the UI spec
633
- console.log(chalk.cyan(`📄 Loading UI spec from: ${specFilePath}`));
634
555
  const uiSpec = await loadUISpec(specFilePath);
635
- console.log(chalk.cyan(`📂 Target directory: ${absoluteTargetDir}`));
636
- console.log(chalk.cyan(`📦 Output will be created in: ${path.join(absoluteTargetDir, 'ui')}`));
556
+ ui.label('Spec', specFilePath, 'cyan');
557
+ ui.label('Target', absoluteTargetDir);
558
+ ui.label('Output', path.join(absoluteTargetDir, 'ui'));
559
+ ui.blank();
637
560
  // Generate or update the UI
638
561
  await generateUI(uiSpec, absoluteTargetDir, apiKey, (options === null || options === void 0 ? void 0 : options.push) || false, options === null || options === void 0 ? void 0 : options.message);
639
- console.log(chalk.green('\n✅ UI generation completed successfully!'));
562
+ ui.blank();
563
+ ui.divider(50);
564
+ ui.success('UI generation completed!');
565
+ ui.divider(50);
566
+ ui.blank();
640
567
  }
641
568
  catch (error) {
642
- console.error(chalk.red('\n❌ Error:'), error instanceof Error ? error.message : error);
569
+ ui.error(error instanceof Error ? error.message : String(error));
643
570
  process.exit(1);
644
571
  }
645
572
  };
646
- /* eslint-enable no-console */
647
- /* eslint-disable no-console */
648
573
  export const studioCommand = async (projectPath, options) => {
649
574
  try {
650
575
  const port = parseInt((options === null || options === void 0 ? void 0 : options.port) || '4000', 10);
651
576
  if (isNaN(port) || port < 1 || port > 65535) {
652
- console.error(chalk.red('Invalid port number. Port must be between 1 and 65535.'));
577
+ ui.error('Invalid port number. Port must be between 1 and 65535.');
653
578
  process.exit(1);
654
579
  }
655
580
  await startStudio(projectPath || '.', port, options === null || options === void 0 ? void 0 : options.serverOnly);
656
581
  }
657
582
  catch (error) {
658
- console.error(chalk.red(`Failed to start Studio: ${error instanceof Error ? error.message : String(error)}`));
583
+ ui.error(`Failed to start Studio: ${error instanceof Error ? error.message : String(error)}`);
659
584
  process.exit(1);
660
585
  }
661
586
  };
662
- /* eslint-enable no-console */
663
- /* eslint-disable no-console */
664
587
  export const forkCommand = async (source, name, options) => {
665
588
  try {
666
- console.log(chalk.blue('🚀 Forking Agentlang application...\n'));
667
589
  // Determine destination name
668
590
  let appName = name;
669
591
  if (!appName) {
670
592
  if (source.startsWith('http') || source.startsWith('git@')) {
671
- // Try to infer from URL
672
593
  const parts = source.split('/');
673
594
  const lastPart = parts[parts.length - 1].replace('.git', '');
674
595
  appName = lastPart;
@@ -690,24 +611,41 @@ export const forkCommand = async (source, name, options) => {
690
611
  token: options.token,
691
612
  };
692
613
  }
693
- console.log(chalk.cyan(`📦 Source: ${source}`));
694
- console.log(chalk.cyan(`📂 Destination: ${destPath}`));
614
+ ui.blank();
615
+ ui.banner('Fork App');
616
+ ui.blank();
617
+ ui.label('Source', source, 'cyan');
618
+ ui.label('Destination', destPath);
695
619
  if (options === null || options === void 0 ? void 0 : options.branch) {
696
- console.log(chalk.cyan(`🌿 Branch: ${options.branch}`));
620
+ ui.label('Branch', options.branch, 'cyan');
697
621
  }
698
622
  if (forkOptions.credentials) {
699
- console.log(chalk.cyan(`🔐 Authenticated as: ${forkOptions.credentials.username}`));
623
+ ui.label('Auth', forkOptions.credentials.username, 'cyan');
700
624
  }
625
+ ui.blank();
701
626
  // Perform the fork
702
627
  const result = await forkApp(source, destPath, forkOptions);
703
- console.log(chalk.green(`\n✅ Successfully forked app "${result.name}"!`));
704
- console.log(chalk.dim('\nNext steps:'));
705
- console.log(chalk.dim(' 1. Change directory: ') + chalk.cyan(`cd ${result.name}`));
706
- console.log(chalk.dim(' 2. Run your app: ') + chalk.cyan('agent run'));
707
- console.log(chalk.dim(' 3. Or start Studio: ') + chalk.cyan('agent studio'));
628
+ ui.divider(50);
629
+ ui.success(`Forked "${result.name}" successfully!`);
630
+ ui.blank();
631
+ ui.dim('Next steps:');
632
+ ui.row([
633
+ { text: ' 1. Change directory: ', dimColor: true },
634
+ { text: `cd ${result.name}`, color: 'cyan' },
635
+ ]);
636
+ ui.row([
637
+ { text: ' 2. Run your app: ', dimColor: true },
638
+ { text: 'agent run', color: 'cyan' },
639
+ ]);
640
+ ui.row([
641
+ { text: ' 3. Or start Studio: ', dimColor: true },
642
+ { text: 'agent studio', color: 'cyan' },
643
+ ]);
644
+ ui.divider(50);
645
+ ui.blank();
708
646
  }
709
647
  catch (error) {
710
- console.error(chalk.red('\n❌ Error:'), error instanceof Error ? error.message : error);
648
+ ui.error(error instanceof Error ? error.message : String(error));
711
649
  process.exit(1);
712
650
  }
713
651
  };