@isl-lang/repl 0.1.0 → 0.1.1

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/index.js CHANGED
@@ -22,6 +22,12 @@ var Session = class {
22
22
  loadedFiles = /* @__PURE__ */ new Set();
23
23
  /** Session configuration */
24
24
  config;
25
+ /** Evaluation context (set by .context command) */
26
+ evalContext = {};
27
+ /** Pre-state context for old() expressions */
28
+ preContext = null;
29
+ /** Loaded domain AST (from real parser) */
30
+ domainAST = null;
25
31
  constructor(config = {}) {
26
32
  this.config = {
27
33
  colors: true,
@@ -299,6 +305,97 @@ var Session = class {
299
305
  }
300
306
  }
301
307
  // ─────────────────────────────────────────────────────────────────────────
308
+ // Evaluation Context Management
309
+ // ─────────────────────────────────────────────────────────────────────────
310
+ /**
311
+ * Set evaluation context from JSON string
312
+ */
313
+ setEvalContext(json) {
314
+ try {
315
+ const parsed = JSON.parse(json);
316
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
317
+ return { success: false, count: 0, error: "Context must be a JSON object" };
318
+ }
319
+ this.evalContext = parsed;
320
+ for (const [key, value] of Object.entries(this.evalContext)) {
321
+ this.variables.set(key, value);
322
+ }
323
+ return { success: true, count: Object.keys(parsed).length };
324
+ } catch (e) {
325
+ return { success: false, count: 0, error: e instanceof Error ? e.message : String(e) };
326
+ }
327
+ }
328
+ /**
329
+ * Set pre-state context for old() expressions
330
+ */
331
+ setPreContext(json) {
332
+ try {
333
+ const parsed = JSON.parse(json);
334
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
335
+ return { success: false, error: "Pre-context must be a JSON object" };
336
+ }
337
+ this.preContext = parsed;
338
+ return { success: true };
339
+ } catch (e) {
340
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
341
+ }
342
+ }
343
+ /**
344
+ * Get evaluation context
345
+ */
346
+ getEvalContext() {
347
+ return { ...this.evalContext };
348
+ }
349
+ /**
350
+ * Get pre-state context
351
+ */
352
+ getPreContext() {
353
+ return this.preContext ? { ...this.preContext } : null;
354
+ }
355
+ /**
356
+ * Resolve a dot-path against the evaluation context
357
+ */
358
+ resolveValue(dotPath) {
359
+ const parts = dotPath.split(".");
360
+ let current = this.evalContext;
361
+ for (const part of parts) {
362
+ if (current === null || current === void 0 || typeof current !== "object") {
363
+ return { found: false, value: void 0 };
364
+ }
365
+ current = current[part];
366
+ }
367
+ return { found: current !== void 0, value: current };
368
+ }
369
+ /**
370
+ * Resolve a dot-path against the pre-state context
371
+ */
372
+ resolvePreValue(dotPath) {
373
+ if (!this.preContext) {
374
+ return { found: false, value: void 0 };
375
+ }
376
+ const parts = dotPath.split(".");
377
+ let current = this.preContext;
378
+ for (const part of parts) {
379
+ if (current === null || current === void 0 || typeof current !== "object") {
380
+ return { found: false, value: void 0 };
381
+ }
382
+ current = current[part];
383
+ }
384
+ return { found: current !== void 0, value: current };
385
+ }
386
+ /**
387
+ * Set domain AST (from real parser)
388
+ */
389
+ setDomainAST(ast) {
390
+ this.domainAST = ast;
391
+ }
392
+ /**
393
+ * Get domain AST
394
+ */
395
+ getDomainAST() {
396
+ return this.domainAST;
397
+ }
398
+ // ─────────────────────────────────────────────────────────────────────────
302
399
  // State Management
303
400
  // ─────────────────────────────────────────────────────────────────────────
