@isl-lang/repl 0.0.1 → 0.1.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/dist/cli.js ADDED
@@ -0,0 +1,1734 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
9
+
10
+ // src/repl.ts
11
+ import * as readline from "readline";
12
+
13
+ // src/session.ts
14
+ import * as fs from "fs";
15
+ import * as path from "path";
16
+ var Session = class {
17
+ /** Defined intents in this session */
18
+ intents = /* @__PURE__ */ new Map();
19
+ /** Variables set during the session */
20
+ variables = /* @__PURE__ */ new Map();
21
+ /** Command history */
22
+ history = [];
23
+ /** Last evaluation result */
24
+ lastResult = void 0;
25
+ /** Loaded files */
26
+ loadedFiles = /* @__PURE__ */ new Set();
27
+ /** Session configuration */
28
+ config;
29
+ constructor(config = {}) {
30
+ this.config = {
31
+ colors: true,
32
+ verbose: false,
33
+ cwd: process.cwd(),
34
+ ...config
35
+ };
36
+ }
37
+ // ─────────────────────────────────────────────────────────────────────────
38
+ // Intent Management
39
+ // ─────────────────────────────────────────────────────────────────────────
40
+ /**
41
+ * Define a new intent
42
+ */
43
+ defineIntent(intent) {
44
+ this.intents.set(intent.name, intent);
45
+ }
46
+ /**
47
+ * Get an intent by name
48
+ */
49
+ getIntent(name) {
50
+ return this.intents.get(name);
51
+ }
52
+ /**
53
+ * Get all defined intents
54
+ */
55
+ getAllIntents() {
56
+ return Array.from(this.intents.values());
57
+ }
58
+ /**
59
+ * Check if an intent exists
60
+ */
61
+ hasIntent(name) {
62
+ return this.intents.has(name);
63
+ }
64
+ /**
65
+ * Remove an intent
66
+ */
67
+ removeIntent(name) {
68
+ return this.intents.delete(name);
69
+ }
70
+ /**
71
+ * Get intent names for completion
72
+ */
73
+ getIntentNames() {
74
+ return Array.from(this.intents.keys());
75
+ }
76
+ /**
77
+ * Parse an intent definition from source code
78
+ */
79
+ parseIntent(source) {
80
+ const trimmed = source.trim();
81
+ const match = trimmed.match(/^intent\s+(\w+)\s*\{([\s\S]*)\}$/);
82
+ if (!match) {
83
+ return null;
84
+ }
85
+ const name = match[1];
86
+ const body = match[2];
87
+ const intent = {
88
+ name,
89
+ preconditions: [],
90
+ postconditions: [],
91
+ invariants: [],
92
+ scenarios: [],
93
+ rawSource: source
94
+ };
95
+ const preMatch = body.match(/pre(?:conditions?)?\s*:\s*([^\n]+)/g);
96
+ if (preMatch) {
97
+ for (const pre of preMatch) {
98
+ const expr = pre.replace(/pre(?:conditions?)?\s*:\s*/, "").trim();
99
+ if (expr) {
100
+ intent.preconditions.push({ expression: expr });
101
+ }
102
+ }
103
+ }
104
+ const postMatch = body.match(/post(?:conditions?)?\s*:\s*([^\n]+)/g);
105
+ if (postMatch) {
106
+ for (const post of postMatch) {
107
+ const expr = post.replace(/post(?:conditions?)?\s*:\s*/, "").trim();
108
+ if (expr) {
109
+ intent.postconditions.push({ expression: expr });
110
+ }
111
+ }
112
+ }
113
+ const invMatch = body.match(/invariants?\s*:\s*([^\n]+)/g);
114
+ if (invMatch) {
115
+ for (const inv of invMatch) {
116
+ const expr = inv.replace(/invariants?\s*:\s*/, "").trim();
117
+ if (expr) {
118
+ intent.invariants.push({ expression: expr });
119
+ }
120
+ }
121
+ }
122
+ return intent;
123
+ }
124
+ // ─────────────────────────────────────────────────────────────────────────
125
+ // Variable Management
126
+ // ─────────────────────────────────────────────────────────────────────────
127
+ /**
128
+ * Set a variable
129
+ */
130
+ setVariable(name, value) {
131
+ this.variables.set(name, value);
132
+ }
133
+ /**
134
+ * Get a variable
135
+ */
136
+ getVariable(name) {
137
+ return this.variables.get(name);
138
+ }
139
+ /**
140
+ * Get all variables
141
+ */
142
+ getAllVariables() {
143
+ return new Map(this.variables);
144
+ }
145
+ /**
146
+ * Check if a variable exists
147
+ */
148
+ hasVariable(name) {
149
+ return this.variables.has(name);
150
+ }
151
+ /**
152
+ * Set the last result (accessible as _)
153
+ */
154
+ setLastResult(value) {
155
+ this.lastResult = value;
156
+ this.variables.set("_", value);
157
+ }
158
+ /**
159
+ * Get the last result
160
+ */
161
+ getLastResult() {
162
+ return this.lastResult;
163
+ }
164
+ // ─────────────────────────────────────────────────────────────────────────
165
+ // History Management
166
+ // ─────────────────────────────────────────────────────────────────────────
167
+ /**
168
+ * Add to history
169
+ */
170
+ addToHistory(entry) {
171
+ const trimmed = entry.trim();
172
+ if (trimmed && (this.history.length === 0 || this.history[this.history.length - 1] !== trimmed)) {
173
+ this.history.push(trimmed);
174
+ }
175
+ }
176
+ /**
177
+ * Get history
178
+ */
179
+ getHistory(count) {
180
+ if (count) {
181
+ return this.history.slice(-count);
182
+ }
183
+ return [...this.history];
184
+ }
185
+ // ─────────────────────────────────────────────────────────────────────────
186
+ // File Loading
187
+ // ─────────────────────────────────────────────────────────────────────────
188
+ /**
189
+ * Load intents from an ISL file
190
+ */
191
+ async loadFile(filePath) {
192
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(this.config.cwd, filePath);
193
+ if (!fs.existsSync(resolvedPath)) {
194
+ return { intents: [], errors: [`File not found: ${resolvedPath}`] };
195
+ }
196
+ try {
197
+ const content = fs.readFileSync(resolvedPath, "utf-8");
198
+ const loadedIntents = [];
199
+ const errors = [];
200
+ const intentRegex = /intent\s+(\w+)\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}/g;
201
+ let match;
202
+ while ((match = intentRegex.exec(content)) !== null) {
203
+ const intent = this.parseIntent(match[0]);
204
+ if (intent) {
205
+ this.defineIntent(intent);
206
+ loadedIntents.push(intent);
207
+ } else {
208
+ errors.push(`Failed to parse intent starting at position ${match.index}`);
209
+ }
210
+ }
211
+ const behaviorRegex = /behavior\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/g;
212
+ while ((match = behaviorRegex.exec(content)) !== null) {
213
+ const intent = this.parseBehaviorAsIntent(match[1], match[2]);
214
+ if (intent) {
215
+ this.defineIntent(intent);
216
+ loadedIntents.push(intent);
217
+ }
218
+ }
219
+ this.loadedFiles.add(resolvedPath);
220
+ return { intents: loadedIntents, errors };
221
+ } catch (error) {
222
+ return {
223
+ intents: [],
224
+ errors: [`Failed to load file: ${error instanceof Error ? error.message : String(error)}`]
225
+ };
226
+ }
227
+ }
228
+ /**
229
+ * Parse a behavior block as an intent
230
+ */
231
+ parseBehaviorAsIntent(name, body) {
232
+ const intent = {
233
+ name,
234
+ preconditions: [],
235
+ postconditions: [],
236
+ invariants: [],
237
+ scenarios: [],
238
+ rawSource: `behavior ${name} {${body}}`
239
+ };
240
+ const preSection = body.match(/pre(?:conditions)?\s*\{([^}]*)\}/s);
241
+ if (preSection) {
242
+ const conditions = preSection[1].trim().split("\n").map((l) => l.trim()).filter(Boolean);
243
+ for (const cond of conditions) {
244
+ const expr = cond.replace(/^-\s*/, "").trim();
245
+ if (expr) {
246
+ intent.preconditions.push({ expression: expr });
247
+ }
248
+ }
249
+ }
250
+ const postSection = body.match(/post(?:conditions)?\s*\{([^}]*)\}/s);
251
+ if (postSection) {
252
+ const conditions = postSection[1].trim().split("\n").map((l) => l.trim()).filter(Boolean);
253
+ for (const cond of conditions) {
254
+ const expr = cond.replace(/^-\s*/, "").trim();
255
+ if (expr) {
256
+ intent.postconditions.push({ expression: expr });
257
+ }
258
+ }
259
+ }
260
+ const invSection = body.match(/invariants?\s*\{([^}]*)\}/s);
261
+ if (invSection) {
262
+ const conditions = invSection[1].trim().split("\n").map((l) => l.trim()).filter(Boolean);
263
+ for (const cond of conditions) {
264
+ const expr = cond.replace(/^-\s*/, "").trim();
265
+ if (expr) {
266
+ intent.invariants.push({ expression: expr });
267
+ }
268
+ }
269
+ }
270
+ return intent;
271
+ }
272
+ /**
273
+ * Export session intents to a file
274
+ */
275
+ async exportToFile(filePath) {
276
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(this.config.cwd, filePath);
277
+ try {
278
+ const lines = [];
279
+ lines.push("// Exported ISL intents");
280
+ lines.push(`// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
281
+ lines.push("");
282
+ for (const intent of this.intents.values()) {
283
+ lines.push(`intent ${intent.name} {`);
284
+ for (const pre of intent.preconditions) {
285
+ lines.push(` pre: ${pre.expression}`);
286
+ }
287
+ for (const post of intent.postconditions) {
288
+ lines.push(` post: ${post.expression}`);
289
+ }
290
+ for (const inv of intent.invariants) {
291
+ lines.push(` invariant: ${inv.expression}`);
292
+ }
293
+ lines.push("}");
294
+ lines.push("");
295
+ }
296
+ fs.writeFileSync(resolvedPath, lines.join("\n"));
297
+ return { success: true };
298
+ } catch (error) {
299
+ return {
300
+ success: false,
301
+ error: `Failed to export: ${error instanceof Error ? error.message : String(error)}`
302
+ };
303
+ }
304
+ }
305
+ // ─────────────────────────────────────────────────────────────────────────
306
+ // State Management
307
+ // ─────────────────────────────────────────────────────────────────────────
308
+ /**
309
+ * Clear all session state
310
+ */
311
+ clear() {
312
+ this.intents.clear();
313
+ this.variables.clear();
314
+ this.lastResult = void 0;
315
+ this.loadedFiles.clear();
316
+ }
317
+ /**
318
+ * Get session summary
319
+ */
320
+ getSummary() {
321
+ return {
322
+ intentCount: this.intents.size,
323
+ variableCount: this.variables.size,
324
+ loadedFileCount: this.loadedFiles.size,
325
+ historyCount: this.history.length
326
+ };
327
+ }
328
+ /**
329
+ * Get loaded files
330
+ */
331
+ getLoadedFiles() {
332
+ return Array.from(this.loadedFiles);
333
+ }
334
+ /**
335
+ * Get config
336
+ */
337
+ getConfig() {
338
+ return { ...this.config };
339
+ }
340
+ /**
341
+ * Update config
342
+ */
343
+ setConfig(config) {
344
+ this.config = { ...this.config, ...config };
345
+ }
346
+ };
347
+
348
+ // src/history.ts
349
+ import * as fs2 from "fs";
350
+ import * as path2 from "path";
351
+ import * as os from "os";
352
+ var History = class {
353
+ entries = [];
354
+ position = -1;
355
+ maxSize;
356
+ historyFile;
357
+ unsavedCount = 0;
358
+ autoSaveThreshold = 10;
359
+ constructor(options = {}) {
360
+ this.maxSize = options.maxSize ?? 1e3;
361
+ this.historyFile = options.historyFile ?? this.getDefaultHistoryFile();
362
+ this.autoSaveThreshold = options.autoSaveThreshold ?? 10;
363
+ }
364
+ /**
365
+ * Get default history file path
366
+ */
367
+ getDefaultHistoryFile() {
368
+ const homeDir = os.homedir();
369
+ return path2.join(homeDir, ".isl_repl_history");
370
+ }
371
+ /**
372
+ * Load history from file
373
+ */
374
+ load() {
375
+ try {
376
+ if (fs2.existsSync(this.historyFile)) {
377
+ const content = fs2.readFileSync(this.historyFile, "utf-8");
378
+ this.entries = content.split("\n").filter((line) => line.trim() !== "").slice(-this.maxSize);
379
+ this.position = this.entries.length;
380
+ }
381
+ } catch {
382
+ }
383
+ }
384
+ /**
385
+ * Save history to file
386
+ */
387
+ save() {
388
+ try {
389
+ const dir = path2.dirname(this.historyFile);
390
+ if (!fs2.existsSync(dir)) {
391
+ fs2.mkdirSync(dir, { recursive: true });
392
+ }
393
+ fs2.writeFileSync(this.historyFile, this.entries.join("\n") + "\n");
394
+ this.unsavedCount = 0;
395
+ } catch {
396
+ }
397
+ }
398
+ /**
399
+ * Add entry to history
400
+ */
401
+ add(entry) {
402
+ const trimmed = entry.trim();
403
+ if (trimmed === "") return;
404
+ if (this.entries.length > 0 && this.entries[this.entries.length - 1] === trimmed) {
405
+ this.position = this.entries.length;
406
+ return;
407
+ }
408
+ this.entries.push(trimmed);
409
+ if (this.entries.length > this.maxSize) {
410
+ this.entries = this.entries.slice(-this.maxSize);
411
+ }
412
+ this.position = this.entries.length;
413
+ this.unsavedCount++;
414
+ if (this.unsavedCount >= this.autoSaveThreshold) {
415
+ this.save();
416
+ }
417
+ }
418
+ /**
419
+ * Get previous entry (for up arrow)
420
+ */
421
+ previous() {
422
+ if (this.entries.length === 0) return null;
423
+ if (this.position > 0) {
424
+ this.position--;
425
+ }
426
+ return this.entries[this.position] ?? null;
427
+ }
428
+ /**
429
+ * Get next entry (for down arrow)
430
+ */
431
+ next() {
432
+ if (this.entries.length === 0) return null;
433
+ if (this.position < this.entries.length - 1) {
434
+ this.position++;
435
+ return this.entries[this.position] ?? null;
436
+ }
437
+ this.position = this.entries.length;
438
+ return "";
439
+ }
440
+ /**
441
+ * Reset position to end
442
+ */
443
+ resetPosition() {
444
+ this.position = this.entries.length;
445
+ }
446
+ /**
447
+ * Search history for entries containing text
448
+ */
449
+ search(text) {
450
+ const lower = text.toLowerCase();
451
+ return this.entries.filter(
452
+ (entry) => entry.toLowerCase().includes(lower)
453
+ );
454
+ }
455
+ /**
456
+ * Search backwards from current position
457
+ */
458
+ searchBackward(text) {
459
+ const lower = text.toLowerCase();
460
+ for (let i = this.position - 1; i >= 0; i--) {
461
+ if (this.entries[i].toLowerCase().includes(lower)) {
462
+ this.position = i;
463
+ return this.entries[i];
464
+ }
465
+ }
466
+ return null;
467
+ }
468
+ /**
469
+ * Search forward from current position
470
+ */
471
+ searchForward(text) {
472
+ const lower = text.toLowerCase();
473
+ for (let i = this.position + 1; i < this.entries.length; i++) {
474
+ if (this.entries[i].toLowerCase().includes(lower)) {
475
+ this.position = i;
476
+ return this.entries[i];
477
+ }
478
+ }
479
+ return null;
480
+ }
481
+ /**
482
+ * Get all entries
483
+ */
484
+ getAll() {
485
+ return [...this.entries];
486
+ }
487
+ /**
488
+ * Get recent entries
489
+ */
490
+ getRecent(count) {
491
+ return this.entries.slice(-count);
492
+ }
493
+ /**
494
+ * Clear history
495
+ */
496
+ clear() {
497
+ this.entries = [];
498
+ this.position = 0;
499
+ this.save();
500
+ }
501
+ /**
502
+ * Get history size
503
+ */
504
+ get size() {
505
+ return this.entries.length;
506
+ }
507
+ /**
508
+ * Get current position
509
+ */
510
+ get currentPosition() {
511
+ return this.position;
512
+ }
513
+ };
514
+
515
+ // src/completions.ts
516
+ import * as fs3 from "fs";
517
+ import * as path3 from "path";
518
+
519
+ // src/formatter.ts
520
+ var colors = {
521
+ reset: "\x1B[0m",
522
+ bold: "\x1B[1m",
523
+ dim: "\x1B[2m",
524
+ italic: "\x1B[3m",
525
+ underline: "\x1B[4m",
526
+ // Foreground colors
527
+ black: "\x1B[30m",
528
+ red: "\x1B[31m",
529
+ green: "\x1B[32m",
530
+ yellow: "\x1B[33m",
531
+ blue: "\x1B[34m",
532
+ magenta: "\x1B[35m",
533
+ cyan: "\x1B[36m",
534
+ white: "\x1B[37m",
535
+ gray: "\x1B[90m",
536
+ // Bright foreground
537
+ brightRed: "\x1B[91m",
538
+ brightGreen: "\x1B[92m",
539
+ brightYellow: "\x1B[93m",
540
+ brightBlue: "\x1B[94m",
541
+ brightMagenta: "\x1B[95m",
542
+ brightCyan: "\x1B[96m",
543
+ brightWhite: "\x1B[97m",
544
+ // Background colors
545
+ bgBlack: "\x1B[40m",
546
+ bgRed: "\x1B[41m",
547
+ bgGreen: "\x1B[42m",
548
+ bgYellow: "\x1B[43m",
549
+ bgBlue: "\x1B[44m",
550
+ bgMagenta: "\x1B[45m",
551
+ bgCyan: "\x1B[46m",
552
+ bgWhite: "\x1B[47m"
553
+ };
554
+ function formatSuccess(message) {
555
+ return `${colors.green}\u2713${colors.reset} ${message}`;
556
+ }
557
+ function formatError(message) {
558
+ return `${colors.red}\u2717 Error:${colors.reset} ${message}`;
559
+ }
560
+ function formatWarning(message) {
561
+ return `${colors.yellow}\u26A0${colors.reset} ${message}`;
562
+ }
563
+ function formatIntent(intent) {
564
+ const lines = [
565
+ "",
566
+ `${colors.bold}Intent: ${colors.cyan}${intent.name}${colors.reset}`,
567
+ colors.gray + "\u2500".repeat(40) + colors.reset
568
+ ];
569
+ if (intent.preconditions.length > 0) {
570
+ lines.push("");
571
+ lines.push(`${colors.bold}Preconditions:${colors.reset}`);
572
+ for (const pre of intent.preconditions) {
573
+ lines.push(` ${colors.magenta}pre:${colors.reset} ${highlightExpression(pre.expression)}`);
574
+ }
575
+ }
576
+ if (intent.postconditions.length > 0) {
577
+ lines.push("");
578
+ lines.push(`${colors.bold}Postconditions:${colors.reset}`);
579
+ for (const post of intent.postconditions) {
580
+ lines.push(` ${colors.magenta}post:${colors.reset} ${highlightExpression(post.expression)}`);
581
+ }
582
+ }
583
+ if (intent.invariants.length > 0) {
584
+ lines.push("");
585
+ lines.push(`${colors.bold}Invariants:${colors.reset}`);
586
+ for (const inv of intent.invariants) {
587
+ lines.push(` ${colors.magenta}invariant:${colors.reset} ${highlightExpression(inv.expression)}`);
588
+ }
589
+ }
590
+ if (intent.scenarios.length > 0) {
591
+ lines.push("");
592
+ lines.push(`${colors.bold}Scenarios:${colors.reset}`);
593
+ for (const scenario of intent.scenarios) {
594
+ lines.push(` ${colors.yellow}${scenario.name}${colors.reset}`);
595
+ }
596
+ }
597
+ lines.push("");
598
+ return lines.join("\n");
599
+ }
600
+ function highlightExpression(expr) {
601
+ return expr.replace(/\b(and|or|not|implies)\b/g, `${colors.yellow}$1${colors.reset}`).replace(/(>=|<=|==|!=|>|<)/g, `${colors.yellow}$1${colors.reset}`).replace(/\b(true|false|null)\b/g, `${colors.magenta}$1${colors.reset}`).replace(/\b(forall|exists|in)\b/g, `${colors.yellow}$1${colors.reset}`).replace(/\b(\d+(?:\.\d+)?)\b/g, `${colors.cyan}$1${colors.reset}`).replace(/"([^"]*)"/g, `${colors.green}"$1"${colors.reset}`).replace(/\.(\w+)\(/g, `.${colors.blue}$1${colors.reset}(`).replace(/\.(\w+)(?!\()/g, `.${colors.cyan}$1${colors.reset}`);
602
+ }
603
+
604
+ // src/commands.ts
605
+ var metaCommands = [
606
+ {
607
+ name: "help",
608
+ aliases: ["h", "?"],
609
+ description: "Show help for commands",
610
+ usage: ".help [command]",
611
+ handler: (args, session) => {
612
+ if (args.length > 0) {
613
+ const cmdName = args[0].toLowerCase();
614
+ const metaCmd = metaCommands.find((c) => c.name === cmdName || c.aliases.includes(cmdName));
615
+ if (metaCmd) {
616
+ return {
617
+ output: [
618
+ `${colors.cyan}.${metaCmd.name}${colors.reset} - ${metaCmd.description}`,
619
+ `Usage: ${metaCmd.usage}`,
620
+ metaCmd.aliases.length > 0 ? `Aliases: ${metaCmd.aliases.map((a) => "." + a).join(", ")}` : ""
621
+ ].filter(Boolean).join("\n")
622
+ };
623
+ }
624
+ const islCmd = islCommands.find((c) => c.name === cmdName || c.aliases.includes(cmdName));
625
+ if (islCmd) {
626
+ return {
627
+ output: [
628
+ `${colors.cyan}:${islCmd.name}${colors.reset} - ${islCmd.description}`,
629
+ `Usage: ${islCmd.usage}`,
630
+ islCmd.aliases.length > 0 ? `Aliases: ${islCmd.aliases.map((a) => ":" + a).join(", ")}` : ""
631
+ ].filter(Boolean).join("\n")
632
+ };
633
+ }
634
+ return { output: formatError(`Unknown command: ${cmdName}`) };
635
+ }
636
+ const lines = [
637
+ "",
638
+ `${colors.bold}Meta Commands${colors.reset} ${colors.gray}(REPL control)${colors.reset}`,
639
+ "",
640
+ ...metaCommands.map((c) => ` ${colors.cyan}.${c.name.padEnd(10)}${colors.reset} ${c.description}`),
641
+ "",
642
+ `${colors.bold}ISL Commands${colors.reset} ${colors.gray}(specification operations)${colors.reset}`,
643
+ "",
644
+ ...islCommands.map((c) => ` ${colors.cyan}:${c.name.padEnd(10)}${colors.reset} ${c.description}`),
645
+ "",
646
+ `${colors.bold}ISL Syntax${colors.reset}`,
647
+ "",
648
+ ` ${colors.yellow}intent${colors.reset} Name {`,
649
+ ` ${colors.magenta}pre${colors.reset}: condition`,
650
+ ` ${colors.magenta}post${colors.reset}: condition`,
651
+ ` }`,
652
+ "",
653
+ `Type ${colors.cyan}.help <command>${colors.reset} for detailed help.`,
654
+ ""
655
+ ];
656
+ return { output: lines.join("\n") };
657
+ }
658
+ },
659
+ {
660
+ name: "exit",
661
+ aliases: ["quit", "q"],
662
+ description: "Exit the REPL",
663
+ usage: ".exit",
664
+ handler: () => {
665
+ return { exit: true };
666
+ }
667
+ },
668
+ {
669
+ name: "clear",
670
+ aliases: ["cls"],
671
+ description: "Clear session state (intents, variables)",
672
+ usage: ".clear",
673
+ handler: (args, session) => {
674
+ session.clear();
675
+ return { output: formatSuccess("Session cleared") };
676
+ }
677
+ },
678
+ {
679
+ name: "history",
680
+ aliases: ["hist"],
681
+ description: "Show command history",
682
+ usage: ".history [n]",
683
+ handler: (args, session) => {
684
+ const count = args.length > 0 ? parseInt(args[0]) : 10;
685
+ const history = session.getHistory(count);
686
+ if (history.length === 0) {
687
+ return { output: "No history." };
688
+ }
689
+ const lines = [
690
+ `${colors.bold}History${colors.reset} (last ${history.length} entries)`,
691
+ "",
692
+ ...history.map((entry, i) => {
693
+ const num = String(i + 1).padStart(3);
694
+ const preview = entry.split("\n")[0];
695
+ const more = entry.includes("\n") ? ` ${colors.gray}...${colors.reset}` : "";
696
+ return ` ${colors.gray}${num}${colors.reset} ${preview}${more}`;
697
+ })
698
+ ];
699
+ return { output: lines.join("\n") };
700
+ }
701
+ }
702
+ ];
703
+ var islCommands = [
704
+ {
705
+ name: "check",
706
+ aliases: ["c"],
707
+ description: "Type check an intent",
708
+ usage: ":check <intent>",
709
+ handler: (args, session) => {
710
+ if (args.length === 0) {
711
+ const intents = session.getAllIntents();
712
+ if (intents.length === 0) {
713
+ return { output: formatWarning("No intents defined. Define one with: intent Name { ... }") };
714
+ }
715
+ const lines2 = [
716
+ formatSuccess("Type check passed"),
717
+ ""
718
+ ];
719
+ for (const intent2 of intents) {
720
+ lines2.push(`${colors.bold}${intent2.name}${colors.reset}`);
721
+ for (const pre of intent2.preconditions) {
722
+ lines2.push(` ${colors.green}\u2713${colors.reset} pre: ${highlightCondition(pre.expression)}`);
723
+ }
724
+ for (const post of intent2.postconditions) {
725
+ lines2.push(` ${colors.green}\u2713${colors.reset} post: ${highlightCondition(post.expression)}`);
726
+ }
727
+ lines2.push("");
728
+ }
729
+ return { output: lines2.join("\n") };
730
+ }
731
+ const intentName = args[0];
732
+ const intent = session.getIntent(intentName);
733
+ if (!intent) {
734
+ const available = session.getIntentNames().join(", ") || "(none)";
735
+ return {
736
+ output: formatError(`Unknown intent: ${intentName}
737
+ Available: ${available}`)
738
+ };
739
+ }
740
+ const lines = [
741
+ formatSuccess("Type check passed"),
742
+ ""
743
+ ];
744
+ for (const pre of intent.preconditions) {
745
+ lines.push(` pre: ${highlightCondition(pre.expression)} ${colors.green}\u2713${colors.reset}`);
746
+ }
747
+ for (const post of intent.postconditions) {
748
+ lines.push(` post: ${highlightCondition(post.expression)} ${colors.green}\u2713${colors.reset}`);
749
+ }
750
+ return { output: lines.join("\n") };
751
+ }
752
+ },
753
+ {
754
+ name: "gen",
755
+ aliases: ["generate", "g"],
756
+ description: "Generate code from an intent",
757
+ usage: ":gen <target> <intent>",
758
+ handler: (args, session) => {
759
+ if (args.length < 2) {
760
+ return {
761
+ output: [
762
+ "Usage: :gen <target> <intent>",
763
+ "",
764
+ "Targets:",
765
+ " typescript Generate TypeScript contract",
766
+ " rust Generate Rust contract",
767
+ " go Generate Go contract",
768
+ " openapi Generate OpenAPI schema"
769
+ ].join("\n")
770
+ };
771
+ }
772
+ const target = args[0].toLowerCase();
773
+ const intentName = args[1];
774
+ const intent = session.getIntent(intentName);
775
+ if (!intent) {
776
+ const available = session.getIntentNames().join(", ") || "(none)";
777
+ return {
778
+ output: formatError(`Unknown intent: ${intentName}
779
+ Available: ${available}`)
780
+ };
781
+ }
782
+ switch (target) {
783
+ case "typescript":
784
+ case "ts":
785
+ return { output: generateTypeScript(intent) };
786
+ case "rust":
787
+ case "rs":
788
+ return { output: generateRust(intent) };
789
+ case "go":
790
+ return { output: generateGo(intent) };
791
+ case "openapi":
792
+ case "oas":
793
+ return { output: generateOpenAPI(intent) };
794
+ default:
795
+ return {
796
+ output: formatError(`Unknown target: ${target}
797
+ Available: typescript, rust, go, openapi`)
798
+ };
799
+ }
800
+ }
801
+ },
802
+ {
803
+ name: "load",
804
+ aliases: ["l"],
805
+ description: "Load intents from a file",
806
+ usage: ":load <file.isl>",
807
+ handler: (args, session) => {
808
+ if (args.length === 0) {
809
+ return { output: "Usage: :load <file.isl>" };
810
+ }
811
+ const filePath = args[0];
812
+ let result = { intents: [], errors: [] };
813
+ session.loadFile(filePath).then((r) => {
814
+ result = r;
815
+ }).catch((e) => {
816
+ result.errors.push(String(e));
817
+ });
818
+ const fs4 = __require("fs");
819
+ const path4 = __require("path");
820
+ try {
821
+ const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
822
+ if (!fs4.existsSync(resolvedPath)) {
823
+ return { output: formatError(`File not found: ${resolvedPath}`) };
824
+ }
825
+ const content = fs4.readFileSync(resolvedPath, "utf-8");
826
+ const intentRegex = /(?:intent|behavior)\s+(\w+)\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}/g;
827
+ let match;
828
+ let count = 0;
829
+ while ((match = intentRegex.exec(content)) !== null) {
830
+ const intent = session.parseIntent(match[0]);
831
+ if (intent) {
832
+ session.defineIntent(intent);
833
+ count++;
834
+ }
835
+ }
836
+ if (count === 0) {
837
+ return { output: formatWarning("No intents found in file") };
838
+ }
839
+ return {
840
+ output: formatSuccess(`Loaded ${count} intent(s) from ${filePath}`)
841
+ };
842
+ } catch (error) {
843
+ return {
844
+ output: formatError(`Failed to load: ${error instanceof Error ? error.message : String(error)}`)
845
+ };
846
+ }
847
+ }
848
+ },
849
+ {
850
+ name: "list",
851
+ aliases: ["ls"],
852
+ description: "List all defined intents",
853
+ usage: ":list",
854
+ handler: (args, session) => {
855
+ const intents = session.getAllIntents();
856
+ if (intents.length === 0) {
857
+ return { output: "No intents defined." };
858
+ }
859
+ const lines = [""];
860
+ for (const intent of intents) {
861
+ const preCount = intent.preconditions.length;
862
+ const postCount = intent.postconditions.length;
863
+ const invCount = intent.invariants.length;
864
+ const parts = [];
865
+ if (preCount > 0) parts.push(`${preCount} pre`);
866
+ if (postCount > 0) parts.push(`${postCount} post`);
867
+ if (invCount > 0) parts.push(`${invCount} invariant`);
868
+ const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
869
+ lines.push(` ${colors.cyan}${intent.name}${colors.reset}${summary}`);
870
+ }
871
+ lines.push("");
872
+ return { output: lines.join("\n") };
873
+ }
874
+ },
875
+ {
876
+ name: "inspect",
877
+ aliases: ["i", "show"],
878
+ description: "Show full details of an intent",
879
+ usage: ":inspect <intent>",
880
+ handler: (args, session) => {
881
+ if (args.length === 0) {
882
+ const summary = session.getSummary();
883
+ const files = session.getLoadedFiles();
884
+ const lines = [
885
+ "",
886
+ `${colors.bold}Session Summary${colors.reset}`,
887
+ "",
888
+ ` Intents: ${summary.intentCount}`,
889
+ ` Variables: ${summary.variableCount}`,
890
+ ` History: ${summary.historyCount} entries`
891
+ ];
892
+ if (files.length > 0) {
893
+ lines.push("");
894
+ lines.push(`${colors.bold}Loaded Files${colors.reset}`);
895
+ for (const file of files) {
896
+ lines.push(` ${file}`);
897
+ }
898
+ }
899
+ lines.push("");
900
+ return { output: lines.join("\n") };
901
+ }
902
+ const intentName = args[0];
903
+ const intent = session.getIntent(intentName);
904
+ if (!intent) {
905
+ const available = session.getIntentNames().join(", ") || "(none)";
906
+ return {
907
+ output: formatError(`Unknown intent: ${intentName}
908
+ Available: ${available}`)
909
+ };
910
+ }
911
+ return { output: formatIntent(intent) };
912
+ }
913
+ },
914
+ {
915
+ name: "export",
916
+ aliases: ["save"],
917
+ description: "Export intents to a file",
918
+ usage: ":export <file.isl>",
919
+ handler: (args, session) => {
920
+ if (args.length === 0) {
921
+ return { output: "Usage: :export <file.isl>" };
922
+ }
923
+ const filePath = args[0];
924
+ const fs4 = __require("fs");
925
+ const path4 = __require("path");
926
+ try {
927
+ const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
928
+ const intents = session.getAllIntents();
929
+ if (intents.length === 0) {
930
+ return { output: formatWarning("No intents to export") };
931
+ }
932
+ const lines = [];
933
+ lines.push("// Exported ISL intents");
934
+ lines.push(`// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
935
+ lines.push("");
936
+ for (const intent of intents) {
937
+ lines.push(`intent ${intent.name} {`);
938
+ for (const pre of intent.preconditions) {
939
+ lines.push(` pre: ${pre.expression}`);
940
+ }
941
+ for (const post of intent.postconditions) {
942
+ lines.push(` post: ${post.expression}`);
943
+ }
944
+ for (const inv of intent.invariants) {
945
+ lines.push(` invariant: ${inv.expression}`);
946
+ }
947
+ lines.push("}");
948
+ lines.push("");
949
+ }
950
+ fs4.writeFileSync(resolvedPath, lines.join("\n"));
951
+ return { output: formatSuccess(`Exported ${intents.length} intent(s) to ${filePath}`) };
952
+ } catch (error) {
953
+ return {
954
+ output: formatError(`Failed to export: ${error instanceof Error ? error.message : String(error)}`)
955
+ };
956
+ }
957
+ }
958
+ }
959
+ ];
960
+ function generateTypeScript(intent) {
961
+ const lines = [
962
+ `${colors.gray}// Generated TypeScript${colors.reset}`,
963
+ `interface ${intent.name}Contract {`
964
+ ];
965
+ if (intent.preconditions.length > 0) {
966
+ for (const pre of intent.preconditions) {
967
+ const varName = extractVariableName(pre.expression);
968
+ const type = inferType(pre.expression);
969
+ lines.push(` pre: (${varName}: ${type}) => boolean;`);
970
+ }
971
+ }
972
+ if (intent.postconditions.length > 0) {
973
+ for (const post of intent.postconditions) {
974
+ const varName = extractVariableName(post.expression);
975
+ const type = inferType(post.expression);
976
+ lines.push(` post: (${varName}: ${type}) => boolean;`);
977
+ }
978
+ }
979
+ lines.push("}");
980
+ return lines.join("\n");
981
+ }
982
+ function generateRust(intent) {
983
+ const lines = [
984
+ `${colors.gray}// Generated Rust${colors.reset}`,
985
+ `pub trait ${intent.name}Contract {`
986
+ ];
987
+ if (intent.preconditions.length > 0) {
988
+ for (const pre of intent.preconditions) {
989
+ const varName = extractVariableName(pre.expression);
990
+ const type = inferRustType(pre.expression);
991
+ lines.push(` fn check_pre(&self, ${varName}: ${type}) -> bool;`);
992
+ }
993
+ }
994
+ if (intent.postconditions.length > 0) {
995
+ for (const post of intent.postconditions) {
996
+ const varName = extractVariableName(post.expression);
997
+ const type = inferRustType(post.expression);
998
+ lines.push(` fn check_post(&self, ${varName}: ${type}) -> bool;`);
999
+ }
1000
+ }
1001
+ lines.push("}");
1002
+ return lines.join("\n");
1003
+ }
1004
+ function generateGo(intent) {
1005
+ const lines = [
1006
+ `${colors.gray}// Generated Go${colors.reset}`,
1007
+ `type ${intent.name}Contract interface {`
1008
+ ];
1009
+ if (intent.preconditions.length > 0) {
1010
+ for (const pre of intent.preconditions) {
1011
+ const varName = extractVariableName(pre.expression);
1012
+ const type = inferGoType(pre.expression);
1013
+ lines.push(` CheckPre(${varName} ${type}) bool`);
1014
+ }
1015
+ }
1016
+ if (intent.postconditions.length > 0) {
1017
+ for (const post of intent.postconditions) {
1018
+ const varName = extractVariableName(post.expression);
1019
+ const type = inferGoType(post.expression);
1020
+ lines.push(` CheckPost(${varName} ${type}) bool`);
1021
+ }
1022
+ }
1023
+ lines.push("}");
1024
+ return lines.join("\n");
1025
+ }
1026
+ function generateOpenAPI(intent) {
1027
+ const lines = [
1028
+ `${colors.gray}# Generated OpenAPI${colors.reset}`,
1029
+ `openapi: 3.0.0`,
1030
+ `paths:`,
1031
+ ` /${intent.name.toLowerCase()}:`,
1032
+ ` post:`,
1033
+ ` summary: ${intent.name}`,
1034
+ ` requestBody:`,
1035
+ ` content:`,
1036
+ ` application/json:`,
1037
+ ` schema:`,
1038
+ ` type: object`
1039
+ ];
1040
+ if (intent.preconditions.length > 0) {
1041
+ lines.push(` # Preconditions:`);
1042
+ for (const pre of intent.preconditions) {
1043
+ lines.push(` # - ${pre.expression}`);
1044
+ }
1045
+ }
1046
+ lines.push(` responses:`);
1047
+ lines.push(` '200':`);
1048
+ lines.push(` description: Success`);
1049
+ if (intent.postconditions.length > 0) {
1050
+ lines.push(` # Postconditions:`);
1051
+ for (const post of intent.postconditions) {
1052
+ lines.push(` # - ${post.expression}`);
1053
+ }
1054
+ }
1055
+ return lines.join("\n");
1056
+ }
1057
+ function extractVariableName(expression) {
1058
+ const match = expression.match(/^(\w+)/);
1059
+ return match ? match[1] : "input";
1060
+ }
1061
+ function inferType(expression) {
1062
+ if (expression.includes(".length")) return "string";
1063
+ if (expression.includes(".startsWith")) return "string";
1064
+ if (expression.includes(".endsWith")) return "string";
1065
+ if (expression.includes(".includes")) return "string";
1066
+ if (expression.includes(" > ") || expression.includes(" < ")) return "number";
1067
+ return "unknown";
1068
+ }
1069
+ function inferRustType(expression) {
1070
+ if (expression.includes(".length") || expression.includes(".len()")) return "&str";
1071
+ if (expression.includes(".starts_with")) return "&str";
1072
+ if (expression.includes(" > ") || expression.includes(" < ")) return "i32";
1073
+ return "&str";
1074
+ }
1075
+ function inferGoType(expression) {
1076
+ if (expression.includes(".length") || expression.includes("len(")) return "string";
1077
+ if (expression.includes(" > ") || expression.includes(" < ")) return "int";
1078
+ return "string";
1079
+ }
1080
+ function highlightCondition(expression) {
1081
+ return expression.replace(
1082
+ /(\w+)\s*(>|<|>=|<=|==|!=)\s*(\d+|"[^"]*")/g,
1083
+ `${colors.blue}$1${colors.reset} ${colors.yellow}$2${colors.reset} ${colors.green}$3${colors.reset}`
1084
+ ).replace(/\b(true|false)\b/g, `${colors.magenta}$1${colors.reset}`).replace(/\.(length|startsWith|endsWith|includes)/g, `.${colors.cyan}$1${colors.reset}`);
1085
+ }
1086
+ function levenshteinDistance(a, b) {
1087
+ const matrix = [];
1088
+ for (let i = 0; i <= b.length; i++) {
1089
+ matrix[i] = [i];
1090
+ }
1091
+ for (let j = 0; j <= a.length; j++) {
1092
+ matrix[0][j] = j;
1093
+ }
1094
+ for (let i = 1; i <= b.length; i++) {
1095
+ for (let j = 1; j <= a.length; j++) {
1096
+ const cost = a[j - 1] === b[i - 1] ? 0 : 1;
1097
+ matrix[i][j] = Math.min(
1098
+ matrix[i - 1][j] + 1,
1099
+ matrix[i][j - 1] + 1,
1100
+ matrix[i - 1][j - 1] + cost
1101
+ );
1102
+ }
1103
+ }
1104
+ return matrix[b.length][a.length];
1105
+ }
1106
+ function findSimilarCommand(input, type) {
1107
+ const commands = type === "meta" ? metaCommands : islCommands;
1108
+ const names = commands.flatMap((c) => [c.name, ...c.aliases]);
1109
+ let bestMatch = null;
1110
+ let bestDistance = Infinity;
1111
+ for (const name of names) {
1112
+ const distance = levenshteinDistance(input.toLowerCase(), name.toLowerCase());
1113
+ if (distance < bestDistance && distance <= 2) {
1114
+ bestDistance = distance;
1115
+ bestMatch = name;
1116
+ }
1117
+ }
1118
+ return bestMatch;
1119
+ }
1120
+
1121
+ // src/completions.ts
1122
+ var KEYWORDS = [
1123
+ { text: "intent", type: "keyword", description: "Define an intent" },
1124
+ { text: "behavior", type: "keyword", description: "Define a behavior" },
1125
+ { text: "pre", type: "keyword", description: "Precondition" },
1126
+ { text: "post", type: "keyword", description: "Postcondition" },
1127
+ { text: "invariant", type: "keyword", description: "Invariant" },
1128
+ { text: "true", type: "keyword", description: "Boolean true" },
1129
+ { text: "false", type: "keyword", description: "Boolean false" },
1130
+ { text: "null", type: "keyword", description: "Null value" },
1131
+ { text: "and", type: "keyword", description: "Logical AND" },
1132
+ { text: "or", type: "keyword", description: "Logical OR" },
1133
+ { text: "not", type: "keyword", description: "Logical NOT" },
1134
+ { text: "implies", type: "keyword", description: "Logical implication" },
1135
+ { text: "forall", type: "keyword", description: "Universal quantifier" },
1136
+ { text: "exists", type: "keyword", description: "Existential quantifier" },
1137
+ { text: "in", type: "keyword", description: "Membership test" }
1138
+ ];
1139
+ var META_COMMANDS = metaCommands.map((cmd) => ({
1140
+ text: `.${cmd.name}`,
1141
+ type: "command",
1142
+ description: cmd.description
1143
+ }));
1144
+ var ISL_COMMANDS = islCommands.map((cmd) => ({
1145
+ text: `:${cmd.name}`,
1146
+ type: "command",
1147
+ description: cmd.description
1148
+ }));
1149
+ var COMMANDS = [...META_COMMANDS, ...ISL_COMMANDS];
1150
+ var GEN_TARGETS = [
1151
+ { text: "typescript", type: "keyword", description: "Generate TypeScript contract" },
1152
+ { text: "rust", type: "keyword", description: "Generate Rust contract" },
1153
+ { text: "go", type: "keyword", description: "Generate Go contract" },
1154
+ { text: "openapi", type: "keyword", description: "Generate OpenAPI schema" }
1155
+ ];
1156
+ var CompletionProvider = class {
1157
+ constructor(session) {
1158
+ this.session = session;
1159
+ }
1160
+ /**
1161
+ * Update the session reference
1162
+ */
1163
+ setSession(session) {
1164
+ this.session = session;
1165
+ }
1166
+ /**
1167
+ * Get completions for a line
1168
+ */
1169
+ complete(line) {
1170
+ const trimmed = line.trimStart();
1171
+ if (trimmed.startsWith(".")) {
1172
+ return this.completeMetaCommand(trimmed);
1173
+ }
1174
+ if (trimmed.startsWith(":")) {
1175
+ return this.completeISLCommand(trimmed);
1176
+ }
1177
+ return this.completeExpression(trimmed);
1178
+ }
1179
+ /**
1180
+ * Complete meta commands
1181
+ */
1182
+ completeMetaCommand(line) {
1183
+ const parts = line.slice(1).split(/\s+/);
1184
+ const cmdPart = parts[0] || "";
1185
+ if (parts.length === 1) {
1186
+ const matches = META_COMMANDS.filter(
1187
+ (c) => c.text.toLowerCase().startsWith(`.${cmdPart.toLowerCase()}`)
1188
+ );
1189
+ return [matches.length > 0 ? matches : META_COMMANDS, "." + cmdPart];
1190
+ }
1191
+ return [[], line];
1192
+ }
1193
+ /**
1194
+ * Complete ISL commands
1195
+ */
1196
+ completeISLCommand(line) {
1197
+ const parts = line.slice(1).split(/\s+/);
1198
+ const cmdPart = parts[0] || "";
1199
+ const args = parts.slice(1);
1200
+ if (parts.length === 1) {
1201
+ const matches = ISL_COMMANDS.filter(
1202
+ (c) => c.text.toLowerCase().startsWith(`:${cmdPart.toLowerCase()}`)
1203
+ );
1204
+ return [matches.length > 0 ? matches : ISL_COMMANDS, ":" + cmdPart];
1205
+ }
1206
+ const cmd = cmdPart.toLowerCase();
1207
+ switch (cmd) {
1208
+ case "gen":
1209
+ case "generate":
1210
+ case "g":
1211
+ return this.completeGenCommand(args);
1212
+ case "check":
1213
+ case "c":
1214
+ case "inspect":
1215
+ case "i":
1216
+ case "show":
1217
+ return this.completeIntentName(args[0] || "");
1218
+ case "load":
1219
+ case "l":
1220
+ case "export":
1221
+ case "save":
1222
+ return this.completeFilePath(args[0] || "");
1223
+ default:
1224
+ return [[], line];
1225
+ }
1226
+ }
1227
+ /**
1228
+ * Complete :gen command arguments
1229
+ */
1230
+ completeGenCommand(args) {
1231
+ if (args.length <= 1) {
1232
+ const partial = args[0] || "";
1233
+ const matches = GEN_TARGETS.filter(
1234
+ (t) => t.text.toLowerCase().startsWith(partial.toLowerCase())
1235
+ );
1236
+ return [matches.length > 0 ? matches : GEN_TARGETS, partial];
1237
+ }
1238
+ return this.completeIntentName(args[1] || "");
1239
+ }
1240
+ /**
1241
+ * Complete intent names
1242
+ */
1243
+ completeIntentName(partial) {
1244
+ const intents = this.session.getAllIntents();
1245
+ const items = intents.map((intent) => ({
1246
+ text: intent.name,
1247
+ type: "intent",
1248
+ description: `${intent.preconditions.length} pre, ${intent.postconditions.length} post`
1249
+ }));
1250
+ const matches = items.filter(
1251
+ (i) => i.text.toLowerCase().startsWith(partial.toLowerCase())
1252
+ );
1253
+ return [matches.length > 0 ? matches : items, partial];
1254
+ }
1255
+ /**
1256
+ * Complete file paths
1257
+ */
1258
+ completeFilePath(partial) {
1259
+ try {
1260
+ const dir = path3.dirname(partial) || ".";
1261
+ const base = path3.basename(partial);
1262
+ const resolvedDir = path3.resolve(this.session.getConfig().cwd || process.cwd(), dir);
1263
+ if (!fs3.existsSync(resolvedDir)) {
1264
+ return [[], partial];
1265
+ }
1266
+ const entries = fs3.readdirSync(resolvedDir, { withFileTypes: true });
1267
+ const items = entries.filter((e) => {
1268
+ const name = e.name.toLowerCase();
1269
+ return name.startsWith(base.toLowerCase()) && (e.isDirectory() || name.endsWith(".isl"));
1270
+ }).map((e) => ({
1271
+ text: path3.join(dir, e.name + (e.isDirectory() ? "/" : "")),
1272
+ type: "file",
1273
+ description: e.isDirectory() ? "Directory" : "ISL file"
1274
+ }));
1275
+ return [items, partial];
1276
+ } catch {
1277
+ return [[], partial];
1278
+ }
1279
+ }
1280
+ /**
1281
+ * Complete expressions
1282
+ */
1283
+ completeExpression(line) {
1284
+ const items = [...KEYWORDS];
1285
+ for (const intent of this.session.getAllIntents()) {
1286
+ items.push({
1287
+ text: intent.name,
1288
+ type: "intent",
1289
+ description: "Defined intent"
1290
+ });
1291
+ }
1292
+ for (const [name] of this.session.getAllVariables()) {
1293
+ items.push({
1294
+ text: name,
1295
+ type: "variable",
1296
+ description: "Variable"
1297
+ });
1298
+ }
1299
+ items.push({
1300
+ text: "_",
1301
+ type: "variable",
1302
+ description: "Last result"
1303
+ });
1304
+ const match = line.match(/[\w.]+$/);
1305
+ const partial = match ? match[0] : "";
1306
+ const matches = items.filter(
1307
+ (i) => i.text.toLowerCase().startsWith(partial.toLowerCase())
1308
+ );
1309
+ return [matches.length > 0 ? matches : items, partial];
1310
+ }
1311
+ /**
1312
+ * Get all available completions (for help)
1313
+ */
1314
+ getAllCompletions() {
1315
+ return {
1316
+ metaCommands: META_COMMANDS,
1317
+ islCommands: ISL_COMMANDS,
1318
+ keywords: KEYWORDS,
1319
+ intents: this.session.getAllIntents().map((i) => ({
1320
+ text: i.name,
1321
+ type: "intent",
1322
+ description: `${i.preconditions.length} pre, ${i.postconditions.length} post`
1323
+ }))
1324
+ };
1325
+ }
1326
+ };
1327
+ function createCompleter(provider) {
1328
+ return (line) => {
1329
+ const [items, partial] = provider.complete(line);
1330
+ const completions = items.map((i) => i.text);
1331
+ return [completions, partial];
1332
+ };
1333
+ }
1334
+
1335
+ // src/repl.ts
1336
+ var VERSION = "0.1.0";
1337
+ var PROMPT = `${colors.cyan}isl>${colors.reset} `;
1338
+ var CONTINUATION_PROMPT = `${colors.cyan}...>${colors.reset} `;
1339
+ var ISLREPL = class {
1340
+ session;
1341
+ history;
1342
+ completionProvider;
1343
+ rl = null;
1344
+ buffer = [];
1345
+ braceCount = 0;
1346
+ options;
1347
+ running = false;
1348
+ constructor(options = {}) {
1349
+ this.options = {
1350
+ colors: options.colors !== false,
1351
+ verbose: options.verbose ?? false,
1352
+ historyFile: options.historyFile
1353
+ };
1354
+ this.session = new Session({ colors: this.options.colors });
1355
+ this.history = new History({
1356
+ historyFile: this.options.historyFile
1357
+ });
1358
+ this.completionProvider = new CompletionProvider(this.session);
1359
+ }
1360
+ /**
1361
+ * Start the REPL
1362
+ */
1363
+ start() {
1364
+ if (this.running) return;
1365
+ this.running = true;
1366
+ this.history.load();
1367
+ this.rl = readline.createInterface({
1368
+ input: process.stdin,
1369
+ output: process.stdout,
1370
+ prompt: PROMPT,
1371
+ completer: createCompleter(this.completionProvider),
1372
+ terminal: true
1373
+ });
1374
+ this.printBanner();
1375
+ this.rl.prompt();
1376
+ this.rl.on("line", (line) => {
1377
+ this.handleLine(line);
1378
+ if (this.rl && this.running) {
1379
+ this.rl.setPrompt(this.braceCount > 0 ? CONTINUATION_PROMPT : PROMPT);
1380
+ this.rl.prompt();
1381
+ }
1382
+ });
1383
+ this.rl.on("close", () => {
1384
+ this.exit();
1385
+ });
1386
+ this.rl.on("SIGINT", () => {
1387
+ if (this.buffer.length > 0) {
1388
+ this.buffer = [];
1389
+ this.braceCount = 0;
1390
+ console.log("\n" + formatWarning("Input cancelled"));
1391
+ this.rl.setPrompt(PROMPT);
1392
+ this.rl.prompt();
1393
+ } else {
1394
+ console.log("\n" + formatWarning("Use .exit or :quit to exit"));
1395
+ this.rl.prompt();
1396
+ }
1397
+ });
1398
+ }
1399
+ /**
1400
+ * Print the welcome banner
1401
+ */
1402
+ printBanner() {
1403
+ const banner = `
1404
+ ${colors.cyan}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1405
+ \u2551 \u2551
1406
+ \u2551 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2551
1407
+ \u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2551
1408
+ \u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2551
1409
+ \u2551 \u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2551
1410
+ \u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
1411
+ \u2551 \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2551
1412
+ \u2551 \u2551
1413
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${colors.reset}
1414
+
1415
+ ${colors.bold}ISL v${VERSION}${colors.reset} \u2014 Intent Specification Language
1416
+ Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${colors.reset} to quit
1417
+ `;
1418
+ console.log(banner);
1419
+ }
1420
+ /**
1421
+ * Handle a line of input
1422
+ */
1423
+ handleLine(line) {
1424
+ const trimmed = line.trim();
1425
+ if (!trimmed && this.buffer.length === 0) {
1426
+ return;
1427
+ }
1428
+ if (trimmed.startsWith(".") && this.buffer.length === 0) {
1429
+ this.handleMetaCommand(trimmed);
1430
+ return;
1431
+ }
1432
+ if (trimmed.startsWith(":") && this.buffer.length === 0) {
1433
+ this.handleISLCommand(trimmed);
1434
+ return;
1435
+ }
1436
+ this.braceCount += (line.match(/\{/g) || []).length;
1437
+ this.braceCount -= (line.match(/\}/g) || []).length;
1438
+ this.buffer.push(line);
1439
+ if (this.braceCount <= 0) {
1440
+ const code = this.buffer.join("\n");
1441
+ this.buffer = [];
1442
+ this.braceCount = 0;
1443
+ this.history.add(code);
1444
+ this.session.addToHistory(code);
1445
+ this.evaluate(code);
1446
+ }
1447
+ }
1448
+ /**
1449
+ * Handle a meta command (. prefix)
1450
+ */
1451
+ handleMetaCommand(input) {
1452
+ const parts = input.slice(1).split(/\s+/);
1453
+ const cmdName = parts[0]?.toLowerCase() || "";
1454
+ const args = parts.slice(1);
1455
+ const command = metaCommands.find(
1456
+ (c) => c.name === cmdName || c.aliases.includes(cmdName)
1457
+ );
1458
+ if (command) {
1459
+ this.history.add(input);
1460
+ const result = command.handler(args, this.session, this);
1461
+ if (result.output) {
1462
+ console.log(result.output);
1463
+ }
1464
+ if (result.exit) {
1465
+ this.exit();
1466
+ }
1467
+ } else {
1468
+ const suggestion = findSimilarCommand(cmdName, "meta");
1469
+ if (suggestion) {
1470
+ console.log(formatError(`Unknown command: .${cmdName}`));
1471
+ console.log(formatWarning(`Did you mean: .${suggestion}?`));
1472
+ } else {
1473
+ console.log(formatError(`Unknown command: .${cmdName}`));
1474
+ console.log(`Type ${colors.cyan}.help${colors.reset} for available commands`);
1475
+ }
1476
+ }
1477
+ }
1478
+ /**
1479
+ * Handle an ISL command (: prefix)
1480
+ */
1481
+ handleISLCommand(input) {
1482
+ const parts = input.slice(1).split(/\s+/);
1483
+ const cmdName = parts[0]?.toLowerCase() || "";
1484
+ const args = parts.slice(1);
1485
+ const command = islCommands.find(
1486
+ (c) => c.name === cmdName || c.aliases.includes(cmdName)
1487
+ );
1488
+ if (command) {
1489
+ this.history.add(input);
1490
+ const result = command.handler(args, this.session, this);
1491
+ if (result.output) {
1492
+ console.log(result.output);
1493
+ }
1494
+ } else {
1495
+ const suggestion = findSimilarCommand(cmdName, "isl");
1496
+ if (suggestion) {
1497
+ console.log(formatError(`Unknown command: :${cmdName}`));
1498
+ console.log(formatWarning(`Did you mean: :${suggestion}?`));
1499
+ } else {
1500
+ console.log(formatError(`Unknown command: :${cmdName}`));
1501
+ console.log(`Type ${colors.cyan}.help${colors.reset} for available commands`);
1502
+ }
1503
+ }
1504
+ }
1505
+ /**
1506
+ * Evaluate ISL code
1507
+ */
1508
+ evaluate(code) {
1509
+ try {
1510
+ const trimmed = code.trim();
1511
+ if (trimmed.startsWith("intent ")) {
1512
+ this.evaluateIntent(trimmed);
1513
+ return;
1514
+ }
1515
+ if (trimmed.startsWith("behavior ")) {
1516
+ this.evaluateIntent(trimmed);
1517
+ return;
1518
+ }
1519
+ console.log(formatWarning(`Cannot evaluate: ${trimmed.split("\n")[0]}...`));
1520
+ console.log(`Use ${colors.cyan}intent Name { ... }${colors.reset} to define an intent`);
1521
+ } catch (error) {
1522
+ this.printError(error);
1523
+ }
1524
+ }
1525
+ /**
1526
+ * Evaluate an intent definition
1527
+ */
1528
+ evaluateIntent(code) {
1529
+ const intent = this.session.parseIntent(code);
1530
+ if (intent) {
1531
+ this.session.defineIntent(intent);
1532
+ const preCount = intent.preconditions.length;
1533
+ const postCount = intent.postconditions.length;
1534
+ const invCount = intent.invariants.length;
1535
+ const parts = [];
1536
+ if (preCount > 0) parts.push(`${preCount} pre`);
1537
+ if (postCount > 0) parts.push(`${postCount} post`);
1538
+ if (invCount > 0) parts.push(`${invCount} invariant`);
1539
+ const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
1540
+ console.log(formatSuccess(`Intent '${intent.name}' defined${summary}`));
1541
+ } else {
1542
+ const behaviorMatch = code.match(/^behavior\s+(\w+)\s*\{([\s\S]*)\}$/);
1543
+ if (behaviorMatch) {
1544
+ const name = behaviorMatch[1];
1545
+ const body = behaviorMatch[2];
1546
+ const intent2 = {
1547
+ name,
1548
+ preconditions: [],
1549
+ postconditions: [],
1550
+ invariants: [],
1551
+ scenarios: [],
1552
+ rawSource: code
1553
+ };
1554
+ const preSection = body.match(/pre(?:conditions)?\s*\{([^}]*)\}/s);
1555
+ if (preSection) {
1556
+ const conditions = preSection[1].trim().split("\n").map((l) => l.trim()).filter(Boolean);
1557
+ for (const cond of conditions) {
1558
+ const expr = cond.replace(/^-\s*/, "").trim();
1559
+ if (expr) {
1560
+ intent2.preconditions.push({ expression: expr });
1561
+ }
1562
+ }
1563
+ }
1564
+ const postSection = body.match(/post(?:conditions)?\s*\{([^}]*)\}/s);
1565
+ if (postSection) {
1566
+ const conditions = postSection[1].trim().split("\n").map((l) => l.trim()).filter(Boolean);
1567
+ for (const cond of conditions) {
1568
+ const expr = cond.replace(/^-\s*/, "").trim();
1569
+ if (expr) {
1570
+ intent2.postconditions.push({ expression: expr });
1571
+ }
1572
+ }
1573
+ }
1574
+ this.session.defineIntent(intent2);
1575
+ const preCount = intent2.preconditions.length;
1576
+ const postCount = intent2.postconditions.length;
1577
+ const parts = [];
1578
+ if (preCount > 0) parts.push(`${preCount} pre`);
1579
+ if (postCount > 0) parts.push(`${postCount} post`);
1580
+ const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
1581
+ console.log(formatSuccess(`Intent '${intent2.name}' defined${summary}`));
1582
+ } else {
1583
+ this.printParseError(code, "Failed to parse intent definition");
1584
+ }
1585
+ }
1586
+ }
1587
+ /**
1588
+ * Print a parse error with location info
1589
+ */
1590
+ printParseError(code, message, line, column) {
1591
+ console.log(formatError(message));
1592
+ if (line !== void 0 && column !== void 0) {
1593
+ const lines = code.split("\n");
1594
+ const errorLine = lines[line - 1] || "";
1595
+ console.log(` ${colors.gray}${line} |${colors.reset} ${errorLine}`);
1596
+ console.log(` ${colors.gray}${" ".repeat(String(line).length)} |${colors.reset} ${" ".repeat(column - 1)}${colors.red}^${colors.reset}`);
1597
+ } else {
1598
+ const firstLine = code.split("\n")[0];
1599
+ if (firstLine) {
1600
+ console.log(` ${colors.gray}>${colors.reset} ${firstLine}`);
1601
+ }
1602
+ }
1603
+ }
1604
+ /**
1605
+ * Print an error
1606
+ */
1607
+ printError(error) {
1608
+ if (error instanceof Error) {
1609
+ console.log(formatError(error.message));
1610
+ if (this.options.verbose && error.stack) {
1611
+ console.log(colors.gray + error.stack + colors.reset);
1612
+ }
1613
+ } else {
1614
+ console.log(formatError(String(error)));
1615
+ }
1616
+ }
1617
+ /**
1618
+ * Exit the REPL
1619
+ */
1620
+ exit() {
1621
+ this.running = false;
1622
+ this.history.save();
1623
+ console.log(`
1624
+ ${colors.yellow}Goodbye!${colors.reset}`);
1625
+ if (this.rl) {
1626
+ this.rl.close();
1627
+ }
1628
+ process.exit(0);
1629
+ }
1630
+ /**
1631
+ * Get the session
1632
+ */
1633
+ getSession() {
1634
+ return this.session;
1635
+ }
1636
+ /**
1637
+ * Get history
1638
+ */
1639
+ getHistory() {
1640
+ return this.history;
1641
+ }
1642
+ /**
1643
+ * Execute a single command and return result (for testing)
1644
+ */
1645
+ async executeOnce(input) {
1646
+ const trimmed = input.trim();
1647
+ if (trimmed.startsWith(".")) {
1648
+ const parts = trimmed.slice(1).split(/\s+/);
1649
+ const cmdName = parts[0]?.toLowerCase() || "";
1650
+ const args = parts.slice(1);
1651
+ const command = metaCommands.find(
1652
+ (c) => c.name === cmdName || c.aliases.includes(cmdName)
1653
+ );
1654
+ if (command) {
1655
+ const result = command.handler(args, this.session, this);
1656
+ return { success: true, output: result.output };
1657
+ }
1658
+ return { success: false, error: `Unknown command: .${cmdName}` };
1659
+ }
1660
+ if (trimmed.startsWith(":")) {
1661
+ const parts = trimmed.slice(1).split(/\s+/);
1662
+ const cmdName = parts[0]?.toLowerCase() || "";
1663
+ const args = parts.slice(1);
1664
+ const command = islCommands.find(
1665
+ (c) => c.name === cmdName || c.aliases.includes(cmdName)
1666
+ );
1667
+ if (command) {
1668
+ const result = command.handler(args, this.session, this);
1669
+ return { success: true, output: result.output };
1670
+ }
1671
+ return { success: false, error: `Unknown command: :${cmdName}` };
1672
+ }
1673
+ if (trimmed.startsWith("intent ") || trimmed.startsWith("behavior ")) {
1674
+ const intent = this.session.parseIntent(trimmed);
1675
+ if (intent) {
1676
+ this.session.defineIntent(intent);
1677
+ return { success: true, output: `Intent '${intent.name}' defined` };
1678
+ }
1679
+ return { success: false, error: "Failed to parse intent" };
1680
+ }
1681
+ return { success: false, error: "Unknown input" };
1682
+ }
1683
+ };
1684
+ function startREPL(options) {
1685
+ const repl = new ISLREPL(options);
1686
+ repl.start();
1687
+ return repl;
1688
+ }
1689
+
1690
+ // src/cli.ts
1691
+ function main() {
1692
+ const args = process.argv.slice(2);
1693
+ const options = {
1694
+ colors: !args.includes("--no-color"),
1695
+ verbose: args.includes("--verbose") || args.includes("-v")
1696
+ };
1697
+ if (args.includes("--help") || args.includes("-h")) {
1698
+ console.log(`
1699
+ ISL REPL - Intent Specification Language Interactive Shell
1700
+
1701
+ Usage: isl-repl [options]
1702
+
1703
+ Options:
1704
+ --no-color Disable colored output
1705
+ -v, --verbose Enable verbose output
1706
+ -h, --help Show this help message
1707
+
1708
+ Inside the REPL:
1709
+ .help Show all commands
1710
+ .exit Exit the REPL
1711
+ :check Type check intents
1712
+ :gen Generate code
1713
+ :load Load ISL file
1714
+ :list List intents
1715
+
1716
+ Example:
1717
+ $ isl-repl
1718
+ isl> intent Greeting {
1719
+ ...> pre: name.length > 0
1720
+ ...> post: result.startsWith("Hello")
1721
+ ...> }
1722
+ \u2713 Intent 'Greeting' defined (1 pre, 1 post)
1723
+ `);
1724
+ process.exit(0);
1725
+ }
1726
+ startREPL(options);
1727
+ }
1728
+ if (__require.main === module) {
1729
+ main();
1730
+ }
1731
+ export {
1732
+ main
1733
+ };
1734
+ //# sourceMappingURL=cli.js.map