@oprt/cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -65,7 +65,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
65
65
 
66
66
  // src/index.ts
67
67
  var import_node_fs6 = require("fs");
68
- var import_node_path7 = require("path");
68
+ var import_node_path6 = require("path");
69
69
  var import_node_url3 = require("url");
70
70
 
71
71
  // src/contract.ts
@@ -560,7 +560,7 @@ var import_node_crypto = require("crypto");
560
560
  // package.json
561
561
  var package_default = {
562
562
  name: "@oprt/cli",
563
- version: "0.3.0",
563
+ version: "0.4.0",
564
564
  description: "The OPERATOR command line: copy components and recipes into your app as source, give coding agents the contract, and check their code against it. Includes an MCP server.",
565
565
  keywords: [
566
566
  "design-system",
@@ -616,6 +616,8 @@ var package_default = {
616
616
  lint: "eslint src scripts"
617
617
  },
618
618
  devDependencies: {
619
+ "@babel/parser": "8.0.8",
620
+ "@babel/types": "8.0.8",
619
621
  "@modelcontextprotocol/sdk": "1.32.1",
620
622
  tsup: "^8.3.0",
621
623
  typescript: "^5.6.3",
@@ -629,7 +631,7 @@ var package_default = {
629
631
 
630
632
  // src/identity.ts
631
633
  var VERSION = package_default.version;
632
- var RESULT_SCHEMA_VERSION = 1;
634
+ var RESULT_SCHEMA_VERSION = 2;
633
635
  var digests = /* @__PURE__ */ new WeakMap();
634
636
  function contractDigest(contract2) {
635
637
  let digest = digests.get(contract2);
@@ -650,77 +652,241 @@ function identityOf(contract2) {
650
652
 
651
653
  // src/check.ts
652
654
  var import_node_fs3 = require("fs");
653
- var import_node_path4 = require("path");
655
+ var import_node_path3 = require("path");
654
656
 
655
657
  // src/parser.ts
656
658
  var import_node_module = require("module");
657
- var import_node_path3 = require("path");
658
659
  var import_node_url2 = require("url");
659
- var SUPPORTED_TYPESCRIPT = "TypeScript 5 or 6";
660
- var INSTALL_TYPESCRIPT = "npm install -D typescript@6";
661
- function usable(typescript) {
662
- const ts = typescript;
663
- if (typeof ts.createSourceFile !== "function" || ts.ScriptKind === void 0)
664
- return `TypeScript ${ts.version ?? "(unknown version)"} has no JavaScript syntax API (createSourceFile), which oprt reads code with; ${SUPPORTED_TYPESCRIPT} has it`;
660
+
661
+ // ../../node_modules/.pnpm/@babel+parser@8.0.8/node_modules/@babel/parser/package.json
662
+ var version = "8.0.8";
663
+
664
+ // src/parser.ts
665
+ var PARSER = {
666
+ name: "@babel/parser",
667
+ version,
668
+ source: "bundled"
669
+ };
670
+ function optionsFor(file) {
671
+ const typed = /\.tsx$/.test(file);
672
+ const plugins = [
673
+ "jsx",
674
+ ...typed ? ["typescript"] : [],
675
+ "decorators",
676
+ "decoratorAutoAccessors",
677
+ "deferredImportEvaluation",
678
+ "sourcePhaseImports"
679
+ ];
680
+ return {
681
+ sourceType: "module",
682
+ plugins,
683
+ // Recoverable errors are returned beside the tree, instead of the first one being thrown.
684
+ errorRecovery: true,
685
+ attachComment: false,
686
+ allowReturnOutsideFunction: true,
687
+ allowUndeclaredExports: true,
688
+ allowSuperOutsideMethod: true,
689
+ allowNewTargetOutsideFunction: true
690
+ };
691
+ }
692
+ var FILE = "babel-parser.cjs";
693
+ var REINSTALL = "Install @oprt/cli again: the parser ships beside the executable in dist.";
694
+ var loaded;
695
+ function loadBabel() {
696
+ if (loaded !== void 0) return loaded;
697
+ const fromSource = /\.[cm]?tsx?$/.test((0, import_node_url2.fileURLToPath)(importMetaUrl));
665
698
  try {
666
- const probe = ts.createSourceFile(
667
- "probe.tsx",
668
- "<a>",
669
- ts.ScriptTarget?.Latest ?? 99,
670
- true,
671
- ts.ScriptKind.TSX
672
- );
673
- const diagnostics = probe.parseDiagnostics;
674
- if (!Array.isArray(diagnostics)) return "it does not report parse errors on a source file";
675
- if (diagnostics.length === 0) return "it did not report a syntax error in code that has one";
699
+ const require2 = (0, import_node_module.createRequire)(importMetaUrl);
700
+ loaded = fromSource ? require2("@babel/parser") : require2("./babel-parser.cjs");
676
701
  } catch (error) {
677
- return `it failed on a probe parse: ${error instanceof Error ? error.message : String(error)}`;
702
+ throw new CliError(
703
+ `Code checking cannot load its parser (${FILE}): ${error instanceof Error ? error.message : String(error)} ${REINSTALL}`,
704
+ 1,
705
+ { code: "ERR_PARSER_MISSING" }
706
+ );
678
707
  }
679
- return null;
708
+ return loaded;
680
709
  }
681
- var packageFolder = (file) => /^(.*[\\/]node_modules[\\/]typescript)[\\/]/.exec(file)?.[1] ?? file;
682
- function probeTypeScript(cwd, options = {}) {
683
- const places = [
684
- { from: (0, import_node_path3.join)(cwd, "package.json"), source: "project" },
685
- ...options.installer === false ? [] : [{ from: (0, import_node_url2.fileURLToPath)(importMetaUrl), source: "installer" }]
686
- ];
687
- let unusable;
688
- for (const { from, source } of places) {
689
- let file;
690
- let loaded2;
691
- try {
692
- const require2 = (0, import_node_module.createRequire)(from);
693
- file = require2.resolve("typescript");
694
- loaded2 = require2("typescript");
695
- } catch (error) {
696
- if (error.code !== "MODULE_NOT_FOUND")
697
- unusable ??= `typescript was found but failed to load: ${error instanceof Error ? error.message : String(error)}`;
698
- continue;
699
- }
700
- const typescript = loaded2.default ?? loaded2;
701
- const problem = usable(typescript);
702
- if (problem !== null) {
703
- unusable ??= `${problem} (at ${packageFolder(file)}).`;
704
- continue;
705
- }
710
+ var rejects = (babel, code) => {
711
+ try {
712
+ return babel.parse(code, optionsFor("probe.tsx")).errors.length > 0;
713
+ } catch {
714
+ return true;
715
+ }
716
+ };
717
+ function probeParser(load = loadBabel) {
718
+ let babel;
719
+ try {
720
+ babel = load();
721
+ } catch (error) {
706
722
  return {
707
- ready: true,
708
- typescript,
709
- resolution: {
710
- name: "typescript",
711
- version: typescript.version,
712
- source,
713
- resolved_from: packageFolder(file)
714
- }
723
+ ready: false,
724
+ reason: "parser_missing",
725
+ detail: error instanceof Error ? error.message : String(error)
726
+ };
727
+ }
728
+ if (rejects(babel, "export const a = <a b={1 as number} />;"))
729
+ return {
730
+ ready: false,
731
+ reason: "parser_unusable",
732
+ detail: "it rejected valid code in a probe parse"
715
733
  };
734
+ if (!rejects(babel, "export const a = <a>;"))
735
+ return {
736
+ ready: false,
737
+ reason: "parser_unusable",
738
+ detail: "it did not report a syntax error in code that has one"
739
+ };
740
+ return { ready: true, parser: PARSER };
741
+ }
742
+
743
+ // src/syntax.ts
744
+ var attempt = (babel, text2, file) => {
745
+ try {
746
+ const tree = babel.parse(text2, optionsFor(file));
747
+ return { tree, errors: tree.errors };
748
+ } catch (error) {
749
+ return { tree: null, fatal: error };
716
750
  }
717
- if (unusable !== void 0)
718
- return { ready: false, reason: "typescript_unusable", detail: unusable };
751
+ };
752
+ var sentence = (message) => message.replace(/\s*\(\d+:\d+\)$/, "");
753
+ var problemOf = (failure2) => ({
754
+ line: failure2.loc.line,
755
+ column: failure2.loc.column + 1,
756
+ message: sentence(failure2.message),
757
+ code: failure2.reasonCode ?? "SyntaxError",
758
+ position: failure2.pos
759
+ });
760
+ var tagName = (name) => {
761
+ if (name === void 0) return "";
762
+ if (name.type === "JSXIdentifier") return name.name;
763
+ if (name.type === "JSXMemberExpression")
764
+ return `${tagName(name.object)}.${name.property.name}`;
765
+ return `${name.namespace.name}:${name.name.name}`;
766
+ };
767
+ var NOT_NODES = /* @__PURE__ */ new Set(["loc", "extra", "errors", "tokens", "comments"]);
768
+ function* childNodes(node) {
769
+ for (const [key, value] of Object.entries(node)) {
770
+ if (NOT_NODES.has(key) || typeof value !== "object" || value === null) continue;
771
+ if (Array.isArray(value)) {
772
+ for (const item of value)
773
+ if (typeof item === "object" && item !== null && typeof item.type === "string")
774
+ yield item;
775
+ } else if (typeof value.type === "string") yield value;
776
+ }
777
+ }
778
+ var walk = (node, ancestors, visit) => {
779
+ visit(node, ancestors);
780
+ ancestors.push(node);
781
+ for (const child of childNodes(node)) walk(child, ancestors, visit);
782
+ ancestors.pop();
783
+ };
784
+ var unclosed = (element) => {
785
+ const name = element.openingElement.name;
719
786
  return {
720
- ready: false,
721
- reason: "typescript_missing",
722
- detail: `typescript could not be resolved from ${cwd}${options.installer === false ? "" : " or from beside the installer"}.`
787
+ line: name.loc?.start.line ?? 1,
788
+ column: (name.loc?.start.column ?? 0) + 1,
789
+ message: `JSX element '${tagName(name)}' has no corresponding closing tag.`,
790
+ code: "UnclosedJsxElement",
791
+ position: name.start ?? 0
723
792
  };
793
+ };
794
+ function problemsOf(tree, errors, limit, consumed) {
795
+ const mismatched = /* @__PURE__ */ new Map();
796
+ walk(tree.program, [], (node, ancestors) => {
797
+ if (node.type === "JSXElement" && node.closingElement && (node.closingElement.start ?? 0) < limit && tagName(node.openingElement.name) !== tagName(node.closingElement.name))
798
+ mismatched.set(node.closingElement.start ?? 0, { element: node, ancestors: [...ancestors] });
799
+ });
800
+ const problems = [];
801
+ for (const error of errors) {
802
+ if (error.pos >= limit) continue;
803
+ const entry = error.reasonCode === "MissingClosingTagElement" && mismatched.get(error.pos);
804
+ if (entry) {
805
+ const closing = tagName(entry.element.closingElement?.name);
806
+ const outer = [...entry.ancestors].reverse().find(
807
+ (ancestor) => ancestor.type === "JSXElement" && tagName(ancestor.openingElement.name) === closing && !consumed.has(ancestor)
808
+ );
809
+ if (outer !== void 0) {
810
+ consumed.add(outer);
811
+ problems.push(unclosed(entry.element));
812
+ continue;
813
+ }
814
+ }
815
+ problems.push(problemOf(error));
816
+ }
817
+ return problems;
818
+ }
819
+ var TYPE_ARGUMENTS = /(?<=[\w$\])])<(?:[^<>()]|<(?:[^<>()]|<[^<>()]*>)*>)*>(?=\()/g;
820
+ var LINE_BREAKS = new RegExp(`[^\\r\\n${String.fromCharCode(8232, 8233)}]`, "g");
821
+ var blankedOut = (match) => match.replace(LINE_BREAKS, " ");
822
+ var CLOSING_TAG = "</_>";
823
+ var COMPLETIONS = [CLOSING_TAG, ")", "}", "]"];
824
+ var MAX_COMPLETIONS = 24;
825
+ function complete(babel, prefix, file) {
826
+ let text2 = prefix;
827
+ let first = null;
828
+ for (let step = 0; step <= MAX_COMPLETIONS; step++) {
829
+ const tried = attempt(babel, text2, file);
830
+ if (tried.tree !== null) return { tree: tried.tree, errors: tried.errors, first };
831
+ const wantsMore = tried.fatal.reasonCode === "UnterminatedJsxContent" || tried.fatal.pos >= text2.length;
832
+ if (!wantsMore) return null;
833
+ const next = COMPLETIONS.find((piece) => {
834
+ const extended = attempt(babel, text2 + piece, file);
835
+ return extended.tree !== null || extended.fatal.reasonCode === "UnterminatedJsxContent" || extended.fatal.pos >= text2.length + piece.length;
836
+ });
837
+ if (next === void 0) return null;
838
+ first ??= next;
839
+ text2 += next;
840
+ }
841
+ return null;
842
+ }
843
+ function shutByCompletion(tree, limit) {
844
+ const shut = [];
845
+ walk(tree.program, [], (node) => {
846
+ if (node.type === "JSXElement" && node.closingElement && (node.closingElement.start ?? 0) >= limit)
847
+ shut.push(node);
848
+ });
849
+ return shut.sort((a, b) => (a.closingElement?.start ?? 0) - (b.closingElement?.start ?? 0));
850
+ }
851
+ var byPlace = (a, b) => a.position - b.position;
852
+ var MAX_EXPLAINED = 2e5;
853
+ function readSyntax(file, text2, babel = loadBabel()) {
854
+ const first = attempt(babel, text2, file);
855
+ if (first.tree !== null) {
856
+ const problems = problemsOf(first.tree, first.errors, Infinity, /* @__PURE__ */ new Set());
857
+ return problems.length === 0 ? { tree: first.tree, problems: [] } : { tree: null, problems: problems.sort(byPlace) };
858
+ }
859
+ let fatal = first.fatal;
860
+ if (text2.length > MAX_EXPLAINED) return { tree: null, problems: [problemOf(fatal)] };
861
+ if (/\.tsx$/.test(file) && fatal.reasonCode !== "UnterminatedJsxContent") {
862
+ const plain = text2.replace(TYPE_ARGUMENTS, blankedOut);
863
+ if (plain !== text2) {
864
+ const again = attempt(babel, plain, file);
865
+ if (again.tree === null && again.fatal.pos > fatal.pos) fatal = again.fatal;
866
+ }
867
+ }
868
+ try {
869
+ const finished = complete(babel, text2.slice(0, fatal.pos), file);
870
+ if (finished !== null) {
871
+ const limit = fatal.pos;
872
+ const consumed = /* @__PURE__ */ new Set();
873
+ const problems = problemsOf(finished.tree, finished.errors, limit, consumed);
874
+ const open = shutByCompletion(finished.tree, limit).filter(
875
+ (element) => !consumed.has(element)
876
+ );
877
+ const atEnd = fatal.reasonCode === "UnterminatedJsxContent" || fatal.pos >= text2.length;
878
+ const hints = (atEnd ? open : finished.first === CLOSING_TAG ? open.slice(0, 1) : []).map(
879
+ unclosed
880
+ );
881
+ const explained = [...problems, ...hints].some(({ code }) => code === "UnclosedJsxElement");
882
+ problems.push(...hints);
883
+ if (!(explained && fatal.reasonCode === "UnterminatedJsxContent"))
884
+ problems.push(problemOf(fatal));
885
+ return { tree: null, problems: problems.sort(byPlace) };
886
+ }
887
+ } catch {
888
+ }
889
+ return { tree: null, problems: [problemOf(fatal)] };
724
890
  }
725
891
 
726
892
  // src/check.ts
@@ -784,52 +950,43 @@ var quote = (text2) => {
784
950
  return flat.length > 48 ? `${flat.slice(0, 45)}...` : flat;
785
951
  };
786
952
  var MAX_SYNTAX_ERRORS = 10;
787
- function checkSource(typescript, contract2, file, text2) {
788
- const ts = typescript;
789
- const kind = /\.tsx$/.test(file) ? ts.ScriptKind.TSX : ts.ScriptKind.JSX;
790
- const source = ts.createSourceFile(file, text2, ts.ScriptTarget.Latest, true, kind);
791
- const parseDiagnostics = source.parseDiagnostics;
792
- if (!Array.isArray(parseDiagnostics))
793
- throw new CliError(
794
- `This TypeScript does not report syntax errors on a parsed file, so a file that does not parse could be called clean. Install TypeScript 5 or 6 in this project (${INSTALL_TYPESCRIPT}).`,
795
- 1,
796
- { code: "ERR_TYPESCRIPT_UNUSABLE" }
797
- );
798
- if (parseDiagnostics.length > 0) {
799
- const ordered = [...parseDiagnostics].sort((a, b) => (a.start ?? 0) - (b.start ?? 0));
800
- const shown = ordered.slice(0, MAX_SYNTAX_ERRORS);
953
+ function checkSource(contract2, file, text2) {
954
+ const reading = readSyntax(file, text2);
955
+ if (reading.tree === null) {
956
+ const shown = reading.problems.slice(0, MAX_SYNTAX_ERRORS);
957
+ const more = reading.problems.length - shown.length;
801
958
  return {
802
959
  file,
803
960
  status: "not_evaluated",
804
961
  limitations: [],
805
- findings: shown.map((diagnostic, index) => {
806
- const { line, character } = source.getLineAndCharacterOfPosition(diagnostic.start ?? 0);
807
- const more = ordered.length - shown.length;
808
- return {
962
+ findings: shown.map(
963
+ ({ line, column, message, code }, index) => ({
809
964
  code: "SYNTAX_ERROR",
810
965
  severity: "error",
811
966
  file,
812
- line: line + 1,
813
- column: character + 1,
814
- message: `${ts.flattenDiagnosticMessageText(diagnostic.messageText, " ")} (TS${diagnostic.code})` + (index === shown.length - 1 && more > 0 ? ` ${more} more syntax error${more === 1 ? "" : "s"} in this file not shown.` : "")
815
- };
816
- })
967
+ line,
968
+ column,
969
+ message: `${message} (${code})` + (index === shown.length - 1 && more > 0 ? ` ${more} more syntax error${more === 1 ? "" : "s"} in this file not shown.` : "")
970
+ })
971
+ )
817
972
  };
818
973
  }
819
974
  const findings = [];
820
975
  const limitations = [];
976
+ const place = (node) => ({
977
+ line: node.loc?.start.line ?? 1,
978
+ column: (node.loc?.start.column ?? 0) + 1
979
+ });
980
+ const textOf = (node) => text2.slice(node.start ?? 0, node.end ?? 0);
821
981
  const limit = (node, limitation) => {
822
- const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
823
- limitations.push({ ...limitation, file, line: line + 1, column: character + 1 });
982
+ limitations.push({ ...limitation, file, ...place(node) });
824
983
  };
825
984
  const report = (node, finding) => {
826
- const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
827
985
  findings.push({
828
986
  code: finding.code,
829
987
  severity: finding.severity,
830
988
  file,
831
- line: line + 1,
832
- column: character + 1,
989
+ ...place(node),
833
990
  ...finding.component === void 0 ? {} : { component: finding.component },
834
991
  ...finding.prop === void 0 ? {} : { prop: finding.prop },
835
992
  message: finding.message,
@@ -840,21 +997,18 @@ function checkSource(typescript, contract2, file, text2) {
840
997
  const isComponent = (name) => Object.hasOwn(contract2.components, name);
841
998
  const named = /* @__PURE__ */ new Map();
842
999
  const namespaces = /* @__PURE__ */ new Set();
843
- for (const statement of source.statements) {
844
- if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
845
- continue;
846
- const from = statement.moduleSpecifier.text;
847
- const clause = statement.importClause;
848
- if (!clause || clause.isTypeOnly || !clause.namedBindings) continue;
849
- const copied = from !== PACKAGE && /[./@]/.test(from[0] ?? "") && isComponent((0, import_node_path4.basename)(from));
1000
+ for (const statement of reading.tree.program.body) {
1001
+ if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
1002
+ const from = statement.source.value;
1003
+ const copied = from !== PACKAGE && /[./@]/.test(from[0] ?? "") && isComponent((0, import_node_path3.basename)(from));
850
1004
  if (from !== PACKAGE && !copied) continue;
851
- if (ts.isNamespaceImport(clause.namedBindings)) {
852
- if (from === PACKAGE) namespaces.add(clause.namedBindings.name.text);
853
- continue;
854
- }
855
- for (const element2 of clause.namedBindings.elements) {
856
- if (element2.isTypeOnly) continue;
857
- const exported = (element2.propertyName ?? element2.name).text;
1005
+ for (const element2 of statement.specifiers) {
1006
+ if (element2.type === "ImportNamespaceSpecifier") {
1007
+ if (from === PACKAGE) namespaces.add(element2.local.name);
1008
+ continue;
1009
+ }
1010
+ if (element2.type !== "ImportSpecifier" || element2.importKind === "type") continue;
1011
+ const exported = element2.imported.type === "Identifier" ? element2.imported.name : element2.imported.value;
858
1012
  if (from === PACKAGE && !contract2.exports.includes(exported)) {
859
1013
  let suggestions = nearest(exported, Object.keys(contract2.components));
860
1014
  if (suggestions.length === 0) suggestions = nearest(exported, contract2.exports);
@@ -879,51 +1033,55 @@ function checkSource(typescript, contract2, file, text2) {
879
1033
  });
880
1034
  continue;
881
1035
  }
882
- if (isComponent(exported)) named.set(element2.name.text, exported);
1036
+ if (isComponent(exported)) named.set(element2.local.name, exported);
883
1037
  }
884
1038
  }
1039
+ const isString = (node) => node.type === "StringLiteral";
1040
+ const plainTemplate = (node) => node.type === "TemplateLiteral" && node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw : void 0;
885
1041
  const literals = (expression) => {
886
1042
  if (!expression) return [];
887
- if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression))
888
- return [expression.text];
889
- if (ts.isParenthesizedExpression(expression)) return literals(expression.expression);
890
- if (ts.isConditionalExpression(expression))
891
- return [...literals(expression.whenTrue), ...literals(expression.whenFalse)];
1043
+ if (isString(expression)) return [expression.value];
1044
+ const template = plainTemplate(expression);
1045
+ if (template !== void 0) return [template];
1046
+ if (expression.type === "ConditionalExpression")
1047
+ return [...literals(expression.consequent), ...literals(expression.alternate)];
892
1048
  return [];
893
1049
  };
894
- const valueOf = (attribute) => attribute.initializer === void 0 ? void 0 : ts.isJsxExpression(attribute.initializer) ? attribute.initializer.expression : attribute.initializer;
1050
+ const valueOf = (attribute) => {
1051
+ const value = attribute.value;
1052
+ if (value === null || value === void 0) return void 0;
1053
+ if (value.type === "JSXExpressionContainer")
1054
+ return value.expression.type === "JSXEmptyExpression" ? void 0 : value.expression;
1055
+ return value;
1056
+ };
895
1057
  const optionValue = (expression) => {
896
1058
  const read = [];
897
1059
  const unread = [];
898
- const walk = (node) => {
899
- if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
900
- read.push(node.text);
901
- else if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isNonNullExpression(node) || ts.isTypeAssertionExpression(node) || typeof ts.isSatisfiesExpression === "function" && ts.isSatisfiesExpression(node))
902
- walk(node.expression);
903
- else if (ts.isConditionalExpression(node)) {
904
- walk(node.whenTrue);
905
- walk(node.whenFalse);
906
- } else if (ts.isBinaryExpression(node)) {
907
- const operator = node.operatorToken.kind;
908
- if (operator === ts.SyntaxKind.QuestionQuestionToken || operator === ts.SyntaxKind.BarBarToken) {
909
- walk(node.left);
910
- walk(node.right);
911
- } else if (operator === ts.SyntaxKind.AmpersandAmpersandToken) walk(node.right);
912
- else unread.push(node);
1060
+ const walk2 = (node) => {
1061
+ const template = plainTemplate(node);
1062
+ if (isString(node)) read.push(node.value);
1063
+ else if (template !== void 0) read.push(template);
1064
+ else if (node.type === "TSAsExpression" || node.type === "TSNonNullExpression" || node.type === "TSTypeAssertion" || node.type === "TSSatisfiesExpression")
1065
+ walk2(node.expression);
1066
+ else if (node.type === "ConditionalExpression") {
1067
+ walk2(node.consequent);
1068
+ walk2(node.alternate);
1069
+ } else if (node.type === "LogicalExpression") {
1070
+ if (node.operator === "??" || node.operator === "||") {
1071
+ walk2(node.left);
1072
+ walk2(node.right);
1073
+ } else walk2(node.right);
913
1074
  } else if (!// No value: the component's default applies.
914
- (ts.isIdentifier(node) && node.text === "undefined" || node.kind === ts.SyntaxKind.NullKeyword))
1075
+ (node.type === "Identifier" && node.name === "undefined" || node.type === "NullLiteral"))
915
1076
  unread.push(node);
916
1077
  };
917
- if (expression) walk(expression);
1078
+ if (expression) walk2(expression);
918
1079
  return { literals: read, unread };
919
1080
  };
920
1081
  const classText = (expression) => {
921
1082
  if (!expression) return "";
922
- if (ts.isTemplateExpression(expression))
923
- return [
924
- expression.head.text,
925
- ...expression.templateSpans.map((span) => span.literal.text)
926
- ].join(" ");
1083
+ if (expression.type === "TemplateLiteral" && expression.expressions.length > 0)
1084
+ return expression.quasis.map((span) => span.value.cooked ?? span.value.raw).join(" ");
927
1085
  return literals(expression).join(" ");
928
1086
  };
929
1087
  const houseRules = (attribute, name) => {
@@ -949,9 +1107,9 @@ function checkSource(typescript, contract2, file, text2) {
949
1107
  message: "A rounded corner. The radius is 0 everywhere in OPERATOR."
950
1108
  });
951
1109
  }
952
- if (name === "style" && value && ts.isObjectLiteralExpression(value)) {
1110
+ if (name === "style" && value && value.type === "ObjectExpression") {
953
1111
  for (const property of value.properties)
954
- if (ts.isPropertyAssignment(property) && literals(property.initializer).some((literal) => RAW_COLOR_VALUE.test(literal)))
1112
+ if (property.type === "ObjectProperty" && literals(property.value).some((literal) => RAW_COLOR_VALUE.test(literal)))
955
1113
  report(attribute, {
956
1114
  code: "RAW_COLOR",
957
1115
  severity: "warning",
@@ -959,47 +1117,49 @@ function checkSource(typescript, contract2, file, text2) {
959
1117
  });
960
1118
  }
961
1119
  };
1120
+ const attributeName = (attribute) => attribute.name.type === "JSXIdentifier" ? attribute.name.name : textOf(attribute.name);
962
1121
  const element = (opening, ancestors, parent, hasChildren, renderedAs) => {
963
- const tag = opening.tagName;
1122
+ const tag = opening.name;
964
1123
  let component = null;
965
- if (ts.isIdentifier(tag)) {
966
- component = named.get(tag.text) ?? null;
967
- if (component === null && !renderedAs && Object.hasOwn(NATIVE, tag.text))
1124
+ if (tag.type === "JSXIdentifier") {
1125
+ component = named.get(tag.name) ?? null;
1126
+ if (component === null && !renderedAs && Object.hasOwn(NATIVE, tag.name))
968
1127
  report(opening, {
969
1128
  code: "NATIVE_CONTROL",
970
1129
  severity: "warning",
971
- message: `A native <${tag.text}>: use ${NATIVE[tag.text]}.`
1130
+ message: `A native <${tag.name}>: use ${NATIVE[tag.name]}.`
972
1131
  });
973
- } else if (ts.isPropertyAccessExpression(tag) && ts.isIdentifier(tag.expression)) {
974
- const owner = tag.expression.text;
975
- if (namespaces.has(owner) && isComponent(tag.name.text)) component = tag.name.text;
1132
+ } else if (tag.type === "JSXMemberExpression" && tag.object.type === "JSXIdentifier") {
1133
+ const owner = tag.object.name;
1134
+ const member = tag.property.name;
1135
+ if (namespaces.has(owner) && isComponent(member)) component = member;
976
1136
  else if (named.has(owner)) {
977
- const joined = `${named.get(owner)}${tag.name.text}`;
1137
+ const joined = `${named.get(owner)}${member}`;
978
1138
  report(opening, {
979
1139
  code: "DOT_NOTATION",
980
1140
  severity: "error",
981
- message: `${owner}.${tag.name.text} is not a component.` + (isComponent(joined) ? ` Use <${joined}>.` : " OPERATOR's parts are components of their own, imported by name.")
1141
+ message: `${owner}.${member} is not a component.` + (isComponent(joined) ? ` Use <${joined}>.` : " OPERATOR's parts are components of their own, imported by name.")
982
1142
  });
983
1143
  }
984
1144
  }
985
- const attributes = opening.attributes.properties;
1145
+ const attributes = opening.attributes;
986
1146
  for (const attribute of attributes)
987
- if (ts.isJsxAttribute(attribute) && ts.isIdentifier(attribute.name))
988
- houseRules(attribute, attribute.name.text);
1147
+ if (attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier")
1148
+ houseRules(attribute, attribute.name.name);
989
1149
  if (component === null) return null;
990
1150
  const definition = contract2.components[component];
991
- const spread = attributes.some((attribute) => ts.isJsxSpreadAttribute(attribute));
1151
+ const spread = attributes.some((attribute) => attribute.type === "JSXSpreadAttribute");
992
1152
  for (const attribute of attributes)
993
- if (ts.isJsxSpreadAttribute(attribute))
1153
+ if (attribute.type === "JSXSpreadAttribute")
994
1154
  limit(attribute, {
995
1155
  code: "UNRESOLVED_SPREAD",
996
1156
  component,
997
- message: `Props spread into <${component}> (${quote(attribute.getText(source))}) were not read: option values and required props that arrive through them were not checked.`
1157
+ message: `Props spread into <${component}> (${quote(textOf(attribute))}) were not read: option values and required props that arrive through them were not checked.`
998
1158
  });
999
1159
  const given = /* @__PURE__ */ new Set();
1000
1160
  for (const attribute of attributes) {
1001
- if (!ts.isJsxAttribute(attribute)) continue;
1002
- const prop = ts.isIdentifier(attribute.name) ? attribute.name.text : attribute.name.getText(source);
1161
+ if (attribute.type !== "JSXAttribute") continue;
1162
+ const prop = attributeName(attribute);
1003
1163
  given.add(prop);
1004
1164
  const known = Object.hasOwn(definition.props, prop) ? definition.props[prop] : void 0;
1005
1165
  if (known === void 0) {
@@ -1021,7 +1181,7 @@ function checkSource(typescript, contract2, file, text2) {
1021
1181
  code: "UNRESOLVED_VALUE",
1022
1182
  component,
1023
1183
  prop,
1024
- message: `${component} ${prop} is set from an expression (${quote(expression.getText(source))}) whose value is not in the source, so it was not compared with the options.`
1184
+ message: `${component} ${prop} is set from an expression (${quote(textOf(expression))}) whose value is not in the source, so it was not compared with the options.`
1025
1185
  });
1026
1186
  for (const literal of options) {
1027
1187
  if (known.values.includes(literal)) continue;
@@ -1070,35 +1230,34 @@ function checkSource(typescript, contract2, file, text2) {
1070
1230
  return component;
1071
1231
  };
1072
1232
  const visit = (node, ancestors, parent, renderedAs = false) => {
1073
- if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
1074
- const opening = ts.isJsxElement(node) ? node.openingElement : node;
1075
- const hasChildren = ts.isJsxElement(node) && node.children.some(
1076
- (child) => !(ts.isJsxText(child) && child.containsOnlyTriviaWhiteSpaces)
1233
+ if (node.type === "JSXElement") {
1234
+ const opening = node.openingElement;
1235
+ const hasChildren = node.children.some(
1236
+ (child) => !(child.type === "JSXText" && /^\s*$/.test(text2.slice(child.start ?? 0, child.end ?? 0)))
1077
1237
  );
1078
1238
  const component = element(opening, ancestors, parent, hasChildren, renderedAs);
1079
- const asChild = component !== null && opening.attributes.properties.some(
1080
- (attribute) => ts.isJsxAttribute(attribute) && attribute.name.getText(source) === "asChild"
1239
+ const asChild = component !== null && opening.attributes.some(
1240
+ (attribute) => attribute.type === "JSXAttribute" && attributeName(attribute) === "asChild"
1081
1241
  );
1082
- for (const attribute of opening.attributes.properties) visit(attribute, [], null);
1083
- if (ts.isJsxElement(node))
1084
- for (const child of node.children)
1085
- visit(
1086
- child,
1087
- component === null ? ancestors : [...ancestors, component],
1088
- component,
1089
- asChild
1090
- );
1242
+ for (const attribute of opening.attributes) visit(attribute, [], null);
1243
+ for (const child of node.children)
1244
+ visit(
1245
+ child,
1246
+ component === null ? ancestors : [...ancestors, component],
1247
+ component,
1248
+ asChild
1249
+ );
1091
1250
  return;
1092
1251
  }
1093
- ts.forEachChild(node, (child) => visit(child, ancestors, parent));
1252
+ for (const child of childNodes(node)) visit(child, ancestors, parent);
1094
1253
  };
1095
- visit(source, [], null);
1096
- const byPlace = (a, b) => a.line - b.line || a.column - b.column;
1254
+ visit(reading.tree.program, [], null);
1255
+ const byPlace2 = (a, b) => a.line - b.line || a.column - b.column;
1097
1256
  return {
1098
1257
  file,
1099
1258
  status: limitations.length > 0 ? "partial" : "evaluated",
1100
- findings: findings.sort(byPlace),
1101
- limitations: limitations.sort(byPlace)
1259
+ findings: findings.sort(byPlace2),
1260
+ limitations: limitations.sort(byPlace2)
1102
1261
  };
1103
1262
  }
1104
1263
  var SKIPPED = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", "coverage"]);
@@ -1106,19 +1265,19 @@ var READ = /* @__PURE__ */ new Set([".tsx", ".jsx"]);
1106
1265
  var NOT_INTERFACE = /\.(?:test|spec|stories)\.[jt]sx$/;
1107
1266
  var excluded = (name) => SKIPPED.has(name) || name.startsWith(".");
1108
1267
  function* sources(path) {
1109
- if (excluded((0, import_node_path4.basename)(path))) return;
1268
+ if (excluded((0, import_node_path3.basename)(path))) return;
1110
1269
  if ((0, import_node_fs3.statSync)(path).isDirectory()) {
1111
- for (const entry of (0, import_node_fs3.readdirSync)(path).sort()) yield* sources((0, import_node_path4.join)(path, entry));
1112
- } else if (READ.has((0, import_node_path4.extname)(path)) && !NOT_INTERFACE.test((0, import_node_path4.basename)(path))) yield path;
1270
+ for (const entry of (0, import_node_fs3.readdirSync)(path).sort()) yield* sources((0, import_node_path3.join)(path, entry));
1271
+ } else if (READ.has((0, import_node_path3.extname)(path)) && !NOT_INTERFACE.test((0, import_node_path3.basename)(path))) yield path;
1113
1272
  }
1114
1273
  function whyNotRead(path) {
1115
- const name = (0, import_node_path4.basename)(path);
1274
+ const name = (0, import_node_path3.basename)(path);
1116
1275
  if (excluded(name)) return "excluded_folder";
1117
1276
  if ((0, import_node_fs3.statSync)(path).isDirectory()) return null;
1118
- if (!READ.has((0, import_node_path4.extname)(path))) return "unsupported_extension";
1277
+ if (!READ.has((0, import_node_path3.extname)(path))) return "unsupported_extension";
1119
1278
  return NOT_INTERFACE.test(name) ? "test_or_story_file" : null;
1120
1279
  }
1121
- function buildCheckReport(evaluations, context) {
1280
+ function buildCheckReport(evaluations, context = {}) {
1122
1281
  const findings = evaluations.flatMap(({ findings: found }) => found);
1123
1282
  const count = (status2) => evaluations.filter((evaluation) => evaluation.status === status2).length;
1124
1283
  const partial = count("partial");
@@ -1145,7 +1304,7 @@ function buildCheckReport(evaluations, context) {
1145
1304
  limitations: evaluations.flatMap(({ limitations }) => limitations),
1146
1305
  skipped_inputs: context.skipped ?? [],
1147
1306
  scope: { checked: [...SCOPE.checked], not_checked: [...SCOPE.not_checked] },
1148
- parser: context.parser
1307
+ parser: context.parser ?? PARSER
1149
1308
  };
1150
1309
  }
1151
1310
  function checkExitCode(report, { strict }) {
@@ -1153,51 +1312,27 @@ function checkExitCode(report, { strict }) {
1153
1312
  if (report.summary.errors > 0 || report.coverage.files_not_evaluated > 0) return 1;
1154
1313
  return strict && report.summary.warnings > 0 ? 1 : 0;
1155
1314
  }
1156
- var describeProvided = (typescript) => ({
1157
- name: "typescript",
1158
- version: typescript.version ?? "unknown",
1159
- source: "provided"
1160
- });
1161
- function resolveParser(cwd, provided, who) {
1162
- if (provided !== void 0 && provided !== null)
1163
- return { typescript: provided, parser: describeProvided(provided) };
1164
- const probe = provided === null ? null : probeTypeScript(cwd);
1165
- if (probe?.ready) {
1166
- const { name, version, source } = probe.resolution;
1167
- return { typescript: probe.typescript, parser: { name, version, source } };
1168
- }
1169
- if (probe !== null && probe.reason === "typescript_unusable")
1170
- throw new CliError(`${who} cannot use the TypeScript it found: ${probe.detail}`, 1, {
1171
- code: "ERR_TYPESCRIPT_UNUSABLE"
1172
- });
1173
- throw new CliError(
1174
- `${who} reads code with the project's TypeScript, and found none. Install typescript in this project (${INSTALL_TYPESCRIPT}) and run it again. It looked from ${cwd}.`,
1175
- 1,
1176
- { code: "ERR_TYPESCRIPT_MISSING" }
1177
- );
1178
- }
1179
1315
  function checkPaths(paths, context) {
1180
- const root = (0, import_node_path4.resolve)(context.cwd);
1316
+ const root = (0, import_node_path3.resolve)(context.cwd);
1181
1317
  const files = [];
1182
1318
  const skipped = [];
1183
1319
  for (const path of paths) {
1184
- const absolute = (0, import_node_path4.resolve)(root, path);
1320
+ const absolute = (0, import_node_path3.resolve)(root, path);
1185
1321
  if (!(0, import_node_fs3.existsSync)(absolute))
1186
1322
  throw new CliError(`No such file or folder: ${path}`, 2, { code: "ERR_PATH_NOT_FOUND" });
1187
1323
  const reason = whyNotRead(absolute);
1188
1324
  if (reason !== null) skipped.push({ path, reason });
1189
1325
  files.push(...sources(absolute));
1190
1326
  }
1191
- const { typescript, parser } = resolveParser(root, context.typescript, "oprt check");
1327
+ loadBabel();
1192
1328
  const evaluations = [...new Set(files)].map(
1193
1329
  (file) => checkSource(
1194
- typescript,
1195
1330
  context.contract,
1196
- (0, import_node_path4.relative)(root, file).split(import_node_path4.sep).join("/"),
1331
+ (0, import_node_path3.relative)(root, file).split(import_node_path3.sep).join("/"),
1197
1332
  (0, import_node_fs3.readFileSync)(file, "utf8")
1198
1333
  )
1199
1334
  );
1200
- return buildCheckReport(evaluations, { parser, skipped });
1335
+ return buildCheckReport(evaluations, { skipped });
1201
1336
  }
1202
1337
  var plural = (amount, word) => `${amount} ${word}${amount === 1 ? "" : "s"}`;
1203
1338
  var MAX_LIMITS_SHOWN = 20;
@@ -1239,7 +1374,7 @@ function renderCheckText(report) {
1239
1374
 
1240
1375
  // src/doctor.ts
1241
1376
  var import_node_fs4 = require("fs");
1242
- var import_node_path5 = require("path");
1377
+ var import_node_path4 = require("path");
1243
1378
 
1244
1379
  // src/agent-block.ts
1245
1380
  var START = "<!-- OPERATOR:START -->";
@@ -1321,48 +1456,48 @@ var INSTRUCTION_FILES = [
1321
1456
  ".claude/CLAUDE.md",
1322
1457
  ".cursor/rules/operator.mdc"
1323
1458
  ];
1324
- var minor = (version) => version.split(".").slice(0, 2).join(".");
1459
+ var minor = (version2) => version2.split(".").slice(0, 2).join(".");
1325
1460
  function installedVersion(cwd, name) {
1326
- for (let folder = cwd; ; folder = (0, import_node_path5.dirname)(folder)) {
1327
- const manifest = (0, import_node_path5.join)(folder, "node_modules", name, "package.json");
1461
+ for (let folder = cwd; ; folder = (0, import_node_path4.dirname)(folder)) {
1462
+ const manifest = (0, import_node_path4.join)(folder, "node_modules", name, "package.json");
1328
1463
  if ((0, import_node_fs4.existsSync)(manifest))
1329
1464
  return JSON.parse((0, import_node_fs4.readFileSync)(manifest, "utf8")).version;
1330
- if ((0, import_node_path5.dirname)(folder) === folder) return void 0;
1465
+ if ((0, import_node_path4.dirname)(folder) === folder) return void 0;
1331
1466
  }
1332
1467
  }
1333
1468
  function doctor({
1334
1469
  cwd,
1335
1470
  contract: contract2,
1336
- parser = probeTypeScript(cwd)
1471
+ parser = probeParser()
1337
1472
  }) {
1338
1473
  const checks = [];
1339
1474
  for (const name of PACKAGES) {
1340
- const version = installedVersion(cwd, name);
1475
+ const version2 = installedVersion(cwd, name);
1341
1476
  const id = `package:${name}`;
1342
- if (version === void 0)
1477
+ if (version2 === void 0)
1343
1478
  checks.push({
1344
1479
  id,
1345
1480
  status: "warn",
1346
1481
  message: `${name} is not installed.`,
1347
1482
  fix: `Install ${name}, or copy the sources you need with oprt add.`
1348
1483
  });
1349
- else if (minor(version) !== minor(contract2.version))
1484
+ else if (minor(version2) !== minor(contract2.version))
1350
1485
  checks.push({
1351
1486
  id,
1352
1487
  status: "warn",
1353
- message: `${name} is ${version}; this installer describes ${contract2.version}.`,
1488
+ message: `${name} is ${version2}; this installer describes ${contract2.version}.`,
1354
1489
  fix: "Install matching versions of the OPERATOR packages and @oprt/cli."
1355
1490
  });
1356
- else checks.push({ id, status: "ok", message: `${name} ${version} is installed.` });
1491
+ else checks.push({ id, status: "ok", message: `${name} ${version2} is installed.` });
1357
1492
  }
1358
1493
  const written = INSTRUCTION_FILES.map((path) => {
1359
- const file = (0, import_node_path5.join)(cwd, path);
1494
+ const file = (0, import_node_path4.join)(cwd, path);
1360
1495
  return {
1361
1496
  path,
1362
1497
  version: (0, import_node_fs4.existsSync)(file) ? blockVersion((0, import_node_fs4.readFileSync)(file, "utf8")) : void 0
1363
1498
  };
1364
- }).filter(({ version }) => version !== void 0);
1365
- const stale = written.find(({ version }) => version !== contract2.version);
1499
+ }).filter(({ version: version2 }) => version2 !== void 0);
1500
+ const stale = written.find(({ version: version2 }) => version2 !== contract2.version);
1366
1501
  if (written.length === 0)
1367
1502
  checks.push({
1368
1503
  id: "agent-instructions",
@@ -1383,39 +1518,32 @@ function doctor({
1383
1518
  status: "ok",
1384
1519
  message: `${written.map(({ path }) => path).join(", ")} carries the OPERATOR block (${contract2.version}).`
1385
1520
  });
1386
- const install2 = `${INSTALL_TYPESCRIPT} (or your package manager's equivalent), then run oprt doctor again. Lookups work without it.`;
1387
1521
  let checkCode;
1388
1522
  if (parser.ready) {
1389
- const { version, resolved_from, source } = parser.resolution;
1523
+ const { name, version: version2 } = parser.parser;
1390
1524
  checkCode = {
1391
1525
  status: "ready",
1392
- message: `TypeScript ${version}, resolved from ${resolved_from}${source === "project" ? "" : " (beside oprt, not this project)"}.`,
1393
- typescript: { version, resolved_from, source }
1526
+ message: `${name} ${version2}, bundled with oprt: nothing to install.`,
1527
+ parser: parser.parser
1394
1528
  };
1395
- checks.push(
1396
- source === "project" ? {
1397
- id: "typescript",
1398
- status: "ok",
1399
- message: `TypeScript ${version} is installed in this project (${resolved_from}).`
1400
- } : {
1401
- id: "typescript",
1402
- status: "warn",
1403
- message: `TypeScript ${version} is not in this project; the one beside oprt is used (${resolved_from}).`,
1404
- fix: `${INSTALL_TYPESCRIPT}, so checking reads code with a version installed in the project.`
1405
- }
1406
- );
1529
+ checks.push({
1530
+ id: "parser",
1531
+ status: "ok",
1532
+ message: `Code checking reads code with ${name} ${version2}, which ships inside oprt.`
1533
+ });
1407
1534
  } else {
1535
+ const fix = "Install @oprt/cli again, then run oprt doctor again. Lookups work without it.";
1408
1536
  checkCode = {
1409
1537
  status: "blocked",
1410
1538
  message: `${parser.reason}: ${parser.detail}`,
1411
1539
  reason: parser.reason,
1412
- fix: install2
1540
+ fix
1413
1541
  };
1414
1542
  checks.push({
1415
- id: "typescript",
1543
+ id: "parser",
1416
1544
  status: "fail",
1417
1545
  message: `oprt check and the check_code tool cannot run (${parser.reason}): ${parser.detail}`,
1418
- fix: install2
1546
+ fix
1419
1547
  });
1420
1548
  }
1421
1549
  const count = (status) => checks.filter((check) => check.status === status).length;
@@ -1436,7 +1564,7 @@ function doctor({
1436
1564
 
1437
1565
  // src/init.ts
1438
1566
  var import_node_fs5 = require("fs");
1439
- var import_node_path6 = require("path");
1567
+ var import_node_path5 = require("path");
1440
1568
  var AGENT_FILES = {
1441
1569
  claude: ["CLAUDE.md"],
1442
1570
  cursor: [".cursor/rules/operator.mdc"],
@@ -1464,7 +1592,7 @@ function init(options, context) {
1464
1592
  const dryRun = options.dryRun ?? false;
1465
1593
  const files = targets(options).map((path) => {
1466
1594
  validateRelative(path);
1467
- const destination = (0, import_node_path6.resolve)(root, path);
1595
+ const destination = (0, import_node_path5.resolve)(root, path);
1468
1596
  inspectPath(root, destination);
1469
1597
  const existing = (0, import_node_fs5.existsSync)(destination) ? (0, import_node_fs5.readFileSync)(destination, "utf8") : null;
1470
1598
  if (options.remove) {
@@ -1481,7 +1609,7 @@ function init(options, context) {
1481
1609
  const fresh = existing === null && path.endsWith(".mdc") ? CURSOR_FRONT_MATTER : existing;
1482
1610
  const { text: text2, action } = applyBlock(fresh, block);
1483
1611
  if (action !== "unchanged" && !dryRun) {
1484
- (0, import_node_fs5.mkdirSync)((0, import_node_path6.dirname)(destination), { recursive: true });
1612
+ (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(destination), { recursive: true });
1485
1613
  (0, import_node_fs5.writeFileSync)(destination, text2);
1486
1614
  }
1487
1615
  return { path, action: existing === null ? "create" : action };
@@ -1503,7 +1631,7 @@ var RESULT_TYPES = [
1503
1631
  "instructions"
1504
1632
  ];
1505
1633
  var envelope = (type, data, contract2) => ({
1506
- apiVersion: 1,
1634
+ apiVersion: RESULT_SCHEMA_VERSION,
1507
1635
  type,
1508
1636
  identity: identityOf(contract2),
1509
1637
  data
@@ -1517,7 +1645,7 @@ function errorEnvelope(error, contract2) {
1517
1645
  contract_digest
1518
1646
  }))(identityOf(contract2));
1519
1647
  return {
1520
- apiVersion: 1,
1648
+ apiVersion: RESULT_SCHEMA_VERSION,
1521
1649
  error: error instanceof Error ? error.message : String(error),
1522
1650
  code: known?.code ?? "ERR_FAILED",
1523
1651
  ...known?.suggestions === void 0 ? {} : { suggestions: known.suggestions },
@@ -1613,9 +1741,9 @@ var DEFS = {
1613
1741
  reason: words2("unsupported_extension", "test_or_story_file", "excluded_folder")
1614
1742
  }),
1615
1743
  parser: obj({
1616
- name: words2("typescript"),
1744
+ name: words2("@babel/parser"),
1617
1745
  version: str(),
1618
- source: words2("project", "installer", "provided")
1746
+ source: words2("bundled")
1619
1747
  }),
1620
1748
  check_data: obj({
1621
1749
  evaluation_status: words2(
@@ -1707,15 +1835,11 @@ var DEFS = {
1707
1835
  {
1708
1836
  status: words2("ready", "blocked"),
1709
1837
  message: str(),
1710
- reason: words2("typescript_missing", "typescript_unusable"),
1838
+ reason: words2("parser_missing", "parser_unusable"),
1711
1839
  fix: str(),
1712
- typescript: obj({
1713
- version: str(),
1714
- resolved_from: str(),
1715
- source: words2("project", "installer", "provided")
1716
- })
1840
+ parser: ref("parser")
1717
1841
  },
1718
- ["reason", "fix", "typescript"]
1842
+ ["reason", "fix", "parser"]
1719
1843
  )
1720
1844
  }),
1721
1845
  checks: arr(ref("doctor_check")),
@@ -1790,14 +1914,14 @@ var DATA = {
1790
1914
  instructions: "instructions_data"
1791
1915
  };
1792
1916
  var success = (type) => obj({
1793
- apiVersion: { const: 1 },
1917
+ apiVersion: { const: RESULT_SCHEMA_VERSION },
1794
1918
  type: { const: type },
1795
1919
  identity: ref("identity"),
1796
1920
  data: ref(DATA[type])
1797
1921
  });
1798
1922
  var failure = () => obj(
1799
1923
  {
1800
- apiVersion: { const: 1 },
1924
+ apiVersion: { const: RESULT_SCHEMA_VERSION },
1801
1925
  error: str(),
1802
1926
  code: str({ pattern: "^ERR_[A-Z_]+$" }),
1803
1927
  suggestions: names(),
@@ -2378,15 +2502,9 @@ function runTool(name, args, context) {
2378
2502
  structured: envelope("instructions", instructionsData(contract2), contract2)
2379
2503
  };
2380
2504
  case "check_code": {
2381
- const { typescript, parser } = resolveParser(
2382
- context.cwd ?? process.cwd(),
2383
- context.typescript,
2384
- "check_code"
2385
- );
2386
- const report = buildCheckReport(
2387
- [checkSource(typescript, contract2, "snippet.tsx", args["code"])],
2388
- { parser }
2389
- );
2505
+ const report = buildCheckReport([
2506
+ checkSource(contract2, "snippet.tsx", args["code"])
2507
+ ]);
2390
2508
  return { text: renderCheckText(report), structured: envelope("check", report, contract2) };
2391
2509
  }
2392
2510
  default:
@@ -2470,8 +2588,8 @@ function serve(input, output, context) {
2470
2588
 
2471
2589
  // src/index.ts
2472
2590
  function loadRegistry() {
2473
- const here = (0, import_node_path7.dirname)((0, import_node_url3.fileURLToPath)(importMetaUrl));
2474
- const candidates = [(0, import_node_path7.join)(here, "registry.json"), (0, import_node_path7.join)(here, "../generated/registry.json")];
2591
+ const here = (0, import_node_path6.dirname)((0, import_node_url3.fileURLToPath)(importMetaUrl));
2592
+ const candidates = [(0, import_node_path6.join)(here, "registry.json"), (0, import_node_path6.join)(here, "../generated/registry.json")];
2475
2593
  const found = candidates.find((candidate) => (0, import_node_fs6.existsSync)(candidate));
2476
2594
  if (found === void 0) {
2477
2595
  throw new Error(
@@ -2481,8 +2599,8 @@ function loadRegistry() {
2481
2599
  return JSON.parse((0, import_node_fs6.readFileSync)(found, "utf8"));
2482
2600
  }
2483
2601
  var registry = loadRegistry();
2484
- var loaded;
2485
- var contract = () => loaded ??= loadContract();
2602
+ var loaded2;
2603
+ var contract = () => loaded2 ??= loadContract();
2486
2604
  function install(options, context = {}) {
2487
2605
  return installSources(options, {
2488
2606
  cwd: context.cwd ?? process.cwd(),