304
401
  /**
@@ -309,6 +406,9 @@ var Session = class {
309
406
  this.variables.clear();
310
407
  this.lastResult = void 0;
311
408
  this.loadedFiles.clear();
409
+ this.evalContext = {};
410
+ this.preContext = null;
411
+ this.domainAST = null;
312
412
  }
313
413
  /**
314
414
  * Get session summary
@@ -722,32 +822,158 @@ function drawBox(lines, title) {
722
822
  }
723
823
 
724
824
  // src/commands.ts
825
+ function evaluateExpression(expr, session) {
826
+ const trimmed = expr.trim();
827
+ const oldMatch = trimmed.match(/^old\((.+)\)$/);
828
+ if (oldMatch) {
829
+ const innerPath = oldMatch[1].trim();
830
+ if (!session.getPreContext()) {
831
+ return {
832
+ value: void 0,
833
+ error: "old() requires pre-state. Set with .context --pre <json>"
834
+ };
835
+ }
836
+ const { found, value } = session.resolvePreValue(innerPath);
837
+ if (!found) {
838
+ return { value: void 0, error: `Cannot resolve '${innerPath}' in pre-state context` };
839
+ }
840
+ return { value };
841
+ }
842
+ if (trimmed.startsWith("(") && trimmed.endsWith(")")) {
843
+ return evaluateExpression(trimmed.slice(1, -1), session);
844
+ }
845
+ if (trimmed.startsWith("!") || trimmed.startsWith("not ")) {
846
+ const inner = trimmed.startsWith("!") ? trimmed.slice(1) : trimmed.slice(4);
847
+ const result = evaluateExpression(inner.trim(), session);
848
+ if (result.error) return result;
849
+ return { value: !result.value };
850
+ }
851
+ for (const [opStr, opFn] of BINARY_OPS) {
852
+ const idx = findOperator(trimmed, opStr);
853
+ if (idx !== -1) {
854
+ const left = trimmed.slice(0, idx).trim();
855
+ const right = trimmed.slice(idx + opStr.length).trim();
856
+ const lResult = evaluateExpression(left, session);
857
+ if (lResult.error) return lResult;
858
+ const rResult = evaluateExpression(right, session);
859
+ if (rResult.error) return rResult;
860
+ return { value: opFn(lResult.value, rResult.value) };
861
+ }
862
+ }
863
+ if (trimmed === "true") return { value: true };
864
+ if (trimmed === "false") return { value: false };
865
+ if (trimmed === "null") return { value: null };
866
+ if (/^-?\d+$/.test(trimmed)) return { value: parseInt(trimmed, 10) };
867
+ if (/^-?\d+\.\d+$/.test(trimmed)) return { value: parseFloat(trimmed) };
868
+ if (/^"([^"]*)"$/.test(trimmed)) return { value: trimmed.slice(1, -1) };
869
+ if (/^'([^']*)'$/.test(trimmed)) return { value: trimmed.slice(1, -1) };
870
+ if (/^[\w.]+$/.test(trimmed)) {
871
+ const { found, value } = session.resolveValue(trimmed);
872
+ if (found) return { value };
873
+ if (session.hasVariable(trimmed)) {
874
+ return { value: session.getVariable(trimmed) };
875
+ }
876
+ }
877
+ return { value: void 0, error: `Cannot evaluate: ${trimmed}` };
878
+ }
879
+ var BINARY_OPS = [
880
+ // Logical (lowest precedence — scanned first so they split outermost)
881
+ [" || ", (a, b) => Boolean(a) || Boolean(b)],
882
+ [" or ", (a, b) => Boolean(a) || Boolean(b)],
883
+ [" && ", (a, b) => Boolean(a) && Boolean(b)],
884
+ [" and ", (a, b) => Boolean(a) && Boolean(b)],
885
+ // Equality
886
+ [" == ", (a, b) => a === b || String(a) === String(b)],
887
+ [" != ", (a, b) => a !== b && String(a) !== String(b)],
888
+ // Comparison
889
+ [" >= ", (a, b) => Number(a) >= Number(b)],
890
+ [" <= ", (a, b) => Number(a) <= Number(b)],
891
+ [" > ", (a, b) => Number(a) > Number(b)],
892
+ [" < ", (a, b) => Number(a) < Number(b)],
893
+ // Arithmetic
894
+ [" + ", (a, b) => {
895
+ if (typeof a === "string" || typeof b === "string") return String(a) + String(b);
896
+ return Number(a) + Number(b);
897
+ }],
898
+ [" - ", (a, b) => Number(a) - Number(b)],
899
+ [" * ", (a, b) => Number(a) * Number(b)],
900
+ [" / ", (a, b) => {
901
+ const d = Number(b);
902
+ if (d === 0) return Infinity;
903
+ return Number(a) / d;
904
+ }]
905
+ ];
906
+ function findOperator(expr, op) {
907
+ let depth = 0;
908
+ let inString = null;
909
+ for (let i = expr.length - 1; i >= 0; i--) {
910
+ const ch = expr[i];
911
+ if (inString) {
912
+ if (ch === inString && (i === 0 || expr[i - 1] !== "\\")) inString = null;
913
+ continue;
914
+ }
915
+ if (ch === '"' || ch === "'") {
916
+ inString = ch;
917
+ continue;
918
+ }
919
+ if (ch === "(") depth--;
920
+ if (ch === ")") depth++;
921
+ if (depth === 0 && i + op.length <= expr.length && expr.slice(i, i + op.length) === op) {
922
+ return i;
923
+ }
924
+ }
925
+ return -1;
926
+ }
927
+ function prettyPrintAST(node, indent = 0) {
928
+ const pad = " ".repeat(indent);
929
+ if (node === null || node === void 0) return `${pad}${colors.gray}null${colors.reset}`;
930
+ if (typeof node === "string") return `${pad}${colors.green}"${node}"${colors.reset}`;
931
+ if (typeof node === "number") return `${pad}${colors.cyan}${node}${colors.reset}`;
932
+ if (typeof node === "boolean") return `${pad}${colors.magenta}${node}${colors.reset}`;
933
+ if (Array.isArray(node)) {
934
+ if (node.length === 0) return `${pad}[]`;
935
+ const items = node.map((item) => prettyPrintAST(item, indent + 1));
936
+ return `${pad}[
937
+ ${items.join(",\n")}
938
+ ${pad}]`;
939
+ }
940
+ if (typeof node === "object") {
941
+ const obj = node;
942
+ const kind = obj["kind"];
943
+ const entries = Object.entries(obj).filter(
944
+ ([k, v]) => k !== "location" && v !== void 0 && !(Array.isArray(v) && v.length === 0)
945
+ );
946
+ if (entries.length === 0) return `${pad}{}`;
947
+ const header = kind ? `${pad}${colors.yellow}${kind}${colors.reset} {` : `${pad}{`;
948
+ const body = entries.filter(([k]) => k !== "kind").map(([k, v]) => {
949
+ const valStr = typeof v === "object" && v !== null ? "\n" + prettyPrintAST(v, indent + 2) : " " + prettyPrintAST(v, 0).trim();
950
+ return `${pad} ${colors.blue}${k}${colors.reset}:${valStr}`;
951
+ });
952
+ return `${header}
953
+ ${body.join("\n")}
954
+ ${pad}}`;
955
+ }
956
+ return `${pad}${String(node)}`;
957
+ }
725
958
  var metaCommands = [
959
+ // ─── .help ──────────────────────────────────────────────────────────────
726
960
  {
727
961
  name: "help",
728
962
  aliases: ["h", "?"],
729
- description: "Show help for commands",
963
+ description: "Show commands",
730
964
  usage: ".help [command]",
731
- handler: (args, session) => {
965
+ handler: (args) => {
732
966
  if (args.length > 0) {
733
- const cmdName = args[0].toLowerCase();
734
- const metaCmd = metaCommands.find((c) => c.name === cmdName || c.aliases.includes(cmdName));
735
- if (metaCmd) {
736
- return {
737
- output: [
738
- `${colors.cyan}.${metaCmd.name}${colors.reset} - ${metaCmd.description}`,
739
- `Usage: ${metaCmd.usage}`,
740
- metaCmd.aliases.length > 0 ? `Aliases: ${metaCmd.aliases.map((a) => "." + a).join(", ")}` : ""
741
- ].filter(Boolean).join("\n")
742
- };
743
- }
744
- const islCmd = islCommands.find((c) => c.name === cmdName || c.aliases.includes(cmdName));
745
- if (islCmd) {
967
+ const cmdName = args[0].toLowerCase().replace(/^\./, "");
968
+ const cmd = metaCommands.find(
969
+ (c) => c.name === cmdName || c.aliases.includes(cmdName)
970
+ );
971
+ if (cmd) {
746
972
  return {
747
973
  output: [
748
- `${colors.cyan}:${islCmd.name}${colors.reset} - ${islCmd.description}`,
749
- `Usage: ${islCmd.usage}`,
750
- islCmd.aliases.length > 0 ? `Aliases: ${islCmd.aliases.map((a) => ":" + a).join(", ")}` : ""
974
+ `${colors.cyan}.${cmd.name}${colors.reset} \u2014 ${cmd.description}`,
975
+ `Usage: ${cmd.usage}`,
976
+ cmd.aliases.length > 0 ? `Aliases: ${cmd.aliases.map((a) => "." + a).join(", ")}` : ""
751
977
  ].filter(Boolean).join("\n")
752
978
  };
753
979
  }
@@ -755,194 +981,232 @@ var metaCommands = [
755
981
  }
756
982
  const lines = [
757
983
  "",
758
- `${colors.bold}Meta Commands${colors.reset} ${colors.gray}(REPL control)${colors.reset}`,
984
+ `${colors.bold}REPL Commands${colors.reset}`,
759
985
  "",
760
- ...metaCommands.map((c) => ` ${colors.cyan}.${c.name.padEnd(10)}${colors.reset} ${c.description}`),
986
+ ...metaCommands.map(
987
+ (c) => ` ${colors.cyan}.${c.name.padEnd(12)}${colors.reset} ${c.description}`
988
+ ),
761
989
  "",
762
- `${colors.bold}ISL Commands${colors.reset} ${colors.gray}(specification operations)${colors.reset}`,
990
+ `${colors.bold}ISL Input${colors.reset}`,
763
991
  "",
764
- ...islCommands.map((c) => ` ${colors.cyan}:${c.name.padEnd(10)}${colors.reset} ${c.description}`),
992
+ ` Type ISL directly \u2014 multi-line supported (braces auto-detect):`,
765
993
  "",
766
- `${colors.bold}ISL Syntax${colors.reset}`,
767
- "",
768
- ` ${colors.yellow}intent${colors.reset} Name {`,
769
- ` ${colors.magenta}pre${colors.reset}: condition`,
770
- ` ${colors.magenta}post${colors.reset}: condition`,
994
+ ` ${colors.yellow}domain${colors.reset} Example {`,
995
+ ` ${colors.yellow}entity${colors.reset} User {`,
996
+ ` id: ${colors.green}UUID${colors.reset}`,
997
+ ` name: ${colors.green}String${colors.reset}`,
998
+ ` }`,
771
999
  ` }`,
772
1000
  "",
773
- `Type ${colors.cyan}.help <command>${colors.reset} for detailed help.`,
1001
+ `Type ${colors.cyan}.help <command>${colors.reset} for details.`,
774
1002
  ""
775
1003
  ];
776
1004
  return { output: lines.join("\n") };
777
1005
  }
778
1006
  },
1007
+ // ─── .parse ─────────────────────────────────────────────────────────────
779
1008
  {
780
- name: "exit",
781
- aliases: ["quit", "q"],
782
- description: "Exit the REPL",
783
- usage: ".exit",
784
- handler: () => {
785
- return { exit: true };
786
- }
787
- },
788
- {
789
- name: "clear",
790
- aliases: ["cls"],
791
- description: "Clear session state (intents, variables)",
792
- usage: ".clear",
1009
+ name: "parse",
1010
+ aliases: ["p", "ast"],
1011
+ description: "Parse ISL and show AST",
1012
+ usage: ".parse <isl>",
793
1013
  handler: (args, session) => {
794
- session.clear();
795
- return { output: formatSuccess("Session cleared") };
1014
+ const input = args.join(" ").trim();
1015
+ if (!input) {
1016
+ return { output: 'Usage: .parse <isl code>\nExample: .parse domain Foo { version: "1.0" }' };
1017
+ }
1018
+ try {
1019
+ const { parse } = __require("@isl-lang/parser");
1020
+ const result = parse(input, "<repl>");
1021
+ if (!result.success || result.errors.length > 0) {
1022
+ const errLines = result.errors.map((e) => {
1023
+ const loc = e.location;
1024
+ if (loc) {
1025
+ return formatParseError(input, e.message, loc.line, loc.column);
1026
+ }
1027
+ return formatError(e.message);
1028
+ });
1029
+ return { output: errLines.join("\n") };
1030
+ }
1031
+ if (result.domain) {
1032
+ session.setDomainAST(result.domain);
1033
+ return {
1034
+ output: formatSuccess("Parsed successfully") + "\n" + prettyPrintAST(result.domain)
1035
+ };
1036
+ }
1037
+ return { output: formatWarning("Parse returned no AST") };
1038
+ } catch {
1039
+ return {
1040
+ output: formatWarning(
1041
+ "Real parser not available \u2014 install @isl-lang/parser.\nFalling back to simple parse."
1042
+ )
1043
+ };
1044
+ }
796
1045
  }
797
1046
  },
1047
+ // ─── .eval ──────────────────────────────────────────────────────────────
798
1048
  {
799
- name: "history",
800
- aliases: ["hist"],
801
- description: "Show command history",
802
- usage: ".history [n]",
1049
+ name: "eval",
1050
+ aliases: ["e"],
1051
+ description: "Evaluate expression against context",
1052
+ usage: ".eval <expression>",
803
1053
  handler: (args, session) => {
804
- const count = args.length > 0 ? parseInt(args[0]) : 10;
805
- const history = session.getHistory(count);
806
- if (history.length === 0) {
807
- return { output: "No history." };
1054
+ const expr = args.join(" ").trim();
1055
+ if (!expr) {
1056
+ return {
1057
+ output: [
1058
+ "Usage: .eval <expression>",
1059
+ "",
1060
+ "Examples:",
1061
+ ' .eval user.email == "test@x.com"',
1062
+ " .eval user.age > 30",
1063
+ " .eval old(user.age)",
1064
+ "",
1065
+ 'Set context first: .context { "user": { "email": "test@x.com" } }'
1066
+ ].join("\n")
1067
+ };
808
1068
  }
809
- const lines = [
810
- `${colors.bold}History${colors.reset} (last ${history.length} entries)`,
811
- "",
812
- ...history.map((entry, i) => {
813
- const num = String(i + 1).padStart(3);
814
- const preview = entry.split("\n")[0];
815
- const more = entry.includes("\n") ? ` ${colors.gray}...${colors.reset}` : "";
816
- return ` ${colors.gray}${num}${colors.reset} ${preview}${more}`;
817
- })
818
- ];
819
- return { output: lines.join("\n") };
1069
+ const result = evaluateExpression(expr, session);
1070
+ if (result.error) {
1071
+ return { output: formatError(result.error) };
1072
+ }
1073
+ session.setLastResult(result.value);
1074
+ return {
1075
+ output: `${colors.cyan}\u2192${colors.reset} ${formatValue(result.value)}`
1076
+ };
820
1077
  }
821
- }
822
- ];
823
- var islCommands = [
1078
+ },
1079
+ // ─── .check ─────────────────────────────────────────────────────────────
824
1080
  {
825
1081
  name: "check",
826
1082
  aliases: ["c"],
827
- description: "Type check an intent",
828
- usage: ":check <intent>",
1083
+ description: "Type check the current session",
1084
+ usage: ".check [intent]",
829
1085
  handler: (args, session) => {
830
- if (args.length === 0) {
831
- const intents = session.getAllIntents();
832
- if (intents.length === 0) {
833
- return { output: formatWarning("No intents defined. Define one with: intent Name { ... }") };
1086
+ if (args.length > 0) {
1087
+ const intentName = args[0];
1088
+ const intent = session.getIntent(intentName);
1089
+ if (!intent) {
1090
+ const available = session.getIntentNames().join(", ") || "(none)";
1091
+ return {
1092
+ output: formatError(`Unknown intent: ${intentName}
1093
+ Available: ${available}`)
1094
+ };
834
1095
  }
835
- const lines2 = [
836
- formatSuccess("Type check passed"),
837
- ""
838
- ];
839
- for (const intent2 of intents) {
840
- lines2.push(`${colors.bold}${intent2.name}${colors.reset}`);
841
- for (const pre of intent2.preconditions) {
842
- lines2.push(` ${colors.green}\u2713${colors.reset} pre: ${highlightCondition(pre.expression)}`);
843
- }
844
- for (const post of intent2.postconditions) {
845
- lines2.push(` ${colors.green}\u2713${colors.reset} post: ${highlightCondition(post.expression)}`);
846
- }
847
- lines2.push("");
1096
+ const lines2 = [formatSuccess("Type check passed"), ""];
1097
+ for (const pre of intent.preconditions) {
1098
+ lines2.push(` ${colors.green}\u2713${colors.reset} pre: ${highlightExpression(pre.expression)}`);
1099
+ }
1100
+ for (const post of intent.postconditions) {
1101
+ lines2.push(` ${colors.green}\u2713${colors.reset} post: ${highlightExpression(post.expression)}`);
848
1102
  }
849
1103
  return { output: lines2.join("\n") };
850
1104
  }
851
- const intentName = args[0];
852
- const intent = session.getIntent(intentName);
853
- if (!intent) {
854
- const available = session.getIntentNames().join(", ") || "(none)";
1105
+ const intents = session.getAllIntents();
1106
+ if (intents.length === 0) {
855
1107
  return {
856
- output: formatError(`Unknown intent: ${intentName}
857
- Available: ${available}`)
1108
+ output: formatWarning("No intents defined. Write ISL or use .load <file>")
858
1109
  };
859
1110
  }
860
- const lines = [
861
- formatSuccess("Type check passed"),
862
- ""
863
- ];
864
- for (const pre of intent.preconditions) {
865
- lines.push(` pre: ${highlightCondition(pre.expression)} ${colors.green}\u2713${colors.reset}`);
866
- }
867
- for (const post of intent.postconditions) {
868
- lines.push(` post: ${highlightCondition(post.expression)} ${colors.green}\u2713${colors.reset}`);
1111
+ const lines = [formatSuccess(`Type check passed \u2014 ${intents.length} intent(s)`), ""];
1112
+ for (const intent of intents) {
1113
+ lines.push(`${colors.bold}${intent.name}${colors.reset}`);
1114
+ for (const pre of intent.preconditions) {
1115
+ lines.push(` ${colors.green}\u2713${colors.reset} pre: ${highlightExpression(pre.expression)}`);
1116
+ }
1117
+ for (const post of intent.postconditions) {
1118
+ lines.push(` ${colors.green}\u2713${colors.reset} post: ${highlightExpression(post.expression)}`);
1119
+ }
1120
+ lines.push("");
869
1121
  }
870
1122
  return { output: lines.join("\n") };
871
1123
  }
872
1124
  },
1125
+ // ─── .gen ───────────────────────────────────────────────────────────────
873
1126
  {
874
1127
  name: "gen",
875
1128
  aliases: ["generate", "g"],
876
- description: "Generate code from an intent",
877
- usage: ":gen <target> <intent>",
1129
+ description: "Generate TypeScript from intent",
1130
+ usage: ".gen [intent]",
878
1131
  handler: (args, session) => {
879
- if (args.length < 2) {
880
- return {
881
- output: [
882
- "Usage: :gen <target> <intent>",
883
- "",
884
- "Targets:",
885
- " typescript Generate TypeScript contract",
886
- " rust Generate Rust contract",
887
- " go Generate Go contract",
888
- " openapi Generate OpenAPI schema"
889
- ].join("\n")
890
- };
891
- }
892
- const target = args[0].toLowerCase();
893
- const intentName = args[1];
894
- const intent = session.getIntent(intentName);
895
- if (!intent) {
896
- const available = session.getIntentNames().join(", ") || "(none)";
1132
+ const intents = args.length > 0 ? [session.getIntent(args[0])].filter(Boolean) : session.getAllIntents();
1133
+ if (intents.length === 0) {
897
1134
  return {
898
- output: formatError(`Unknown intent: ${intentName}
899
- Available: ${available}`)
1135
+ output: args.length > 0 ? formatError(`Unknown intent: ${args[0]}
1136
+ Available: ${session.getIntentNames().join(", ") || "(none)"}`) : formatWarning("No intents defined. Write ISL or use .load <file>")
900
1137
  };
901
1138
  }
902
- switch (target) {
903
- case "typescript":
904
- case "ts":
905
- return { output: generateTypeScript(intent) };
906
- case "rust":
907
- case "rs":
908
- return { output: generateRust(intent) };
909
- case "go":
910
- return { output: generateGo(intent) };
911
- case "openapi":
912
- case "oas":
913
- return { output: generateOpenAPI(intent) };
914
- default:
915
- return {
916
- output: formatError(`Unknown target: ${target}
917
- Available: typescript, rust, go, openapi`)
918
- };
1139
+ const lines = [`${colors.gray}// Generated TypeScript${colors.reset}`, ""];
1140
+ for (const intent of intents) {
1141
+ lines.push(`interface ${intent.name}Contract {`);
1142
+ if (intent.preconditions.length > 0) {
1143
+ lines.push(" /** Preconditions */");
1144
+ for (const pre of intent.preconditions) {
1145
+ lines.push(` checkPre(): boolean; // ${pre.expression}`);
1146
+ }
1147
+ }
1148
+ if (intent.postconditions.length > 0) {
1149
+ lines.push(" /** Postconditions */");
1150
+ for (const post of intent.postconditions) {
1151
+ lines.push(` checkPost(): boolean; // ${post.expression}`);
1152
+ }
1153
+ }
1154
+ if (intent.invariants.length > 0) {
1155
+ lines.push(" /** Invariants */");
1156
+ for (const inv of intent.invariants) {
1157
+ lines.push(` checkInvariant(): boolean; // ${inv.expression}`);
1158
+ }
1159
+ }
1160
+ lines.push("}");
1161
+ lines.push("");
919
1162
  }
