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