@koi-language/koi 1.0.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.
Files changed (85) hide show
  1. package/QUICKSTART.md +89 -0
  2. package/README.md +545 -0
  3. package/examples/actions-demo.koi +177 -0
  4. package/examples/cache-test.koi +29 -0
  5. package/examples/calculator.koi +61 -0
  6. package/examples/clear-registry.js +33 -0
  7. package/examples/clear-registry.koi +30 -0
  8. package/examples/code-introspection-test.koi +149 -0
  9. package/examples/counter.koi +132 -0
  10. package/examples/delegation-test.koi +52 -0
  11. package/examples/directory-import-test.koi +84 -0
  12. package/examples/hello-world-claude.koi +52 -0
  13. package/examples/hello-world.koi +52 -0
  14. package/examples/hello.koi +24 -0
  15. package/examples/mcp-example.koi +70 -0
  16. package/examples/multi-event-handler-test.koi +144 -0
  17. package/examples/new-import-test.koi +89 -0
  18. package/examples/pipeline.koi +162 -0
  19. package/examples/registry-demo.koi +184 -0
  20. package/examples/registry-playbook-demo.koi +162 -0
  21. package/examples/registry-playbook-email-compositor-2.koi +140 -0
  22. package/examples/registry-playbook-email-compositor.koi +140 -0
  23. package/examples/sentiment.koi +90 -0
  24. package/examples/simple.koi +48 -0
  25. package/examples/skill-import-test.koi +76 -0
  26. package/examples/skills/advanced/index.koi +95 -0
  27. package/examples/skills/math-operations.koi +69 -0
  28. package/examples/skills/string-operations.koi +56 -0
  29. package/examples/task-chaining-demo.koi +244 -0
  30. package/examples/test-await.koi +22 -0
  31. package/examples/test-crypto-sha256.koi +196 -0
  32. package/examples/test-delegation.koi +41 -0
  33. package/examples/test-multi-team-routing.koi +258 -0
  34. package/examples/test-no-handler.koi +35 -0
  35. package/examples/test-npm-import.koi +67 -0
  36. package/examples/test-parse.koi +10 -0
  37. package/examples/test-peers-with-team.koi +59 -0
  38. package/examples/test-permissions-fail.koi +20 -0
  39. package/examples/test-permissions.koi +36 -0
  40. package/examples/test-simple-registry.koi +31 -0
  41. package/examples/test-typescript-import.koi +64 -0
  42. package/examples/test-uses-team-syntax.koi +25 -0
  43. package/examples/test-uses-team.koi +31 -0
  44. package/examples/utils/calculator.test.ts +144 -0
  45. package/examples/utils/calculator.ts +56 -0
  46. package/examples/utils/math-helpers.js +50 -0
  47. package/examples/utils/math-helpers.ts +55 -0
  48. package/examples/web-delegation-demo.koi +165 -0
  49. package/package.json +78 -0
  50. package/src/cli/koi.js +793 -0
  51. package/src/compiler/build-optimizer.js +447 -0
  52. package/src/compiler/cache-manager.js +274 -0
  53. package/src/compiler/import-resolver.js +369 -0
  54. package/src/compiler/parser.js +7542 -0
  55. package/src/compiler/transpiler.js +1105 -0
  56. package/src/compiler/typescript-transpiler.js +148 -0
  57. package/src/grammar/koi.pegjs +767 -0
  58. package/src/runtime/action-registry.js +172 -0
  59. package/src/runtime/actions/call-skill.js +45 -0
  60. package/src/runtime/actions/format.js +115 -0
  61. package/src/runtime/actions/print.js +42 -0
  62. package/src/runtime/actions/registry-delete.js +37 -0
  63. package/src/runtime/actions/registry-get.js +37 -0
  64. package/src/runtime/actions/registry-keys.js +33 -0
  65. package/src/runtime/actions/registry-search.js +34 -0
  66. package/src/runtime/actions/registry-set.js +50 -0
  67. package/src/runtime/actions/return.js +31 -0
  68. package/src/runtime/actions/send-message.js +58 -0
  69. package/src/runtime/actions/update-state.js +36 -0
  70. package/src/runtime/agent.js +1368 -0
  71. package/src/runtime/cli-logger.js +205 -0
  72. package/src/runtime/incremental-json-parser.js +201 -0
  73. package/src/runtime/index.js +33 -0
  74. package/src/runtime/llm-provider.js +1372 -0
  75. package/src/runtime/mcp-client.js +1171 -0
  76. package/src/runtime/planner.js +273 -0
  77. package/src/runtime/registry-backends/keyv-sqlite.js +215 -0
  78. package/src/runtime/registry-backends/local.js +260 -0
  79. package/src/runtime/registry.js +162 -0
  80. package/src/runtime/role.js +14 -0
  81. package/src/runtime/router.js +395 -0
  82. package/src/runtime/runtime.js +113 -0
  83. package/src/runtime/skill-selector.js +173 -0
  84. package/src/runtime/skill.js +25 -0
  85. package/src/runtime/team.js +162 -0