1163
+ return { output: lines.join("\n") };
920
1164
  }
921
1165
  },
1166
+ // ─── .load ──────────────────────────────────────────────────────────────
922
1167
  {
923
1168
  name: "load",
924
1169
  aliases: ["l"],
925
- description: "Load intents from a file",
926
- usage: ":load <file.isl>",
1170
+ description: "Load an .isl file",
1171
+ usage: ".load <file.isl>",
927
1172
  handler: (args, session) => {
928
1173
  if (args.length === 0) {
929
- return { output: "Usage: :load <file.isl>" };
1174
+ return { output: "Usage: .load <file.isl>" };
930
1175
  }
931
1176
  const filePath = args[0];
932
- let result = { errors: [] };
933
- session.loadFile(filePath).then((r) => {
934
- result = r;
935
- }).catch((e) => {
936
- result.errors.push(String(e));
937
- });
938
- const fs4 = __require("fs");
939
- const path4 = __require("path");
1177
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
1178
+ if (!fs2.existsSync(resolvedPath)) {
1179
+ return { output: formatError(`File not found: ${resolvedPath}`) };
1180
+ }
940
1181
  try {
941
- const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
942
- if (!fs4.existsSync(resolvedPath)) {
943
- return { output: formatError(`File not found: ${resolvedPath}`) };
1182
+ const content = fs2.readFileSync(resolvedPath, "utf-8");
1183
+ try {
1184
+ const { parse } = __require("@isl-lang/parser");
1185
+ const result = parse(content, resolvedPath);
1186
+ if (!result.success || result.errors.length > 0) {
1187
+ const errLines = result.errors.map((e) => {
1188
+ const loc = e.location;
1189
+ if (loc) {
1190
+ return formatParseError(content, e.message, loc.line, loc.column);
1191
+ }
1192
+ return formatError(e.message);
1193
+ });
1194
+ return { output: errLines.join("\n") };
1195
+ }
1196
+ if (result.domain) {
1197
+ session.setDomainAST(result.domain);
1198
+ const domain = result.domain;
1199
+ const name = domain.name?.name ?? "Unknown";
1200
+ const entityCount = domain.entities?.length ?? 0;
1201
+ const behaviorCount = domain.behaviors?.length ?? 0;
1202
+ return {
1203
+ output: formatSuccess(
1204
+ `Loaded: ${name} (${entityCount} entities, ${behaviorCount} behaviors) from ${path.basename(filePath)}`
1205
+ )
1206
+ };
1207
+ }
1208
+ } catch {
944
1209
  }
945
- const content = fs4.readFileSync(resolvedPath, "utf-8");
946
1210
  const intentRegex = /(?:intent|behavior)\s+(\w+)\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}/g;
947
1211
  let match;
948
1212
  let count = 0;
@@ -954,37 +1218,125 @@ Available: typescript, rust, go, openapi`)
954
1218
  }
955
1219
  }
956
1220
  if (count === 0) {
957
- return { output: formatWarning("No intents found in file") };
1221
+ return { output: formatWarning("No intents/behaviors found in file") };
958
1222
  }
959
1223
  return {
960
- output: formatSuccess(`Loaded ${count} intent(s) from ${filePath}`)
1224
+ output: formatSuccess(`Loaded ${count} intent(s) from ${path.basename(filePath)}`)
961
1225
  };
962
1226
  } catch (error) {
963
1227
  return {
964
- output: formatError(`Failed to load: ${error instanceof Error ? error.message : String(error)}`)
1228
+ output: formatError(
1229
+ `Failed to load: ${error instanceof Error ? error.message : String(error)}`
1230
+ )
965
1231
  };
966
1232
  }
967
1233
  }
968
1234
  },
1235
+ // ─── .context ───────────────────────────────────────────────────────────
1236
+ {
1237
+ name: "context",
1238
+ aliases: ["ctx"],
1239
+ description: "Set evaluation context (JSON)",
1240
+ usage: ".context <json> | .context --pre <json>",
1241
+ handler: (args, session) => {
1242
+ const input = args.join(" ").trim();
1243
+ if (!input) {
1244
+ const ctx = session.getEvalContext();
1245
+ const pre = session.getPreContext();
1246
+ if (Object.keys(ctx).length === 0 && !pre) {
1247
+ return {
1248
+ output: [
1249
+ "No context set.",
1250
+ "",
1251
+ "Usage:",
1252
+ ' .context { "user": { "email": "test@x.com", "age": 25 } }',
1253
+ ' .context --pre { "user": { "age": 20 } }'
1254
+ ].join("\n")
1255
+ };
1256
+ }
1257
+ const lines = [];
1258
+ if (Object.keys(ctx).length > 0) {
1259
+ lines.push(`${colors.bold}Context:${colors.reset}`);
1260
+ lines.push(formatValue(ctx));
1261
+ }
1262
+ if (pre) {
1263
+ lines.push(`${colors.bold}Pre-state:${colors.reset}`);
1264
+ lines.push(formatValue(pre));
1265
+ }
1266
+ return { output: lines.join("\n") };
1267
+ }
1268
+ if (input.startsWith("--pre ")) {
1269
+ const json = input.slice(6).trim();
1270
+ const result2 = session.setPreContext(json);
1271
+ if (!result2.success) {
1272
+ return { output: formatError(`Invalid JSON: ${result2.error}`) };
1273
+ }
1274
+ return { output: formatSuccess("Pre-state context set") };
1275
+ }
1276
+ const result = session.setEvalContext(input);
1277
+ if (!result.success) {
1278
+ return { output: formatError(`Invalid JSON: ${result.error}`) };
1279
+ }
1280
+ return {
1281
+ output: formatSuccess(
1282
+ `Context set (${result.count} variable${result.count !== 1 ? "s" : ""})`
1283
+ )
1284
+ };
1285
+ }
1286
+ },
1287
+ // ─── .clear ─────────────────────────────────────────────────────────────
1288
+ {
1289
+ name: "clear",
1290
+ aliases: ["cls", "reset"],
1291
+ description: "Reset session state",
1292
+ usage: ".clear",
1293
+ handler: (_args, session) => {
1294
+ session.clear();
1295
+ return { output: formatSuccess("Session cleared") };
1296
+ }
1297
+ },
1298
+ // ─── .history ───────────────────────────────────────────────────────────
1299
+ {
1300
+ name: "history",
1301
+ aliases: ["hist"],
1302
+ description: "Show command history",
1303
+ usage: ".history [n]",
1304
+ handler: (args, session) => {
1305
+ const count = args.length > 0 ? parseInt(args[0], 10) : 10;
1306
+ const history = session.getHistory(count);
1307
+ if (history.length === 0) {
1308
+ return { output: "No history." };
1309
+ }
1310
+ const lines = [
1311
+ `${colors.bold}History${colors.reset} (last ${history.length} entries)`,
1312
+ "",
1313
+ ...history.map((entry, i) => {
1314
+ const num = String(i + 1).padStart(3);
1315
+ const preview = entry.split("\n")[0];
1316
+ const more = entry.includes("\n") ? ` ${colors.gray}...${colors.reset}` : "";
1317
+ return ` ${colors.gray}${num}${colors.reset} ${preview}${more}`;
1318
+ })
1319
+ ];
1320
+ return { output: lines.join("\n") };
1321
+ }
1322
+ },
1323
+ // ─── .list ──────────────────────────────────────────────────────────────
969
1324
  {
970
1325
  name: "list",
971
1326
  aliases: ["ls"],
972
- description: "List all defined intents",
973
- usage: ":list",
974
- handler: (args, session) => {
1327
+ description: "List defined intents",
1328
+ usage: ".list",
1329
+ handler: (_args, session) => {
975
1330
  const intents = session.getAllIntents();
976
1331
  if (intents.length === 0) {
977
1332
  return { output: "No intents defined." };
978
1333
  }
979
1334
  const lines = [""];
980
1335
  for (const intent of intents) {
981
- const preCount = intent.preconditions.length;
982
- const postCount = intent.postconditions.length;
983
- const invCount = intent.invariants.length;
984
1336
  const parts = [];
985
- if (preCount > 0) parts.push(`${preCount} pre`);
986
- if (postCount > 0) parts.push(`${postCount} post`);
987
- if (invCount > 0) parts.push(`${invCount} invariant`);
1337
+ if (intent.preconditions.length > 0) parts.push(`${intent.preconditions.length} pre`);
1338
+ if (intent.postconditions.length > 0) parts.push(`${intent.postconditions.length} post`);
1339
+ if (intent.invariants.length > 0) parts.push(`${intent.invariants.length} invariant`);
988
1340
  const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
989
1341
  lines.push(` ${colors.cyan}${intent.name}${colors.reset}${summary}`);
990
1342
  }
@@ -992,31 +1344,26 @@ Available: typescript, rust, go, openapi`)
992
1344
  return { output: lines.join("\n") };
993
1345
  }
994
1346
  },
1347
+ // ─── .inspect ───────────────────────────────────────────────────────────
995
1348
  {
996
1349
  name: "inspect",
997
1350
  aliases: ["i", "show"],
998
1351
  description: "Show full details of an intent",
999
- usage: ":inspect <intent>",
1352
+ usage: ".inspect [intent]",
1000
1353
  handler: (args, session) => {
1001
1354
  if (args.length === 0) {
1002
1355
  const summary = session.getSummary();
1003
- const files = session.getLoadedFiles();
1356
+ const ctx = session.getEvalContext();
1004
1357
  const lines = [
1005
1358
  "",
1006
1359
  `${colors.bold}Session Summary${colors.reset}`,
1007
1360
  "",
1008
1361
  ` Intents: ${summary.intentCount}`,
1009
1362
  ` Variables: ${summary.variableCount}`,
1010
- ` History: ${summary.historyCount} entries`
1363
+ ` Context: ${Object.keys(ctx).length} keys`,
1364
+ ` History: ${summary.historyCount} entries`,
1365
+ ""
1011
1366
  ];
1012
- if (files.length > 0) {
1013
- lines.push("");
1014
- lines.push(`${colors.bold}Loaded Files${colors.reset}`);
1015
- for (const file of files) {
1016
- lines.push(` ${file}`);
1017
- }
1018
- }
1019
- lines.push("");
1020
1367
  return { output: lines.join("\n") };
1021
1368
  }
1022
1369
  const intentName = args[0];
@@ -1031,178 +1378,18 @@ Available: ${available}`)
1031
1378
  return { output: formatIntent(intent) };
1032
1379
  }
1033
1380
  },
1381
+ // ─── .exit ──────────────────────────────────────────────────────────────
1034
1382
  {
1035
- name: "export",
1036
- aliases: ["save"],
1037
- description: "Export intents to a file",
1038
- usage: ":export <file.isl>",
1039
- handler: (args, session) => {
1040
- if (args.length === 0) {
1041
- return { output: "Usage: :export <file.isl>" };
1042
- }
1043
- const filePath = args[0];
1044
- const fs4 = __require("fs");
1045
- const path4 = __require("path");
1046
- try {
1047
- const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
1048
- const intents = session.getAllIntents();
1049
- if (intents.length === 0) {
1050
- return { output: formatWarning("No intents to export") };
1051
- }
1052
- const lines = [];
1053
- lines.push("// Exported ISL intents");
1054
- lines.push(`// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
1055
- lines.push("");
1056
- for (const intent of intents) {
1057
- lines.push(`intent ${intent.name} {`);
1058
- for (const pre of intent.preconditions) {
1059
- lines.push(` pre: ${pre.expression}`);
1060
- }
1061
- for (const post of intent.postconditions) {
1062
- lines.push(` post: ${post.expression}`);
1063
- }
1064
- for (const inv of intent.invariants) {
1065
- lines.push(` invariant: ${inv.expression}`);
1066
- }
1067
- lines.push("}");
1068
- lines.push("");
1069
- }
1070
- fs4.writeFileSync(resolvedPath, lines.join("\n"));
1071
- return { output: formatSuccess(`Exported ${intents.length} intent(s) to ${filePath}`) };
1072
- } catch (error) {
1073
- return {
1074
- output: formatError(`Failed to export: ${error instanceof Error ? error.message : String(error)}`)
1075
- };
1076
- }
1383
+ name: "exit",
1384
+ aliases: ["quit", "q"],
1385
+ description: "Exit the REPL",
1386
+ usage: ".exit",
1387
+ handler: () => {
1388
+ return { exit: true };
1077
1389
  }
1078
1390
  }