package/src/cli/koi.js ADDED
@@ -0,0 +1,793 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { KoiTranspiler } from '../compiler/transpiler.js';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const VERSION = '1.0.0';
12
+
13
+ const COMMANDS = {
14
+ compile: 'Compile Koi files to JavaScript',
15
+ run: 'Compile and run Koi files',
16
+ init: 'Initialize a new Koi project',
17
+ cache: 'Manage build cache (stats, clear)',
18
+ registry: 'Manage registry data (stats, clear)',
19
+ version: 'Show version information',
20
+ help: 'Show this help message'
21
+ };
22
+
23
+ const FLAGS = {
24
+ '--no-precalculate': 'Disable embedding pre-calculation (dynamic at runtime)',
25
+ '--no-cache': 'Disable persistent cache (always regenerate embeddings)',
26
+ '--verbose': 'Show detailed output',
27
+ '--debug': 'Show all LLM prompts and responses',
28
+ '--output, -o': 'Specify output file path',
29
+ '--help, -h': 'Show help for a command'
30
+ };
31
+
32
+ function showBanner() {
33
+ console.log('🌊 Koi');
34
+ console.log(' Agent-first language. Calm orchestration.\n');
35
+ }
36
+
37
+ function formatBytes(bytes) {
38
+ if (bytes === 0) return '0 Bytes';
39
+ const k = 1024;
40
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
41
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
42
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
43
+ }
44
+
45
+ function showHelp(command = null) {
46
+ if (command) {
47
+ showCommandHelp(command);
48
+ return;
49
+ }
50
+
51
+ showBanner();
52
+ console.log('Usage: koi <command> [options] [file]\n');
53
+
54
+ console.log('Commands:');
55
+ for (const [cmd, desc] of Object.entries(COMMANDS)) {
56
+ console.log(` ${cmd.padEnd(12)} ${desc}`);
57
+ }
58
+
59
+ console.log('\nGlobal Flags:');
60
+ for (const [flag, desc] of Object.entries(FLAGS)) {
61
+ console.log(` ${flag.padEnd(20)} ${desc}`);
62
+ }
63
+
64
+ console.log('\nExamples:');
65
+ console.log(' koi run examples/hello-world.koi # Runs with auto-optimization');
66
+ console.log(' koi compile examples/simple.koi # Compiles with cache');
67
+ console.log(' koi compile -o output.js examples/simple.koi');
68
+ console.log(' koi init my-project');
69
+ console.log(' koi cache stats');
70
+ console.log(' koi registry clear');
71
+ console.log(' koi version');
72
+
73
+ console.log('\nGet started:');
74
+ console.log(' 1. Create a .env file with your OpenAI API key');
75
+ console.log(' 2. Run: koi run examples/hello-world.koi');
76
+ console.log(' 3. Embeddings are pre-computed and cached automatically!');
77
+
78
+ console.log('\nDocumentation:');
79
+ console.log(' README.md - Full language documentation');
80
+ console.log(' SETUP_LLM.md - LLM integration guide');
81
+ console.log(' examples/ - Example Koi programs');
82
+ }
83
+
84
+ function showCommandHelp(command) {
85
+ showBanner();
86
+
87
+ switch (command) {
88
+ case 'compile':
89
+ console.log('koi compile - Compile Koi source to JavaScript\n');
90
+ console.log('By default: Pre-computes embeddings and caches them for fast builds.');
91
+ console.log('Use --no-precalculate or --no-cache to customize behavior.\n');
92
+ console.log('Usage:');
93
+ console.log(' koi compile <file.koi>');
94
+ console.log(' koi compile -o <output.js> <file.koi>\n');
95
+ console.log('Options:');
96
+ console.log(' -o, --output <path> Output file path');
97
+ console.log(' --no-precalculate Disable embedding pre-calculation (dynamic at runtime)');
98
+ console.log(' --no-cache Disable persistent cache (always regenerate)');
99
+ console.log('\nExamples:');
100
+ console.log(' koi compile examples/simple.koi # Default: pre-compute + cache');
101
+ console.log(' koi compile --no-cache examples/simple.koi # Pre-compute without cache');
102
+ console.log(' koi compile --no-precalculate examples/simple.koi # No pre-computation');
103
+ console.log(' koi compile -o dist/app.js src/main.koi');
104
+ break;
105
+
106
+ case 'run':
107
+ console.log('koi run - Compile and execute Koi programs\n');
108
+ console.log('By default: Pre-computes embeddings and caches them.\n');
109
+ console.log('Usage:');
110
+ console.log(' koi run <file.koi>\n');
111
+ console.log('Options:');
112
+ console.log(' --no-precalculate Disable embedding pre-calculation');
113
+ console.log(' --no-cache Disable persistent cache');
114
+ console.log(' --verbose Show detailed execution logs');
115
+ console.log('\nExamples:');
116
+ console.log(' koi run examples/hello-world.koi # Default: pre-compute + cache');
117
+ console.log(' koi run --no-cache examples/hello-world.koi # No persistent cache');
118
+ break;
119
+
120
+ case 'init':
121
+ console.log('koi init - Initialize a new Koi project\n');
122
+ console.log('Usage:');
123
+ console.log(' koi init <project-name>\n');
124
+ console.log('Examples:');
125
+ console.log(' koi init my-agent-system');
126
+ console.log(' koi init ./my-project');
127
+ break;
128
+
129
+ case 'cache':
130
+ console.log('koi cache - Manage build cache\n');
131
+ console.log('Usage:');
132
+ console.log(' koi cache stats Show cache statistics');
133
+ console.log(' koi cache clear Clear all cache');
134
+ console.log(' koi cache clear <file> Clear cache for specific file\n');
135
+ console.log('The cache stores pre-computed embeddings to avoid API calls.');
136
+ console.log('Cache is stored in .koi-cache/ directory.\n');
137
+ console.log('Examples:');
138
+ console.log(' koi cache stats');
139
+ console.log(' koi cache clear');
140
+ console.log(' koi cache clear src/main.koi');
141
+ break;
142
+
143
+ case 'registry':
144
+ console.log('koi registry - Manage registry data\n');
145
+ console.log('Usage:');
146
+ console.log(' koi registry stats Show registry statistics');
147
+ console.log(' koi registry clear Clear all registry data\n');
148
+ console.log('The registry is a shared data store for agent collaboration.');
149
+ console.log('Data is stored based on the configured backend (default: local files).\n');
150
+ console.log('Examples:');
151
+ console.log(' koi registry stats');
152
+ console.log(' koi registry clear');
153
+ break;
154
+
155
+ default:
156
+ console.log(`Unknown command: ${command}\n`);
157
+ showHelp();
158
+ }
159
+ }
160
+
161
+ function showVersion() {
162
+ showBanner();
163
+ console.log(`Version: ${VERSION}`);
164
+ console.log('Node:', process.version);
165
+ console.log('Platform:', process.platform);
166
+ }
167
+
168
+ async function compileFile(sourcePath, outputPath = null, options = {}) {
169
+ const { cliLogger } = await import('../runtime/cli-logger.js');
170
+ const verbose = options.verbose || false;
171
+
172
+ if (verbose) {
173
+ console.log(`\nšŸ“¦ Compiling: ${sourcePath}`);
174
+
175
+ // Show development mode if KOI_RUNTIME_PATH is set
176
+ if (process.env.KOI_RUNTIME_PATH) {
177
+ console.log(`šŸ”§ Development mode: Using local runtime from ${process.env.KOI_RUNTIME_PATH}`);
178
+ }
179
+ }
180
+
181
+ // Check if we need to build the parser first
182
+ const parserPath = path.join(__dirname, '../compiler/parser.js');
183
+ if (!fs.existsSync(parserPath)) {
184
+ cliLogger.progress('āš ļø Parser not found. Building grammar...');
185
+ await buildGrammar();
186
+ cliLogger.clear();
187
+ }
188
+
189
+ // Import parser dynamically
190
+ const { parse } = await import('../compiler/parser.js');
191
+
192
+ // Read source
193
+ const source = fs.readFileSync(sourcePath, 'utf-8');
194
+
195
+ // Parse
196
+ if (verbose) {
197
+ cliLogger.progress('šŸ” Parsing...');
198
+ }
199
+ let ast;
200
+ try {
201
+ ast = parse(source);
202
+ if (verbose) {
203
+ cliLogger.clear();
204
+ }
205
+ } catch (error) {
206
+ if (verbose) {
207
+ cliLogger.clear();
208
+ }
209
+ console.error('āŒ Parse error:');
210
+ if (error.location) {
211
+ console.error(` Line ${error.location.start.line}, Column ${error.location.start.column}`);
212
+ console.error(` ${error.message}`);
213
+
214
+ // Show source context
215
+ const lines = source.split('\n');
216
+ const lineNum = error.location.start.line - 1;
217
+ if (lineNum >= 0 && lineNum < lines.length) {
218
+ console.error(`\n ${lines[lineNum]}`);
219
+ console.error(` ${' '.repeat(error.location.start.column - 1)}^`);
220
+ }
221
+ } else {
222
+ console.error(` ${error.message}`);
223
+ }
224
+ process.exit(1);
225
+ }
226
+
227
+ // Resolve imports
228
+ if (verbose) {
229
+ cliLogger.progress('šŸ“¦ Resolving imports...');
230
+ }
231
+ const { ImportResolver } = await import('../compiler/import-resolver.js');
232
+ const importResolver = new ImportResolver(parse);
233
+ try {
234
+ ast = await importResolver.resolveImports(ast, sourcePath);
235
+
236
+ if (verbose) {
237
+ const counts = {
238
+ skills: importResolver.importedSkills.length,
239
+ agents: importResolver.importedAgents.length,
240
+ roles: importResolver.importedRoles.length,
241
+ teams: importResolver.importedTeams.length,
242
+ external: importResolver.externalImports.length
243
+ };
244
+
245
+ const total = counts.skills + counts.agents + counts.roles + counts.teams + counts.external;
246
+
247
+ if (total > 0) {
248
+ cliLogger.clear();
249
+ const parts = [];
250
+ if (counts.skills > 0) parts.push(`${counts.skills} skill(s)`);
251
+ if (counts.agents > 0) parts.push(`${counts.agents} agent(s)`);
252
+ if (counts.roles > 0) parts.push(`${counts.roles} role(s)`);
253
+ if (counts.teams > 0) parts.push(`${counts.teams} team(s)`);
254
+ if (counts.external > 0) parts.push(`${counts.external} external module(s)`);
255
+ console.log(`āœ… Imported ${parts.join(', ')}`);
256
+ }
257
+ }
258
+ if (verbose) {
259
+ cliLogger.clear();
260
+ }
261
+ } catch (error) {
262
+ if (verbose) {
263
+ cliLogger.clear();
264
+ }
265
+ console.error('āŒ Import error:', error.message);
266
+ process.exit(1);
267
+ }
268
+
269
+ // Build-time optimization: BY DEFAULT pre-calculate and cache
270
+ // Use --no-precalculate to disable pre-calculation (dynamic at runtime)
271
+ // Use --no-cache to disable persistent cache (always regenerate)
272
+ let cacheData = null;
273
+
274
+ const shouldPrecalculate = !options.noPrecalculate;
275
+ const shouldCache = !options.noCache;
276
+
277
+ if (shouldPrecalculate) {
278
+ const { CacheManager } = await import('../compiler/cache-manager.js');
279
+ const cacheManager = new CacheManager({ verbose: options.verbose });
280
+
281
+ // Try to load from cache if caching is enabled
282
+ if (shouldCache) {
283
+ if (verbose) {
284
+ cliLogger.progress('šŸ” Checking cache...');
285
+ }
286
+ cacheData = cacheManager.get(source, sourcePath);
287
+
288
+ if (cacheData && verbose) {
289
+ cliLogger.success(`āœ… Using cached embeddings (${cacheData.metadata.totalAffordances} affordances)`);
290
+ } else if (verbose) {
291
+ cliLogger.clear();
292
+ }
293
+ }
294
+
295
+ // Generate if no cache or cache disabled
296
+ if (!cacheData) {
297
+ const { BuildTimeOptimizer } = await import('../compiler/build-optimizer.js');
298
+ const optimizer = new BuildTimeOptimizer({
299
+ cache: shouldCache,
300
+ verbose: options.verbose
301
+ });
302
+
303
+ try {
304
+ if (shouldCache) {
305
+ if (verbose) {
306
+ cliLogger.progress('šŸ”„ Pre-computing embeddings...');
307
+ }
308
+ cacheData = await optimizer.optimizeAST(ast, source, sourcePath);
309
+ if (verbose) {
310
+ cliLogger.success(`āœ… Pre-computed ${cacheData.metadata.totalAffordances} embeddings`);
311
+ }
312
+ } else {
313
+ if (verbose) {
314
+ cliLogger.progress('šŸ”„ Pre-computing embeddings (no cache)...');
315
+ }
316
+ cacheData = await optimizer.optimizeASTWithoutCache(ast);
317
+ if (verbose) {
318
+ cliLogger.success(`āœ… Pre-computed ${cacheData.metadata.totalAffordances} embeddings`);
319
+ }
320
+ }
321
+ } catch (error) {
322
+ if (verbose) {
323
+ cliLogger.clear();
324
+ }
325
+ console.error('āš ļø Build-time optimization failed:', error.message);
326
+ console.error(' Continuing without pre-computed embeddings...\n');
327
+ }
328
+ }
329
+ } else {
330
+ if (options.verbose) {
331
+ cliLogger.info('[Optimizer] Pre-calculation disabled. Runtime will generate embeddings dynamically.');
332
+ }
333
+ }
334
+
335
+ // Determine output path first (needed for transpiler to calculate runtime path)
336
+ if (!outputPath) {
337
+ // Create .build directory if it doesn't exist
338
+ const buildDir = path.join(path.dirname(sourcePath), '.build');
339
+ if (!fs.existsSync(buildDir)) {
340
+ fs.mkdirSync(buildDir, { recursive: true });
341
+ }
342
+
343
+ // Ensure package.json exists in .build directory for ES modules
344
+ const packageJsonPath = path.join(buildDir, 'package.json');
345
+ if (!fs.existsSync(packageJsonPath)) {
346
+ fs.writeFileSync(packageJsonPath, JSON.stringify({ type: 'module' }, null, 2));
347
+ }
348
+
349
+ const basename = path.basename(sourcePath, '.koi');
350
+ outputPath = path.join(buildDir, basename + '.js');
351
+ }
352
+
353
+ // Transpile with output path so it can calculate correct runtime import path
354
+ if (verbose) {
355
+ cliLogger.progress('šŸ”Ø Transpiling...');
356
+ }
357
+ const runtimePath = path.join(__dirname, '../runtime/index.js');
358
+ const transpiler = new KoiTranspiler(path.basename(sourcePath), {
359
+ cacheData,
360
+ outputPath: path.resolve(outputPath),
361
+ runtimePath: path.resolve(runtimePath),
362
+ externalImports: importResolver.externalImports
363
+ });
364
+ const { code, map } = transpiler.transpile(ast);
365
+ if (verbose) {
366
+ cliLogger.clear();
367
+ }
368
+
369
+ // Ensure output directory exists
370
+ const outputDir = path.dirname(outputPath);
371
+ if (!fs.existsSync(outputDir)) {
372
+ fs.mkdirSync(outputDir, { recursive: true });
373
+
374
+ // Ensure package.json exists in output directory for ES modules
375
+ const packageJsonPath = path.join(outputDir, 'package.json');
376
+ if (!fs.existsSync(packageJsonPath)) {
377
+ fs.writeFileSync(packageJsonPath, JSON.stringify({ type: 'module' }, null, 2));
378
+ }
379
+ }
380
+
381
+ // Write output
382
+ if (verbose) {
383
+ cliLogger.progress(`šŸ’¾ Writing: ${outputPath}`);
384
+ }
385
+ fs.writeFileSync(outputPath, code + `\n//# sourceMappingURL=${path.basename(outputPath)}.map`);
386
+ fs.writeFileSync(outputPath + '.map', map);
387
+ if (verbose) {
388
+ cliLogger.clear();
389
+ }
390
+
391
+ if (verbose) {
392
+ console.log('āœ… Compilation complete!\n');
393
+ }
394
+ return outputPath;
395
+ }
396
+
397
+ async function runFile(sourcePath, options = {}) {
398
+ const verbose = options.verbose || false;
399
+ const debug = options.debug || false;
400
+
401
+ // Show simple 1-line log if not verbose
402
+ if (!verbose) {
403
+ console.log(`🌊 Running ${sourcePath}`);
404
+ }
405
+
406
+ // Compile first
407
+ const jsPath = await compileFile(sourcePath, null, options);
408
+
409
+ // Run
410
+ if (verbose) {
411
+ console.log(`šŸš€ Executing: ${jsPath}\n`);
412
+ console.log('─'.repeat(60));
413
+ }
414
+
415
+ // Execute in a child process to avoid module cache issues
416
+ // This ensures each run uses the freshly compiled code
417
+ const { spawn } = await import('child_process');
418
+
419
+ // Prepare environment variables
420
+ const env = { ...process.env };
421
+ if (debug) {
422
+ env.KOI_DEBUG_LLM = '1';
423
+ }
424
+
425
+ const child = spawn('node', [jsPath], {
426
+ stdio: 'inherit',
427
+ env
428
+ });
429
+
430
+ return new Promise((resolve, reject) => {
431
+ child.on('exit', (code) => {
432
+ if (code !== 0) {
433
+ // Exit silently - error message already shown by child process
434
+ process.exit(code);
435
+ } else {
436
+ resolve();
437
+ }
438
+ });
439
+
440
+ child.on('error', (error) => {
441
+ console.error('\nāŒ Runtime error:');
442
+ console.error(error);
443
+ process.exit(1);
444
+ });
445
+ });
446
+ }
447
+
448
+ async function buildGrammar() {
449
+ const { execSync } = await import('child_process');
450
+ const grammarPath = path.join(__dirname, '../grammar/koi.pegjs');
451
+ const outputPath = path.join(__dirname, '../compiler/parser.js');
452
+
453
+ console.log('šŸ—ļø Building parser from grammar...');
454
+ try {
455
+ execSync(`npx peggy -o "${outputPath}" "${grammarPath}"`, {
456
+ stdio: 'inherit',
457
+ cwd: path.join(__dirname, '../..')
458
+ });
459
+ console.log('āœ… Parser built successfully!\n');
460
+ } catch (error) {
461
+ console.error('āŒ Failed to build parser');
462
+ process.exit(1);
463
+ }
464
+ }
465
+
466
+ async function initProject(projectName) {
467
+ if (!projectName) {
468
+ console.error('āŒ Please provide a project name');
469
+ console.log('Usage: koi init <project-name>');
470
+ process.exit(1);
471
+ }
472
+
473
+ const projectPath = path.resolve(projectName);
474
+
475
+ if (fs.existsSync(projectPath)) {
476
+ console.error(`āŒ Directory already exists: ${projectPath}`);
477
+ process.exit(1);
478
+ }
479
+
480
+ console.log(`🌊 Initializing Koi project: ${projectName}\n`);
481
+
482
+ // Create directories
483
+ fs.mkdirSync(projectPath, { recursive: true });
484
+ fs.mkdirSync(path.join(projectPath, 'src'));
485
+ fs.mkdirSync(path.join(projectPath, 'dist'));
486
+
487
+ // Create example main.koi
488
+ const exampleCode = `// ${projectName} - Koi Project
489
+ package "${projectName}"
490
+
491
+ role Worker { can execute }
492
+ role Lead { can delegate }
493
+
494
+ Agent HelloAgent : Worker {
495
+ on greet(args: Json) {
496
+ const name = args.name
497
+ return { message: "Hello from ${projectName}, " + name + "!" }
498
+ }
499
+ }
500
+
501
+ Team MainTeam {
502
+ hello = HelloAgent
503
+ }
504
+
505
+ Agent Orchestrator : Lead {
506
+ uses Team MainTeam
507
+
508
+ on start(args: Json) {
509
+ const result =
510
+ await send peers.event("greet").role(Worker).any()({ name: "World" }) timeout 5s
511
+ return result
512
+ }
513
+ }
514
+
515
+ run Orchestrator.start({})
516
+ `;
517
+
518
+ fs.writeFileSync(path.join(projectPath, 'src', 'main.koi'), exampleCode);
519
+
520
+ // Create package.json
521
+ const packageJson = {
522
+ name: projectName.toLowerCase().replace(/\s+/g, '-'),
523
+ version: '1.0.0',
524
+ description: `${projectName} - Koi project`,
525
+ type: 'module',
526
+ scripts: {
527
+ start: 'koi run src/main.koi',
528
+ compile: 'koi compile src/main.koi'
529
+ },
530
+ keywords: ['koi', 'agents'],
531
+ author: '',
532
+ license: 'MIT',
533
+ dependencies: {
534
+ 'koi-lang': '^1.0.0'
535
+ }
536
+ };
537
+
538
+ fs.writeFileSync(path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2));
539
+
540
+ // Create .env.example
541
+ const envExample = `# OpenAI API Key
542
+ OPENAI_API_KEY=your-key-here
543
+
544
+ # Anthropic API Key (optional)
545
+ # ANTHROPIC_API_KEY=your-key-here
546
+ `;
547
+
548
+ fs.writeFileSync(path.join(projectPath, '.env.example'), envExample);
549
+
550
+ // Create README
551
+ const readme = `# ${projectName}
552
+
553
+ Koi project - Agent-first language. Calm orchestration.
554
+
555
+ ## Quick Start
556
+
557
+ 1. Install dependencies:
558
+ \`\`\`bash
559
+ npm install
560
+ \`\`\`
561
+
562
+ 2. Configure API key:
563
+ \`\`\`bash
564
+ cp .env.example .env
565
+ # Edit .env and add your OpenAI API key
566
+ \`\`\`
567
+
568
+ 3. Run:
569
+ \`\`\`bash
570
+ npm start
571
+ # or directly: koi run src/main.koi
572
+ \`\`\`
573
+
574
+ ## Structure
575
+
576
+ - \`src/\` - Koi source files (.koi)
577
+ - \`dist/\` - Compiled JavaScript output
578
+
579
+ ## Documentation
580
+
581
+ - [Koi Language Docs](https://github.com/yourusername/koi)
582
+ `;
583
+
584
+ fs.writeFileSync(path.join(projectPath, 'README.md'), readme);
585
+
586
+ // Create .gitignore
587
+ const gitignore = `node_modules/
588
+ .env
589
+ dist/
590
+ .build/
591
+ .koi-cache/
592
+ *.js.map
593
+ .DS_Store
594
+ `;
595
+
596
+ fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
597
+
598
+ console.log('āœ… Project initialized!');
599
+ console.log(`\nNext steps:`);
600
+ console.log(` cd ${projectName}`);
601
+ console.log(` npm install`);
602
+ console.log(` cp .env.example .env`);
603
+ console.log(` # Edit .env and add your API key`);
604
+ console.log(` npm start`);
605
+ }
606
+
607
+ // Parse arguments and flags
608
+ function parseArgs(argv) {
609
+ const flags = {};
610
+ const positional = [];
611
+
612
+ // Define which flags are boolean (don't take values)
613
+ const booleanFlags = ['no-precalculate', 'no-cache', 'verbose', 'debug', 'help', 'h', 'version', 'v'];
614
+ // Define which flags take values
615
+ const valuedFlags = ['output', 'o'];
616
+
617
+ for (let i = 0; i < argv.length; i++) {
618
+ const arg = argv[i];
619
+
620
+ if (arg.startsWith('--')) {
621
+ // Long flag
622
+ const flagName = arg.slice(2);
623
+
624
+ if (booleanFlags.includes(flagName)) {
625
+ flags[flagName] = true;
626
+ } else if (valuedFlags.includes(flagName)) {
627
+ if (argv[i + 1] && !argv[i + 1].startsWith('-')) {
628
+ flags[flagName] = argv[++i];
629
+ } else {
630
+ console.error(`āŒ Flag --${flagName} requires a value`);
631
+ process.exit(1);
632
+ }
633
+ } else {
634
+ // Unknown flag: try to guess
635
+ if (argv[i + 1] && !argv[i + 1].startsWith('-') && !argv[i + 1].endsWith('.koi')) {
636
+ flags[flagName] = argv[++i];
637
+ } else {
638
+ flags[flagName] = true;
639
+ }
640
+ }
641
+ } else if (arg.startsWith('-') && arg.length === 2) {
642
+ // Short flag
643
+ const flagChar = arg[1];
644
+
645
+ if (valuedFlags.includes(flagChar)) {
646
+ if (argv[i + 1] && !argv[i + 1].startsWith('-')) {
647
+ flags[flagChar] = argv[++i];
648
+ } else {
649
+ console.error(`āŒ Flag -${flagChar} requires a value`);
650
+ process.exit(1);
651
+ }
652
+ } else {
653
+ flags[flagChar] = true;
654
+ }
655
+ } else {
656
+ positional.push(arg);
657
+ }
658
+ }
659
+
660
+ return { flags, positional };
661
+ }
662
+
663
+ // Main
664
+ const rawArgs = process.argv.slice(2);
665
+
666
+ if (rawArgs.length === 0 || rawArgs[0] === 'help' || rawArgs[0] === '--help' || rawArgs[0] === '-h') {
667
+ showHelp(rawArgs[1]);
668
+ process.exit(0);
669
+ }
670
+
671
+ const { flags, positional } = parseArgs(rawArgs);
672
+ const command = positional[0];
673
+
674
+ // Handle version
675
+ if (command === 'version' || flags.version || flags.v) {
676
+ showVersion();
677
+ process.exit(0);
678
+ }
679
+
680
+ // Check command exists
681
+ if (!COMMANDS[command]) {
682
+ console.error(`āŒ Unknown command: ${command}\n`);
683
+ showHelp();
684
+ process.exit(1);
685
+ }
686
+
687
+ // Handle init
688
+ if (command === 'init') {
689
+ await initProject(positional[1]);
690
+ process.exit(0);
691
+ }
692
+
693
+ // Handle cache
694
+ if (command === 'cache') {
695
+ const { CacheManager } = await import('../compiler/cache-manager.js');
696
+ const cacheManager = new CacheManager({ verbose: true });
697
+
698
+ const subcommand = positional[1];
699
+
700
+ if (!subcommand || subcommand === 'stats') {
701
+ cacheManager.printStats();
702
+ } else if (subcommand === 'clear') {
703
+ const targetFile = positional[2];
704
+ if (targetFile) {
705
+ cacheManager.clear(targetFile);
706
+ } else {
707
+ cacheManager.clear();
708
+ }
709
+ } else {
710
+ console.error(`āŒ Unknown cache subcommand: ${subcommand}\n`);
711
+ console.log('Usage: koi cache [stats|clear]');
712
+ process.exit(1);
713
+ }
714
+
715
+ process.exit(0);
716
+ }
717
+
718
+ // Handle registry
719
+ if (command === 'registry') {
720
+ const { registry } = await import('../runtime/registry.js');
721
+ const subcommand = positional[1];
722
+
723
+ if (!subcommand || subcommand === 'stats') {
724
+ console.log('🌊 Registry Statistics\n');
725
+ try {
726
+ const stats = await registry.stats();
727
+ console.log(`Total entries: ${stats.count}`);
728
+ console.log(`Storage size: ${formatBytes(stats.size)}`);
729
+ console.log(`Backend: ${stats.backend || 'local'}`);
730
+ if (stats.location) {
731
+ console.log(`Location: ${stats.location}`);
732
+ }
733
+ } catch (error) {
734
+ console.error(`āŒ Failed to get registry stats: ${error.message}`);
735
+ process.exit(1);
736
+ }
737
+ } else if (subcommand === 'clear') {
738
+ console.log('🌊 Clearing Registry\n');
739
+ try {
740
+ const statsBefore = await registry.stats();
741
+ console.log(`Entries before: ${statsBefore.count}`);
742
+
743
+ await registry.clear();
744
+
745
+ const statsAfter = await registry.stats();
746
+ console.log(`Entries after: ${statsAfter.count}`);
747
+ console.log('\nāœ… Registry cleared successfully!');
748
+ } catch (error) {
749
+ console.error(`āŒ Failed to clear registry: ${error.message}`);
750
+ process.exit(1);
751
+ }
752
+ } else {
753
+ console.error(`āŒ Unknown registry subcommand: ${subcommand}\n`);
754
+ console.log('Usage: koi registry [stats|clear]');
755
+ process.exit(1);
756
+ }
757
+
758
+ process.exit(0);
759
+ }
760
+
761
+ // Handle compile and run
762
+ if (command === 'compile' || command === 'run') {
763
+ const file = positional[1];
764
+
765
+ if (!file) {
766
+ console.error(`āŒ Please provide a Koi file\n`);
767
+ console.log(`Usage: koi ${command} <file.koi>`);
768
+ process.exit(1);
769
+ }
770
+
771
+ if (!fs.existsSync(file)) {
772
+ console.error(`āŒ File not found: ${file}`);
773
+ process.exit(1);
774
+ }
775
+
776
+ // Build options object
777
+ // By default: pre-calculate AND cache (best performance)
778
+ // Use --no-precalculate to disable pre-calculation (dynamic at runtime)
779
+ // Use --no-cache to disable persistent cache (always regenerate)
780
+ const options = {
781
+ noPrecalculate: flags['no-precalculate'] || false,
782
+ noCache: flags['no-cache'] || false,
783
+ verbose: flags.verbose || false,
784
+ debug: flags.debug || false
785
+ };
786
+
787
+ if (command === 'compile') {
788
+ const outputPath = flags.output || flags.o;
789
+ await compileFile(file, outputPath, options);
790
+ } else if (command === 'run') {
791
+ await runFile(file, options);
792
+ }
793
+ }