1079
1391
  ];
1080
- function generateTypeScript(intent) {
1081
- const lines = [
1082
- `${colors.gray}// Generated TypeScript${colors.reset}`,
1083
- `interface ${intent.name}Contract {`
1084
- ];
1085
- if (intent.preconditions.length > 0) {
1086
- for (const pre of intent.preconditions) {
1087
- const varName = extractVariableName(pre.expression);
1088
- const type = inferType(pre.expression);
1089
- lines.push(` pre: (${varName}: ${type}) => boolean;`);
1090
- }
1091
- }
1092
- if (intent.postconditions.length > 0) {
1093
- for (const post of intent.postconditions) {
1094
- const varName = extractVariableName(post.expression);
1095
- const type = inferType(post.expression);
1096
- lines.push(` post: (${varName}: ${type}) => boolean;`);
1097
- }
1098
- }
1099
- lines.push("}");
1100
- return lines.join("\n");
1101
- }
1102
- function generateRust(intent) {
1103
- const lines = [
1104
- `${colors.gray}// Generated Rust${colors.reset}`,
1105
- `pub trait ${intent.name}Contract {`
1106
- ];
1107
- if (intent.preconditions.length > 0) {
1108
- for (const pre of intent.preconditions) {
1109
- const varName = extractVariableName(pre.expression);
1110
- const type = inferRustType(pre.expression);
1111
- lines.push(` fn check_pre(&self, ${varName}: ${type}) -> bool;`);
1112
- }
1113
- }
1114
- if (intent.postconditions.length > 0) {
1115
- for (const post of intent.postconditions) {
1116
- const varName = extractVariableName(post.expression);
1117
- const type = inferRustType(post.expression);
1118
- lines.push(` fn check_post(&self, ${varName}: ${type}) -> bool;`);
1119
- }
1120
- }
1121
- lines.push("}");
1122
- return lines.join("\n");
1123
- }
1124
- function generateGo(intent) {
1125
- const lines = [
1126
- `${colors.gray}// Generated Go${colors.reset}`,
1127
- `type ${intent.name}Contract interface {`
1128
- ];
1129
- if (intent.preconditions.length > 0) {
1130
- for (const pre of intent.preconditions) {
1131
- const varName = extractVariableName(pre.expression);
1132
- const type = inferGoType(pre.expression);
1133
- lines.push(` CheckPre(${varName} ${type}) bool`);
1134
- }
1135
- }
1136
- if (intent.postconditions.length > 0) {
1137
- for (const post of intent.postconditions) {
1138
- const varName = extractVariableName(post.expression);
1139
- const type = inferGoType(post.expression);
1140
- lines.push(` CheckPost(${varName} ${type}) bool`);
1141
- }
1142
- }
1143
- lines.push("}");
1144
- return lines.join("\n");
1145
- }
1146
- function generateOpenAPI(intent) {
1147
- const lines = [
1148
- `${colors.gray}# Generated OpenAPI${colors.reset}`,
1149
- `openapi: 3.0.0`,
1150
- `paths:`,
1151
- ` /${intent.name.toLowerCase()}:`,
1152
- ` post:`,
1153
- ` summary: ${intent.name}`,
1154
- ` requestBody:`,
1155
- ` content:`,
1156
- ` application/json:`,
1157
- ` schema:`,
1158
- ` type: object`
1159
- ];
1160
- if (intent.preconditions.length > 0) {
1161
- lines.push(` # Preconditions:`);
1162
- for (const pre of intent.preconditions) {
1163
- lines.push(` # - ${pre.expression}`);
1164
- }
1165
- }
1166
- lines.push(` responses:`);
1167
- lines.push(` '200':`);
1168
- lines.push(` description: Success`);
1169
- if (intent.postconditions.length > 0) {
1170
- lines.push(` # Postconditions:`);
1171
- for (const post of intent.postconditions) {
1172
- lines.push(` # - ${post.expression}`);
1173
- }
1174
- }
1175
- return lines.join("\n");
1176
- }
1177
- function extractVariableName(expression) {
1178
- const match = expression.match(/^(\w+)/);
1179
- return match ? match[1] : "input";
1180
- }
1181
- function inferType(expression) {
1182
- if (expression.includes(".length")) return "string";
1183
- if (expression.includes(".startsWith")) return "string";
1184
- if (expression.includes(".endsWith")) return "string";
1185
- if (expression.includes(".includes")) return "string";
1186
- if (expression.includes(" > ") || expression.includes(" < ")) return "number";
1187
- return "unknown";
1188
- }
1189
- function inferRustType(expression) {
1190
- if (expression.includes(".length") || expression.includes(".len()")) return "&str";
1191
- if (expression.includes(".starts_with")) return "&str";
1192
- if (expression.includes(" > ") || expression.includes(" < ")) return "i32";
1193
- return "&str";
1194
- }
1195
- function inferGoType(expression) {
1196
- if (expression.includes(".length") || expression.includes("len(")) return "string";
1197
- if (expression.includes(" > ") || expression.includes(" < ")) return "int";
1198
- return "string";
1199
- }
1200
- function highlightCondition(expression) {
1201
- return expression.replace(
1202
- /(\w+)\s*(>|<|>=|<=|==|!=)\s*(\d+|"[^"]*")/g,
1203
- `${colors.blue}$1${colors.reset} ${colors.yellow}$2${colors.reset} ${colors.green}$3${colors.reset}`
1204
- ).replace(/\b(true|false)\b/g, `${colors.magenta}$1${colors.reset}`).replace(/\.(length|startsWith|endsWith|includes)/g, `.${colors.cyan}$1${colors.reset}`);
1205
- }
1392
+ var islCommands = [];
1206
1393
  function levenshteinDistance(a, b) {
1207
1394
  const matrix = [];
1208
1395
  for (let i = 0; i <= b.length; i++) {
@@ -1223,9 +1410,8 @@ function levenshteinDistance(a, b) {
1223
1410
  }
1224
1411
  return matrix[b.length][a.length];
1225
1412
  }
1226
- function findSimilarCommand(input, type) {
1227
- const commands = type === "meta" ? metaCommands : islCommands;
1228
- const names = commands.flatMap((c) => [c.name, ...c.aliases]);
1413
+ function findSimilarCommand(input, _type) {
1414
+ const names = metaCommands.flatMap((c) => [c.name, ...c.aliases]);
1229
1415
  let bestMatch = null;
1230
1416
  let bestDistance = Infinity;
1231
1417
  for (const name of names) {
@@ -1240,11 +1426,31 @@ function findSimilarCommand(input, type) {
1240
1426
 
1241
1427
  // src/completions.ts
1242
1428
  var KEYWORDS = [
1243
- { text: "intent", type: "keyword", description: "Define an intent" },
1429
+ // Structure keywords
1430
+ { text: "domain", type: "keyword", description: "Define a domain" },
1431
+ { text: "entity", type: "keyword", description: "Define an entity" },
1244
1432
  { text: "behavior", type: "keyword", description: "Define a behavior" },
1433
+ { text: "intent", type: "keyword", description: "Define an intent" },
1434
+ { text: "input", type: "keyword", description: "Input block" },
1435
+ { text: "output", type: "keyword", description: "Output block" },
1245
1436
  { text: "pre", type: "keyword", description: "Precondition" },
1246
1437
  { text: "post", type: "keyword", description: "Postcondition" },
1247
1438
  { text: "invariant", type: "keyword", description: "Invariant" },
1439
+ { text: "scenario", type: "keyword", description: "Scenario block" },
1440
+ { text: "version", type: "keyword", description: "Version declaration" },
1441
+ // Types
1442
+ { text: "String", type: "keyword", description: "String type" },
1443
+ { text: "Number", type: "keyword", description: "Number type" },
1444
+ { text: "Int", type: "keyword", description: "Integer type" },
1445
+ { text: "Decimal", type: "keyword", description: "Decimal type" },
1446
+ { text: "Boolean", type: "keyword", description: "Boolean type" },
1447
+ { text: "UUID", type: "keyword", description: "UUID type" },
1448
+ { text: "Timestamp", type: "keyword", description: "Timestamp type" },
1449
+ { text: "Duration", type: "keyword", description: "Duration type" },
1450
+ { text: "List", type: "keyword", description: "List<T> type" },
1451
+ { text: "Map", type: "keyword", description: "Map<K,V> type" },
1452
+ { text: "Optional", type: "keyword", description: "Optional<T> type" },
1453
+ // Literals and operators
1248
1454
  { text: "true", type: "keyword", description: "Boolean true" },
1249
1455
  { text: "false", type: "keyword", description: "Boolean false" },
1250
1456
  { text: "null", type: "keyword", description: "Null value" },
@@ -1254,19 +1460,16 @@ var KEYWORDS = [
1254
1460
  { text: "implies", type: "keyword", description: "Logical implication" },
1255
1461
  { text: "forall", type: "keyword", description: "Universal quantifier" },
1256
1462
  { text: "exists", type: "keyword", description: "Existential quantifier" },
1257
- { text: "in", type: "keyword", description: "Membership test" }
1463
+ { text: "in", type: "keyword", description: "Membership test" },
1464
+ { text: "old", type: "keyword", description: "Pre-state value (old(x))" }
1258
1465
  ];
1259
1466
  var META_COMMANDS = metaCommands.map((cmd) => ({
1260
1467
  text: `.${cmd.name}`,
1261
1468
  type: "command",
1262
1469
  description: cmd.description
1263
1470
  }));
1264
- var ISL_COMMANDS = islCommands.map((cmd) => ({
1265
- text: `:${cmd.name}`,
1266
- type: "command",
1267
- description: cmd.description
1268
- }));
1269
- var COMMANDS = [...META_COMMANDS, ...ISL_COMMANDS];
1471
+ var ISL_COMMANDS = [];
1472
+ var COMMANDS = [...META_COMMANDS];
1270
1473
  var GEN_TARGETS = [
1271
1474
  { text: "typescript", type: "keyword", description: "Generate TypeScript contract" },
1272
1475
  { text: "rust", type: "keyword", description: "Generate Rust contract" },
@@ -1292,7 +1495,7 @@ var CompletionProvider = class {
1292
1495
  return this.completeMetaCommand(trimmed);
1293
1496
  }
1294
1497
  if (trimmed.startsWith(":")) {
1295
- return this.completeISLCommand(trimmed);
1498
+ return this.completeMetaCommand("." + trimmed.slice(1));
1296
1499
  }
1297
1500
  return this.completeExpression(trimmed);
1298
1501
  }
@@ -1469,7 +1672,10 @@ var ISLREPL = class {
1469
1672
  this.options = {
1470
1673
  colors: options.colors !== false,
1471
1674
  verbose: options.verbose ?? false,
1472
- historyFile: options.historyFile
1675
+ historyFile: options.historyFile,
1676
+ load: options.load,
1677
+ context: options.context,
1678
+ parseOnly: options.parseOnly ?? false
1473
1679
  };
1474
1680
  this.session = new Session({ colors: this.options.colors });
1475
1681
  this.history = new History({
@@ -1483,6 +1689,11 @@ var ISLREPL = class {
1483
1689
  start() {
1484
1690
  if (this.running) return;
1485
1691
  this.running = true;
1692
+ this.applyStartupOptions();
1693
+ if (this.options.parseOnly || !process.stdin.isTTY) {
1694
+ this.runPipeMode();
1695
+ return;
1696
+ }
1486
1697
  this.history.load();
1487
1698
  this.rl = readline.createInterface({
1488
1699
  input: process.stdin,
@@ -1507,15 +1718,71 @@ var ISLREPL = class {
1507
1718
  if (this.buffer.length > 0) {
1508
1719
  this.buffer = [];
1509
1720
  this.braceCount = 0;
1510
- console.log("\n" + formatWarning("Input cancelled"));
1721
+ process.stdout.write("\n" + formatWarning("Input cancelled") + "\n");
1511
1722
  this.rl.setPrompt(PROMPT);
1512
1723
  this.rl.prompt();
1513
1724
  } else {
1514
- console.log("\n" + formatWarning("Use .exit or :quit to exit"));
1725
+ process.stdout.write("\n" + formatWarning("Use .exit to quit") + "\n");
1515
1726
  this.rl.prompt();
1516
1727
  }
1517
1728
  });
1518
1729
  }
1730
+ /**
1731
+ * Apply startup options (--load, --context)
1732
+ */
1733
+ applyStartupOptions() {
1734
+ if (this.options.context) {
1735
+ const result = this.session.setEvalContext(this.options.context);
1736
+ if (result.success) {
1737
+ process.stdout.write(
1738
+ formatSuccess(`Context set (${result.count} variable${result.count !== 1 ? "s" : ""})`) + "\n"
1739
+ );
1740
+ } else {
1741
+ process.stdout.write(formatError(`Invalid context JSON: ${result.error}`) + "\n");
1742
+ }
1743
+ }
1744
+ if (this.options.load) {
1745
+ const loadCmd = metaCommands.find((c) => c.name === "load");
1746
+ if (loadCmd) {
1747
+ const result = loadCmd.handler([this.options.load], this.session, this);
1748
+ if (result.output) {
1749
+ process.stdout.write(result.output + "\n");
1750
+ }
1751
+ }
1752
+ }
1753
+ }
1754
+ /**
1755
+ * Run in pipe mode (read all stdin, parse, and output)
1756
+ */
1757
+ runPipeMode() {
1758
+ let input = "";
1759
+ process.stdin.setEncoding("utf-8");
1760
+ process.stdin.on("data", (chunk) => {
1761
+ input += chunk;
1762
+ });
1763
+ process.stdin.on("end", () => {
1764
+ const trimmed = input.trim();
1765
+ if (!trimmed) {
1766
+ process.exit(0);
1767
+ return;
1768
+ }
1769
+ if (this.options.parseOnly) {
1770
+ const parseCmd = metaCommands.find((c) => c.name === "parse");
1771
+ if (parseCmd) {
1772
+ const result = parseCmd.handler(trimmed.split(" "), this.session, this);
1773
+ if (result.output) {
1774
+ process.stdout.write(result.output + "\n");
1775
+ }
1776
+ }
1777
+ } else {
1778
+ const lines = trimmed.split("\n");
1779
+ for (const line of lines) {
1780
+ this.handleLine(line);
1781
+ }
1782
+ }
1783
+ process.exit(0);
1784
+ });
1785
+ }
1519
1786
  /**
1520
1787
  * Print the welcome banner
1521
1788
  */
@@ -1535,7 +1802,7 @@ ${colors.cyan}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1535
1802
  ${colors.bold}ISL v${VERSION}${colors.reset} \u2014 Intent Specification Language
1536
1803
  Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${colors.reset} to quit
1537
1804
  `;
1538
- console.log(banner);
1805
+ process.stdout.write(banner);
1539
1806
  }
1540
1807
  /**
1541
1808
  * Handle a line of input
@@ -1546,11 +1813,11 @@ Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${color
1546
1813
  return;
1547
1814
  }
1548
1815
  if (trimmed.startsWith(".") && this.buffer.length === 0) {
1549
- this.handleMetaCommand(trimmed);
1816
+ this.handleDotCommand(trimmed);
1550
1817
  return;
1551
1818
  }
1552
1819
  if (trimmed.startsWith(":") && this.buffer.length === 0) {
1553
- this.handleISLCommand(trimmed);
1820
+ this.handleDotCommand("." + trimmed.slice(1));
1554
1821
  return;
1555
1822
  }
1556
1823
  this.braceCount += (line.match(/\{/g) || []).length;
@@ -1566,78 +1833,134 @@ Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${color
1566
1833
  }
1567
1834
  }
1568
1835
  /**
1569
- * Handle a meta command (. prefix)
1836
+ * Handle a dot command (. prefix)
1570
1837
  */
1571
- handleMetaCommand(input) {
1838
+ handleDotCommand(input) {
1572
1839
  const parts = input.slice(1).split(/\s+/);
1573
1840
  const cmdName = parts[0]?.toLowerCase() || "";
1574
1841
  const args = parts.slice(1);
1842
+ const rawArgs = input.slice(1 + (cmdName.length || 0)).trim();
1575
1843
  const command = metaCommands.find(
1576
1844
  (c) => c.name === cmdName || c.aliases.includes(cmdName)
1577
1845
  );
1578
1846
  if (command) {
1579
1847
  this.history.add(input);
1580
- const result = command.handler(args, this.session, this);
1848
+ const needsRawArgs = ["context", "ctx", "eval", "e", "parse", "p", "ast", "load", "l"];
1849
+ const effectiveArgs = needsRawArgs.includes(cmdName) && rawArgs ? [rawArgs] : args;
1850
+ const result = command.handler(effectiveArgs, this.session, this);
1581
1851
  if (result.output) {
1582
- console.log(result.output);
1852
+ process.stdout.write(result.output + "\n");
1583
1853
  }
1584
1854
  if (result.exit) {
1585
1855
  this.exit();
1586
1856
  }
1587
1857
  } else {
1588
- const suggestion = findSimilarCommand(cmdName, "meta");
1858
+ const suggestion = findSimilarCommand(cmdName);
1589
1859
  if (suggestion) {
1590
- console.log(formatError(`Unknown command: .${cmdName}`));
1591
- console.log(formatWarning(`Did you mean: .${suggestion}?`));
1860
+ process.stdout.write(formatError(`Unknown command: .${cmdName}`) + "\n");
1861
+ process.stdout.write(formatWarning(`Did you mean: .${suggestion}?`) + "\n");
1592
1862
  } else {
1593
- console.log(formatError(`Unknown command: .${cmdName}`));
1594
- console.log(`Type ${colors.cyan}.help${colors.reset} for available commands`);
1863
+ process.stdout.write(formatError(`Unknown command: .${cmdName}`) + "\n");
1864
+ process.stdout.write(`Type ${colors.cyan}.help${colors.reset} for available commands
1865
+ `);
1595
1866
  }
1596
1867
  }
1597
1868
  }
1598
1869
  /**
1599
- * Handle an ISL command (: prefix)
1600
- */
1601
- handleISLCommand(input) {
1602
- const parts = input.slice(1).split(/\s+/);
1603
- const cmdName = parts[0]?.toLowerCase() || "";
1604
- const args = parts.slice(1);
1605
- const command = islCommands.find(
1606
- (c) => c.name === cmdName || c.aliases.includes(cmdName)
1607
- );
1608
- if (command) {
1609
- this.history.add(input);
1610
- const result = command.handler(args, this.session, this);
1611
- if (result.output) {
1612
- console.log(result.output);
1613
- }
1614
- } else {
1615
- const suggestion = findSimilarCommand(cmdName, "isl");
1616
- if (suggestion) {
1617
- console.log(formatError(`Unknown command: :${cmdName}`));
1618
- console.log(formatWarning(`Did you mean: :${suggestion}?`));
1619
- } else {
1620
- console.log(formatError(`Unknown command: :${cmdName}`));
1621
- console.log(`Type ${colors.cyan}.help${colors.reset} for available commands`);
1622
- }
1623
- }
1624
- }
1625
- /**
1626
- * Evaluate ISL code
1870
+ * Evaluate ISL code (multi-line input or bare expressions)
1627
1871
  */
1628
1872
  evaluate(code) {
1629
1873
  try {
1630
1874
  const trimmed = code.trim();
1631
- if (trimmed.startsWith("intent ")) {
1875
+ try {
1876
+ const { parse } = __require("@isl-lang/parser");
1877
+ let parseInput = trimmed;
1878
+ const needsWrapper = !trimmed.startsWith("domain ");
1879
+ if (needsWrapper) {
1880
+ parseInput = `domain _REPL { version: "0.0.1"
1881
+ ${trimmed}
1882
+ }`;
1883
+ }
1884
+ const result = parse(parseInput, "<repl>");
1885
+ if (result.errors.length > 0) {
1886
+ for (const err of result.errors) {
1887
+ const loc = err.location;
1888
+ if (loc) {
1889
+ const adjustedLine = needsWrapper ? Math.max(1, loc.line - 1) : loc.line;
1890
+ const lines = trimmed.split("\n");
1891
+ const errorLine = lines[adjustedLine - 1] || "";
1892
+ process.stdout.write(
1893
+ `${colors.red}\u2717 Error at line ${adjustedLine}, col ${loc.column}:${colors.reset}
1894
+ `
1895
+ );
1896
+ process.stdout.write(` ${errorLine}
1897
+ `);
1898
+ process.stdout.write(` ${" ".repeat(Math.max(0, loc.column - 1))}${colors.red}^^^^^${colors.reset}
1899
+ `);
1900
+ const typeMatch = err.message.match(/Unknown type '(\w+)'/i) || err.message.match(/unexpected.*'(\w+)'/i);
1901
+ if (typeMatch) {
1902
+ const suggestion = suggestCorrection(typeMatch[1]);
1903
+ if (suggestion) {
1904
+ process.stdout.write(
1905
+ ` ${colors.yellow}Did you mean '${suggestion}'?${colors.reset}
1906
+ `
1907
+ );
1908
+ }
1909
+ } else {
1910
+ process.stdout.write(` ${err.message}
1911
+ `);
1912
+ }
1913
+ } else {
1914
+ process.stdout.write(formatError(err.message) + "\n");
1915
+ }
1916
+ }
1917
+ return;
1918
+ }
1919
+ if (result.domain) {
1920
+ this.session.setDomainAST(result.domain);
1921
+ const domain = result.domain;
1922
+ if (needsWrapper) {
1923
+ const entityCount = domain.entities?.length ?? 0;
1924
+ const behaviorCount = domain.behaviors?.length ?? 0;
1925
+ const parts = [];
1926
+ if (entityCount > 0) parts.push(`${entityCount} entit${entityCount === 1 ? "y" : "ies"}`);
1927
+ if (behaviorCount > 0) parts.push(`${behaviorCount} behavior${behaviorCount === 1 ? "" : "s"}`);
1928
+ if (parts.length > 0) {
1929
+ process.stdout.write(
1930
+ formatSuccess(`Parsed: ${parts.join(", ")}`) + "\n"
1931
+ );
1932
+ } else {
1933
+ process.stdout.write(formatSuccess("Parsed successfully") + "\n");
1934
+ }
1935
+ } else {
1936
+ const name = domain.name?.name ?? "Unknown";
1937
+ const entityCount = domain.entities?.length ?? 0;
1938
+ const behaviorCount = domain.behaviors?.length ?? 0;
1939
+ process.stdout.write(
1940
+ formatSuccess(
1941
+ `Parsed: domain ${name} (${entityCount} entit${entityCount === 1 ? "y" : "ies"}, ${behaviorCount} behavior${behaviorCount === 1 ? "" : "s"})`
1942
+ ) + "\n"
1943
+ );
1944
+ }
1945
+ return;
1946
+ }
1947
+ } catch {
1948
+ }
1949
+ if (trimmed.startsWith("intent ") || trimmed.startsWith("behavior ")) {
1632
1950
  this.evaluateIntent(trimmed);
1633
1951
  return;
1634
1952
  }
1635
- if (trimmed.startsWith("behavior ")) {
1636
- this.evaluateIntent(trimmed);
1953
+ if (trimmed.startsWith("domain ")) {
1954
+ process.stdout.write(formatSuccess("Parsed domain block") + "\n");
1637
1955
  return;
1638
1956
  }
1639
- console.log(formatWarning(`Cannot evaluate: ${trimmed.split("\n")[0]}...`));
1640
- console.log(`Use ${colors.cyan}intent Name { ... }${colors.reset} to define an intent`);
1957
+ process.stdout.write(
1958
+ formatWarning(`Cannot evaluate: ${trimmed.split("\n")[0]}...`) + "\n"
1959
+ );
1960
+ process.stdout.write(
1961
+ `Use ${colors.cyan}.help${colors.reset} for available commands
1962
+ `
1963
+ );
1641
1964
  } catch (error) {
1642
1965
  this.printError(error);
1643
1966
  }
@@ -1657,13 +1980,15 @@ Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${color
1657
1980
  if (postCount > 0) parts.push(`${postCount} post`);
1658
1981
  if (invCount > 0) parts.push(`${invCount} invariant`);
1659
1982
  const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
1660
- console.log(formatSuccess(`Intent '${intent.name}' defined${summary}`));
1983
+ process.stdout.write(
1984
+ formatSuccess(`Intent '${intent.name}' defined${summary}`) + "\n"
1985
+ );
1661
1986
  } else {
1662
1987
  const behaviorMatch = code.match(/^behavior\s+(\w+)\s*\{([\s\S]*)\}$/);
1663
1988
  if (behaviorMatch) {
1664
1989
  const name = behaviorMatch[1];
1665
1990
  const body = behaviorMatch[2];
1666
- const intent2 = {
1991
+ const newIntent = {
1667
1992
  name,
1668
1993
  preconditions: [],
1669
1994
  postconditions: [],
@@ -1673,51 +1998,28 @@ Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${color
1673
1998
  };
1674
1999
  const preSection = body.match(/pre(?:conditions)?\s*\{([^}]*)\}/s);
1675
2000
  if (preSection) {
1676
- const conditions = preSection[1].trim().split("\n").map((l) => l.trim()).filter(Boolean);
1677
- for (const cond of conditions) {
1678
- const expr = cond.replace(/^-\s*/, "").trim();
1679
- if (expr) {
1680
- intent2.preconditions.push({ expression: expr });
1681
- }
2001
+ for (const line of preSection[1].trim().split("\n")) {
2002
+ const expr = line.trim().replace(/^-\s*/, "").trim();
2003
+ if (expr) newIntent.preconditions.push({ expression: expr });
1682
2004
  }
1683
2005
  }
1684
2006
  const postSection = body.match(/post(?:conditions)?\s*\{([^}]*)\}/s);
1685
2007
  if (postSection) {
1686
- const conditions = postSection[1].trim().split("\n").map((l) => l.trim()).filter(Boolean);
1687
- for (const cond of conditions) {
1688
- const expr = cond.replace(/^-\s*/, "").trim();
1689
- if (expr) {
1690
- intent2.postconditions.push({ expression: expr });
1691
- }
2008
+ for (const line of postSection[1].trim().split("\n")) {
2009
+ const expr = line.trim().replace(/^-\s*/, "").trim();
2010
+ if (expr) newIntent.postconditions.push({ expression: expr });
1692
2011
  }
1693
2012
  }
1694
- this.session.defineIntent(intent2);
1695
- const preCount = intent2.preconditions.length;
1696
- const postCount = intent2.postconditions.length;
2013
+ this.session.defineIntent(newIntent);
1697
2014
  const parts = [];
1698
- if (preCount > 0) parts.push(`${preCount} pre`);
1699
- if (postCount > 0) parts.push(`${postCount} post`);
2015
+ if (newIntent.preconditions.length > 0) parts.push(`${newIntent.preconditions.length} pre`);
2016
+ if (newIntent.postconditions.length > 0) parts.push(`${newIntent.postconditions.length} post`);
1700
2017
  const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
1701
- console.log(formatSuccess(`Intent '${intent2.name}' defined${summary}`));
2018
+ process.stdout.write(
2019
+ formatSuccess(`Intent '${name}' defined${summary}`) + "\n"
2020
+ );
1702
2021
  } else {
1703
- this.printParseError(code, "Failed to parse intent definition");
1704
- }
1705
- }
1706
- }
1707
- /**
1708
- * Print a parse error with location info
1709
- */
1710
- printParseError(code, message, line, column) {
1711
- console.log(formatError(message));
1712
- if (line !== void 0 && column !== void 0) {
1713
- const lines = code.split("\n");
1714
- const errorLine = lines[line - 1] || "";
1715
- console.log(` ${colors.gray}${line} |${colors.reset} ${errorLine}`);
1716
- console.log(` ${colors.gray}${" ".repeat(String(line).length)} |${colors.reset} ${" ".repeat(column - 1)}${colors.red}^${colors.reset}`);
1717
- } else {
1718
- const firstLine = code.split("\n")[0];
1719
- if (firstLine) {
1720
- console.log(` ${colors.gray}>${colors.reset} ${firstLine}`);
2022
+ process.stdout.write(formatError("Failed to parse intent definition") + "\n");
1721
2023
  }
1722
2024
  }
1723
2025
  }
@@ -1726,12 +2028,12 @@ Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${color
1726
2028
  */
1727
2029
  printError(error) {
1728
2030
  if (error instanceof Error) {
1729
- console.log(formatError(error.message));
2031
+ process.stdout.write(formatError(error.message) + "\n");
1730
2032
  if (this.options.verbose && error.stack) {
1731
- console.log(colors.gray + error.stack + colors.reset);
2033
+ process.stdout.write(colors.gray + error.stack + colors.reset + "\n");
1732
2034
  }
1733
2035
  } else {
1734
- console.log(formatError(String(error)));
2036
+ process.stdout.write(formatError(String(error)) + "\n");
1735
2037
  }
1736
2038
  }
1737
2039
  /**
@@ -1740,8 +2042,9 @@ Type ${colors.cyan}.help${colors.reset} for commands, ${colors.cyan}.exit${color
1740
2042
  exit() {
1741
2043
  this.running = false;
1742
2044
  this.history.save();
1743
- console.log(`
1744
- ${colors.yellow}Goodbye!${colors.reset}`);
2045
+ process.stdout.write(`
2046
+ ${colors.yellow}Goodbye!${colors.reset}
2047
+ `);
1745
2048
  if (this.rl) {
1746
2049
  this.rl.close();
1747
2050
  }
@@ -1764,31 +2067,21 @@ ${colors.yellow}Goodbye!${colors.reset}`);
1764
2067
  */
1765
2068
  async executeOnce(input) {
1766
2069
  const trimmed = input.trim();
1767
- if (trimmed.startsWith(".")) {
1768
- const parts = trimmed.slice(1).split(/\s+/);
2070
+ if (trimmed.startsWith(".") || trimmed.startsWith(":")) {
2071
+ const normalized = trimmed.startsWith(":") ? "." + trimmed.slice(1) : trimmed;
2072
+ const parts = normalized.slice(1).split(/\s+/);
1769
2073
  const cmdName = parts[0]?.toLowerCase() || "";
1770
- const args = parts.slice(1);
2074
+ const rawArgs = normalized.slice(1 + (cmdName.length || 0)).trim();
1771
2075
  const command = metaCommands.find(
1772
2076
  (c) => c.name === cmdName || c.aliases.includes(cmdName)
1773
2077
  );
1774
2078
  if (command) {
1775
- const result = command.handler(args, this.session, this);
2079
+ const needsRawArgs = ["context", "ctx", "eval", "e", "parse", "p", "ast", "load", "l"];
2080
+ const effectiveArgs = needsRawArgs.includes(cmdName) && rawArgs ? [rawArgs] : parts.slice(1);
2081
+ const result = command.handler(effectiveArgs, this.session, this);
1776
2082
  return { success: true, output: result.output };
1777
2083
  }
1778
- return { success: false, error: `Unknown command: .${cmdName}` };
1779
- }
1780
- if (trimmed.startsWith(":")) {
1781
- const parts = trimmed.slice(1).split(/\s+/);
1782
- const cmdName = parts[0]?.toLowerCase() || "";
1783
- const args = parts.slice(1);
1784
- const command = islCommands.find(
1785
- (c) => c.name === cmdName || c.aliases.includes(cmdName)
1786
- );
1787
- if (command) {
1788
- const result = command.handler(args, this.session, this);
1789
- return { success: true, output: result.output };
1790
- }
1791
- return { success: false, error: `Unknown command: :${cmdName}` };
2084
+ return { success: false, error: `Unknown command: ${cmdName}` };
1792
2085
  }
1793
2086
  if (trimmed.startsWith("intent ") || trimmed.startsWith("behavior ")) {
1794
2087
  const intent = this.session.parseIntent(trimmed);
@@ -1801,12 +2094,135 @@ ${colors.yellow}Goodbye!${colors.reset}`);
1801
2094
  return { success: false, error: "Unknown input" };
1802
2095
  }
1803
2096
  };
2097
+ var KNOWN_TYPES = [
2098
+ "String",
2099
+ "Int",
2100
+ "Decimal",
2101
+ "Boolean",
2102
+ "UUID",
2103
+ "Timestamp",
2104
+ "Duration",
2105
+ "List",
2106
+ "Map",
2107
+ "Optional",
2108
+ "Number"
2109
+ ];
2110
+ function suggestCorrection(typo) {
2111
+ const lower = typo.toLowerCase();
2112
+ for (const t of KNOWN_TYPES) {
2113
+ if (t.toLowerCase() === lower) return t;
2114
+ if (t.toLowerCase().startsWith(lower.slice(0, 3)) && Math.abs(t.length - typo.length) <= 2) {
2115
+ return t;
2116
+ }
2117
+ }
2118
+ return null;
2119
+ }
1804
2120
  function startREPL(options) {
1805
2121
  const repl = new ISLREPL(options);
1806
2122
  repl.start();
1807
2123
  return repl;
1808
2124
  }
1809
2125
 
1810
- export { COMMANDS, CompletionProvider, History, ISLREPL, ISL_COMMANDS, KEYWORDS, META_COMMANDS, MemoryHistory, Session, colors, createCompleter, createSession, drawBox, findSimilarCommand, formatCondition, formatError, formatInfo, formatIntent, formatParseError, formatSuccess, formatTable, formatTypeError, formatValue, formatWarning, highlightExpression, highlightISL, islCommands, metaCommands, startREPL, stripColors, wrapText };
2126
+ // src/cli.ts
2127
+ function parseArgs(argv) {
2128
+ const options = {
2129
+ help: false,
2130
+ colors: true,
2131
+ verbose: false,
2132
+ parseOnly: false
2133
+ };
2134
+ for (let i = 0; i < argv.length; i++) {
2135
+ const arg = argv[i];
2136
+ switch (arg) {
2137
+ case "--help":
2138
+ case "-h":
2139
+ options.help = true;
2140
+ break;
2141
+ case "--no-color":
2142
+ options.colors = false;
2143
+ break;
2144
+ case "--verbose":
2145
+ case "-v":
2146
+ options.verbose = true;
2147
+ break;
2148
+ case "--load":
2149
+ options.load = argv[++i];
2150
+ break;
2151
+ case "--context":
2152
+ options.context = argv[++i];
2153
+ break;
2154
+ case "--parse":
2155
+ options.parseOnly = true;
2156
+ break;
2157
+ default:
2158
+ if (arg.startsWith("--load=")) {
2159
+ options.load = arg.slice(7);
2160
+ } else if (arg.startsWith("--context=")) {
2161
+ options.context = arg.slice(10);
2162
+ }
2163
+ break;
2164
+ }
2165
+ }
2166
+ return options;
2167
+ }
2168
+ function printHelp() {
2169
+ process.stdout.write(`
2170
+ ISL REPL - Intent Specification Language Interactive Shell
2171
+
2172
+ Usage: isl-repl [options]
2173
+
2174
+ Options:
2175
+ --load <file> Load an ISL file on start
2176
+ --context <json> Set initial evaluation context
2177
+ --parse Parse mode (non-interactive, for piped input)
2178
+ --no-color Disable colored output
2179
+ -v, --verbose Enable verbose output
2180
+ -h, --help Show this help message
2181
+
2182
+ Inside the REPL:
2183
+ .help Show all commands
2184
+ .parse <isl> Parse ISL and show AST
2185
+ .eval <expr> Evaluate expression against context
2186
+ .check [intent] Type check intents
2187
+ .gen [intent] Generate TypeScript from intent
2188
+ .load <file> Load an .isl file
2189
+ .context <json> Set evaluation context (mock data)
2190
+ .clear Reset session state
2191
+ .list List defined intents
2192
+ .inspect [intent] Show full details of an intent
2193
+ .history Show command history
2194
+ .exit Exit the REPL
2195
+
2196
+ Multi-line Input:
2197
+ Type ISL with braces \u2014 the REPL auto-detects multi-line:
2198
+ isl> domain Example {
2199
+ ...> entity User {
2200
+ ...> id: UUID
2201
+ ...> name: String
2202
+ ...> }
2203
+ ...> }
2204
+
2205
+ Examples:
2206
+ $ isl-repl
2207
+ $ isl-repl --load auth.isl
2208
+ $ isl-repl --context '{"user": {"id": 1}}'
2209
+ $ echo 'domain X { version: "1.0" }' | isl-repl --parse
2210
+ `);
2211
+ }
2212
+ function main() {
2213
+ const args = process.argv.slice(2);
2214
+ const options = parseArgs(args);
2215
+ if (options.help) {
2216
+ printHelp();
2217
+ process.exit(0);
2218
+ }
2219
+ startREPL(options);
2220
+ }
2221
+ var isMainModule = typeof __require !== "undefined" ? __require.main === module : process.argv[1]?.includes("cli");
2222
+ if (isMainModule) {
2223
+ main();
2224
+ }
2225
+
2226
+ export { COMMANDS, CompletionProvider, History, ISLREPL, ISL_COMMANDS, KEYWORDS, META_COMMANDS, MemoryHistory, Session, colors, createCompleter, createSession, drawBox, findSimilarCommand, formatCondition, formatError, formatInfo, formatIntent, formatParseError, formatSuccess, formatTable, formatTypeError, formatValue, formatWarning, highlightExpression, highlightISL, islCommands, main, metaCommands, startREPL, stripColors, wrapText };
1811
2227
  //# sourceMappingURL=index.js.map
1812
2228
  //# sourceMappingURL=index.js.map