token-goat 2.6.33 → 2.6.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/README.md +4 -0
  2. package/dist/{token-goat-chunk-WFRR2DBG.mjs → token-goat-chunk-7FWBSPCT.mjs} +446 -339
  3. package/dist/{token-goat-chunk-HPWPUBAK.mjs → token-goat-chunk-CKRATWCJ.mjs} +5 -5
  4. package/dist/{token-goat-chunk-TDYTBCT4.mjs → token-goat-chunk-CN7GUPX6.mjs} +328 -86
  5. package/dist/{token-goat-chunk-L6FVTDPD.mjs → token-goat-chunk-FRMEOIRU.mjs} +361 -23
  6. package/dist/{token-goat-chunk-RPOXYJWK.mjs → token-goat-chunk-GXBPEQR2.mjs} +70 -25
  7. package/dist/{token-goat-chunk-77CX3MCN.mjs → token-goat-chunk-I6EQH27G.mjs} +2 -2
  8. package/dist/{token-goat-chunk-FN6WLFIX.mjs → token-goat-chunk-IYYQOPY4.mjs} +801 -51
  9. package/dist/{token-goat-chunk-72R7M5H7.mjs → token-goat-chunk-L7QLEVO7.mjs} +3 -3
  10. package/dist/{token-goat-chunk-OHQ7RBLL.mjs → token-goat-chunk-OA3GFKP2.mjs} +2 -2
  11. package/dist/{token-goat-hook-chunk-HXHFLKEL.mjs → token-goat-chunk-WA2ER6TZ.mjs} +7 -2
  12. package/dist/{token-goat-chunk-RQQ4SY2P.mjs → token-goat-hook-chunk-2ISZ4AZR.mjs} +7 -2
  13. package/dist/{token-goat-hook-chunk-ZT47M3IA.mjs → token-goat-hook-chunk-6C5RN3CM.mjs} +2 -2
  14. package/dist/{token-goat-hook-chunk-UWV4MTYI.mjs → token-goat-hook-chunk-6QTWMSRO.mjs} +328 -86
  15. package/dist/{token-goat-hook-chunk-XBOWDNJB.mjs → token-goat-hook-chunk-ATEGFQAU.mjs} +5 -5
  16. package/dist/{token-goat-hook-chunk-QD2DX2U7.mjs → token-goat-hook-chunk-BU5OFDGQ.mjs} +361 -23
  17. package/dist/{token-goat-hook-chunk-E6AIPKNF.mjs → token-goat-hook-chunk-CNLMPHXT.mjs} +442 -335
  18. package/dist/{token-goat-hook-chunk-WBBHV6TZ.mjs → token-goat-hook-chunk-I5VM5PO5.mjs} +70 -25
  19. package/dist/{token-goat-hook-chunk-VDXQXYJK.mjs → token-goat-hook-chunk-KGLMNOGD.mjs} +801 -51
  20. package/dist/{token-goat-hook-chunk-EKGDLI4D.mjs → token-goat-hook-chunk-OQML5EMA.mjs} +3 -3
  21. package/dist/{token-goat-hook-chunk-26DLRODR.mjs → token-goat-hook-chunk-QTZ6YUMQ.mjs} +2 -2
  22. package/dist/token-goat-hook.mjs +5 -5
  23. package/dist/token-goat.core.mjs +5 -5
  24. package/package.json +8 -8
@@ -50,7 +50,7 @@ import {
50
50
  walkProject,
51
51
  yamlLineClosesQuote,
52
52
  yamlOpenQuoteAfter
53
- } from "./token-goat-chunk-TDYTBCT4.mjs";
53
+ } from "./token-goat-chunk-CN7GUPX6.mjs";
54
54
  import {
55
55
  PER_FILE_COUNTERFACTUAL_CEILING,
56
56
  _detectOpenQuote,
@@ -61,11 +61,14 @@ import {
61
61
  colorStdout,
62
62
  compileGrepMatcher,
63
63
  countNoun,
64
+ decodeSource,
64
65
  detectLanguage,
66
+ displaySafeText,
65
67
  ensureNewline,
66
68
  escapeRegExp,
67
69
  excludeTestsHiddenNote,
68
70
  extractErrorMessage,
71
+ fileIsAbsent,
69
72
  findHtmlHeadingMatches,
70
73
  findProject,
71
74
  fingerprintContent,
@@ -97,7 +100,7 @@ import {
97
100
  unsupportedLanguageName,
98
101
  windowsCmdQuoteArg,
99
102
  withExtension
100
- } from "./token-goat-chunk-WFRR2DBG.mjs";
103
+ } from "./token-goat-chunk-7FWBSPCT.mjs";
101
104
  import {
102
105
  registerReset
103
106
  } from "./token-goat-chunk-AO2QD2AG.mjs";
@@ -387,7 +390,7 @@ function readTextForSections(filePath, readFn) {
387
390
  text = read;
388
391
  } else {
389
392
  try {
390
- text = readFileSync(filePath, "utf-8");
393
+ text = decodeSource(readFileSync(filePath));
391
394
  } catch {
392
395
  return null;
393
396
  }
@@ -489,13 +492,13 @@ function formatJsonOutline(outline) {
489
492
  if (outline.kind === "primitive") return `(scalar ${outline.type})`;
490
493
  if (outline.kind === "object") {
491
494
  if (outline.fields.length === 0) return "(empty object)";
492
- return outline.fields.map((f) => `${f.name}: ${f.type}${f.size !== void 0 ? ` (${f.size})` : ""}`).join("\n");
495
+ return outline.fields.map((f) => `${displaySafeText(f.name)}: ${f.type}${f.size !== void 0 ? ` (${f.size})` : ""}`).join("\n");
493
496
  }
494
497
  const lines = [`array of ${outline.length} element${outline.length === 1 ? "" : "s"} (${outline.elementType})`];
495
498
  if (outline.sampleKeys !== void 0) {
496
499
  lines.push(outline.heterogeneous === true ? "keys (from first elements, shape varies across sample):" : "keys (from first elements):");
497
500
  for (const f of outline.sampleKeys) {
498
- lines.push(` ${f.name}: ${f.type}${f.size !== void 0 ? ` (${f.size})` : ""}`);
501
+ lines.push(` ${displaySafeText(f.name)}: ${f.type}${f.size !== void 0 ? ` (${f.size})` : ""}`);
499
502
  }
500
503
  }
501
504
  return lines.join("\n");
@@ -583,6 +586,474 @@ function queryJson(data, spec) {
583
586
  return evalJsonPath(data, parseJsonPath(spec));
584
587
  }
585
588
 
589
+ // src/xml_query.ts
590
+ var XML_NAME_START = "A-Za-z_:\xC0-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uD800-\uDFFF\uF900-\uFDCF\uFDF0-\uFFFD";
591
+ var XML_NAME_REST = "A-Za-z_:\xC0-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uD800-\uDFFF\uF900-\uFDCF\uFDF0-\uFFFD0-9.\xB7\u0300-\u036F\u203F-\u2040-";
592
+ var XML_NAME = `[${XML_NAME_START}][${XML_NAME_REST}]*`;
593
+ var XML_ATTR_REGION = `(?:[^>"']|"[^"]*"|'[^']*'|["'])*?`;
594
+ var XML_DOCTYPE_TOKEN = `<!DOCTYPE(?:[^>"']|"[^"]*"|'[^']*')*>`;
595
+ function decodeXmlEntities(text) {
596
+ return text.replace(/&(?:quot|apos|lt|gt|amp|#x([0-9a-fA-F]+)|#([0-9]+));/g, (match, hex, dec) => {
597
+ if (match === "&quot;") return '"';
598
+ if (match === "&apos;") return "'";
599
+ if (match === "&lt;") return "<";
600
+ if (match === "&gt;") return ">";
601
+ if (match === "&amp;") return "&";
602
+ if (hex !== void 0) {
603
+ const code = parseInt(hex, 16);
604
+ try {
605
+ return code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
606
+ } catch {
607
+ return match;
608
+ }
609
+ }
610
+ if (dec !== void 0) {
611
+ const code = parseInt(dec, 10);
612
+ try {
613
+ return code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
614
+ } catch {
615
+ return match;
616
+ }
617
+ }
618
+ return match;
619
+ });
620
+ }
621
+ function escapeXmlText(text) {
622
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
623
+ }
624
+ function escapeXmlAttr(text) {
625
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/\n/g, "&#xA;").replace(/\r/g, "&#xD;").replace(/\t/g, "&#x9;");
626
+ }
627
+ function parseXmlTree(xmlText) {
628
+ const leadingTrim = xmlText.length - xmlText.trimStart().length;
629
+ let text = xmlText.trim();
630
+ let lineOffset = (xmlText.slice(0, leadingTrim).match(/\n/g) ?? []).length;
631
+ if (text.charCodeAt(0) === 65279) text = text.slice(1);
632
+ let doctype = null;
633
+ const namespaces = {};
634
+ let totalElements = 0;
635
+ const doctypeMatch = new RegExp(
636
+ `<!DOCTYPE\\s+((?:[^>[\\]"']|"[^"]*"|'[^']*'|\\[[\\s\\S]*?\\])*)\\s*>`,
637
+ "i"
638
+ ).exec(text);
639
+ if (doctypeMatch) {
640
+ doctype = doctypeMatch[1]?.trim() ?? null;
641
+ }
642
+ function parseAttributes(attrString) {
643
+ const attrs = {};
644
+ const attrRegex = new RegExp(`(${XML_NAME})(?:\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+)))?`, "g");
645
+ let m;
646
+ while ((m = attrRegex.exec(attrString)) !== null) {
647
+ const name = m[1];
648
+ const rawVal = m[2] !== void 0 ? m[2] : m[3] !== void 0 ? m[3] : m[4] ?? "";
649
+ const val = decodeXmlEntities(rawVal);
650
+ attrs[name] = val;
651
+ if (name === "xmlns" || name.startsWith("xmlns:")) {
652
+ namespaces[name] = val;
653
+ }
654
+ }
655
+ return attrs;
656
+ }
657
+ const tagRegex = new RegExp(
658
+ // eslint-disable-next-line no-misleading-character-class
659
+ `<(\\/)?(${XML_NAME})(${XML_ATTR_REGION})(\\/)?>|<!--[\\s\\S]*?-->|<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>|<\\?[\\s\\S]*?\\?>|${XML_DOCTYPE_TOKEN}`,
660
+ "gi"
661
+ );
662
+ const stack = [];
663
+ let rootNode = null;
664
+ let match;
665
+ let lastIndex = 0;
666
+ let lineScan = 0;
667
+ while ((match = tagRegex.exec(text)) !== null) {
668
+ const fullMatch = match[0];
669
+ const isClosing = match[1] === "/";
670
+ const tagName = match[2];
671
+ const attrText = match[3] ?? "";
672
+ const isSelfClosing = match[4] === "/" || fullMatch.endsWith("/>");
673
+ const cdataContent = match[5];
674
+ if (stack.length > 0 && match.index > lastIndex) {
675
+ const raw = text.slice(lastIndex, match.index);
676
+ if (raw.trim()) {
677
+ stack[stack.length - 1].text += decodeXmlEntities(raw);
678
+ }
679
+ }
680
+ if (cdataContent !== void 0) {
681
+ if (stack.length > 0) {
682
+ stack[stack.length - 1].text += cdataContent;
683
+ }
684
+ lastIndex = tagRegex.lastIndex;
685
+ continue;
686
+ }
687
+ if (!tagName) {
688
+ lastIndex = tagRegex.lastIndex;
689
+ continue;
690
+ }
691
+ while (lineScan < match.index) {
692
+ if (text.charCodeAt(lineScan) === 10) lineOffset++;
693
+ lineScan++;
694
+ }
695
+ const line = lineOffset + 1;
696
+ if (isClosing) {
697
+ if (stack.length > 0) {
698
+ let popIdx = stack.length - 1;
699
+ while (popIdx >= 0 && stack[popIdx].tag !== tagName) {
700
+ popIdx--;
701
+ }
702
+ if (popIdx >= 0) {
703
+ while (stack.length > popIdx) {
704
+ stack.pop();
705
+ }
706
+ }
707
+ }
708
+ } else {
709
+ totalElements++;
710
+ const attrs = parseAttributes(attrText);
711
+ const newNode = {
712
+ tag: tagName,
713
+ attributes: attrs,
714
+ children: [],
715
+ text: "",
716
+ line
717
+ };
718
+ if (stack.length === 0) {
719
+ if (!rootNode) rootNode = newNode;
720
+ } else {
721
+ stack[stack.length - 1].children.push(newNode);
722
+ }
723
+ if (!isSelfClosing) {
724
+ stack.push(newNode);
725
+ }
726
+ }
727
+ lastIndex = tagRegex.lastIndex;
728
+ }
729
+ return { root: rootNode, namespaces, doctype, totalElements };
730
+ }
731
+ function outlineXml(xmlText, opts = {}) {
732
+ const maxDepth = opts.maxDepth ?? 4;
733
+ const { root, namespaces, doctype, totalElements } = parseXmlTree(xmlText);
734
+ if (!root) {
735
+ throw new Error("No valid XML root element found");
736
+ }
737
+ const uniqueTags = /* @__PURE__ */ new Set();
738
+ let maxTreeDepth = 1;
739
+ function buildElementOutline(node, currentDepth) {
740
+ uniqueTags.add(node.tag);
741
+ if (currentDepth > maxTreeDepth) maxTreeDepth = currentDepth;
742
+ const keyAttrs = {};
743
+ for (const [k, v] of Object.entries(node.attributes)) {
744
+ if (!k.startsWith("xmlns")) {
745
+ keyAttrs[k] = v;
746
+ }
747
+ }
748
+ const children = [];
749
+ if (currentDepth < maxDepth) {
750
+ const tagCounts = {};
751
+ for (const child of node.children) {
752
+ tagCounts[child.tag] = (tagCounts[child.tag] || 0) + 1;
753
+ }
754
+ for (const child of node.children) {
755
+ const count = tagCounts[child.tag] || 0;
756
+ const existingWithTag = children.filter((c) => c.tag === child.tag).length;
757
+ if (count > 5 && existingWithTag >= 3) {
758
+ if (existingWithTag === 3) {
759
+ children.push({
760
+ tag: `... (${count - 3} more <${child.tag}> elements)`,
761
+ attributes: {},
762
+ childCount: 0,
763
+ children: [],
764
+ textLength: 0
765
+ });
766
+ }
767
+ continue;
768
+ }
769
+ children.push(buildElementOutline(child, currentDepth + 1));
770
+ }
771
+ }
772
+ return {
773
+ tag: node.tag,
774
+ attributes: keyAttrs,
775
+ childCount: node.children.length,
776
+ children,
777
+ textLength: node.text.length
778
+ };
779
+ }
780
+ const tree = buildElementOutline(root, 1);
781
+ return {
782
+ rootTag: root.tag,
783
+ namespaces,
784
+ doctype,
785
+ totalElements,
786
+ uniqueTags: Array.from(uniqueTags).sort(),
787
+ maxDepth: maxTreeDepth,
788
+ tree
789
+ };
790
+ }
791
+ function formatXmlOutline(summary) {
792
+ const lines = [];
793
+ lines.push(
794
+ `Root element: <${summary.rootTag}> (${summary.totalElements} total elements, ${summary.uniqueTags.length} unique tags, max depth ${summary.maxDepth})`
795
+ );
796
+ if (summary.doctype) {
797
+ lines.push(`DOCTYPE: ${summary.doctype}`);
798
+ }
799
+ const nsKeys = Object.keys(summary.namespaces);
800
+ if (nsKeys.length > 0) {
801
+ lines.push("Namespaces:");
802
+ for (const [k, v] of Object.entries(summary.namespaces)) {
803
+ lines.push(` ${k}: ${v}`);
804
+ }
805
+ }
806
+ lines.push("");
807
+ lines.push("Element hierarchy:");
808
+ function renderNode(node, indent) {
809
+ const pad = " ".repeat(indent);
810
+ const attrParts = [];
811
+ const attrKeys = Object.keys(node.attributes);
812
+ for (const [k, v] of Object.entries(node.attributes)) {
813
+ if (["id", "name", "type", "key", "class", "code", "status", "value"].includes(k.toLowerCase()) || attrKeys.length <= 3) {
814
+ const valPreview = v.length > 35 ? `${v.slice(0, 32)}...` : v;
815
+ attrParts.push(`${k}="${valPreview}"`);
816
+ }
817
+ }
818
+ const attrStr = attrParts.length > 0 ? ` [${attrParts.join(" ")}]` : "";
819
+ const countStr = node.childCount > 0 ? ` (${node.childCount} children)` : node.textLength > 0 ? ` (~${node.textLength} chars text)` : " (empty)";
820
+ lines.push(`${pad}<${node.tag}>${attrStr}${countStr}`);
821
+ for (const child of node.children) {
822
+ renderNode(child, indent + 1);
823
+ }
824
+ }
825
+ renderNode(summary.tree, 0);
826
+ return lines.join("\n");
827
+ }
828
+ function parseXmlPath(pathStr) {
829
+ let normalized = pathStr.trim();
830
+ if (normalized === "" || normalized === "/") return [];
831
+ const isGlobalRecursive = normalized.startsWith("//");
832
+ if (isGlobalRecursive) {
833
+ normalized = normalized.slice(2);
834
+ } else if (normalized.startsWith("/")) {
835
+ normalized = normalized.slice(1);
836
+ }
837
+ const segments = [];
838
+ let inBracket = false;
839
+ let currentSegment = "";
840
+ for (let i = 0; i < normalized.length; i++) {
841
+ const ch = normalized[i];
842
+ if (ch === "[") {
843
+ inBracket = true;
844
+ currentSegment += ch;
845
+ } else if (ch === "]") {
846
+ inBracket = false;
847
+ currentSegment += ch;
848
+ } else if (!inBracket && (ch === "/" || ch === ".")) {
849
+ if (currentSegment) {
850
+ segments.push(currentSegment);
851
+ currentSegment = "";
852
+ }
853
+ if (ch === "/" && normalized[i + 1] === "/") {
854
+ segments.push("//");
855
+ i++;
856
+ }
857
+ } else {
858
+ currentSegment += ch;
859
+ }
860
+ }
861
+ if (currentSegment) segments.push(currentSegment);
862
+ const steps = [];
863
+ let nextIsRecursive = isGlobalRecursive;
864
+ for (let i = 0; i < segments.length; i++) {
865
+ const s = segments[i];
866
+ if (s === "//") {
867
+ nextIsRecursive = true;
868
+ continue;
869
+ }
870
+ const isRecursive = nextIsRecursive;
871
+ nextIsRecursive = false;
872
+ const attrSelectMatch = /^@([a-zA-Z0-9_:.\\-]+|\*)$/.exec(s);
873
+ if (attrSelectMatch) {
874
+ steps.push({ tag: "", isRecursive, attributeSelect: attrSelectMatch[1] });
875
+ continue;
876
+ }
877
+ const indexMatch = /\[(-?\d+)\]$/.exec(s);
878
+ const wildcardMatch = /\[\*\]$/.exec(s);
879
+ const attrMatch = /\[@?([a-zA-Z0-9_:.\\-]+)(?:(!?=)\s*["']?([^"'\]]*)["']?)?\]$/.exec(s);
880
+ let tag = s.replace(/\[.*\]$/, "");
881
+ if (!tag) tag = "*";
882
+ let index;
883
+ let allIndices;
884
+ let attributeFilter;
885
+ if (indexMatch) {
886
+ index = parseInt(indexMatch[1], 10);
887
+ } else if (wildcardMatch) {
888
+ allIndices = true;
889
+ } else if (attrMatch) {
890
+ const [, attrName, op, attrVal] = attrMatch;
891
+ attributeFilter = {
892
+ name: attrName,
893
+ ...attrVal !== void 0 ? { value: attrVal } : {},
894
+ ...op === "!=" ? { notEqual: true } : {}
895
+ };
896
+ }
897
+ const step = { tag, isRecursive };
898
+ if (index !== void 0) step.index = index;
899
+ if (allIndices !== void 0) step.allIndices = allIndices;
900
+ if (attributeFilter !== void 0) step.attributeFilter = attributeFilter;
901
+ steps.push(step);
902
+ }
903
+ return steps;
904
+ }
905
+ function serializeXmlNode(node, indent = 0) {
906
+ const pad = " ".repeat(indent);
907
+ const attrParts = [];
908
+ for (const [k, v] of Object.entries(node.attributes)) {
909
+ attrParts.push(`${k}="${escapeXmlAttr(v)}"`);
910
+ }
911
+ const attrStr = attrParts.length > 0 ? " " + attrParts.join(" ") : "";
912
+ if (node.children.length === 0 && !node.text) {
913
+ return `${pad}<${node.tag}${attrStr}/>`;
914
+ }
915
+ if (node.children.length === 0) {
916
+ return `${pad}<${node.tag}${attrStr}>${escapeXmlText(node.text)}</${node.tag}>`;
917
+ }
918
+ const lines = [];
919
+ lines.push(`${pad}<${node.tag}${attrStr}>`);
920
+ if (node.text) {
921
+ lines.push(`${pad} ${escapeXmlText(node.text)}`);
922
+ }
923
+ for (const child of node.children) {
924
+ lines.push(serializeXmlNode(child, indent + 1));
925
+ }
926
+ lines.push(`${pad}</${node.tag}>`);
927
+ return lines.join("\n");
928
+ }
929
+ function getAllDescendants(node) {
930
+ const desc = [];
931
+ function walk(n) {
932
+ for (const c of n.children) {
933
+ desc.push(c);
934
+ walk(c);
935
+ }
936
+ }
937
+ walk(node);
938
+ return desc;
939
+ }
940
+ function xmlNodeToJson(node) {
941
+ const hasAttrs = Object.keys(node.attributes).length > 0;
942
+ const hasChildren = node.children.length > 0;
943
+ if (!hasChildren && !hasAttrs) {
944
+ return node.text;
945
+ }
946
+ if (!hasChildren) {
947
+ const result2 = {};
948
+ for (const [k, v] of Object.entries(node.attributes)) {
949
+ result2[`@${k}`] = v;
950
+ }
951
+ if (node.text) {
952
+ result2["#text"] = node.text;
953
+ }
954
+ return result2;
955
+ }
956
+ const result = {};
957
+ for (const [k, v] of Object.entries(node.attributes)) {
958
+ result[`@${k}`] = v;
959
+ }
960
+ const childGroups = /* @__PURE__ */ new Map();
961
+ for (const child of node.children) {
962
+ const existing = childGroups.get(child.tag);
963
+ if (existing !== void 0) {
964
+ existing.push(child);
965
+ } else {
966
+ childGroups.set(child.tag, [child]);
967
+ }
968
+ }
969
+ for (const [tag, group] of childGroups) {
970
+ if (group.length === 1) {
971
+ result[tag] = xmlNodeToJson(group[0]);
972
+ } else {
973
+ result[tag] = group.map(xmlNodeToJson);
974
+ }
975
+ }
976
+ if (node.text) {
977
+ result["#text"] = node.text;
978
+ }
979
+ return result;
980
+ }
981
+ function queryXml(xmlText, pathStr) {
982
+ const { root } = parseXmlTree(xmlText);
983
+ if (!root) {
984
+ throw new Error("No valid XML root element found");
985
+ }
986
+ const steps = parseXmlPath(pathStr);
987
+ if (steps.length === 0) {
988
+ return { items: [root], fanned: false };
989
+ }
990
+ let currentCandidates = [root];
991
+ let hasFanned = false;
992
+ let isAtRootLevel = true;
993
+ for (let sIdx = 0; sIdx < steps.length; sIdx++) {
994
+ const step = steps[sIdx];
995
+ if (step.attributeSelect !== void 0) {
996
+ const attrVals = [];
997
+ const attrName = step.attributeSelect;
998
+ for (const cand of currentCandidates) {
999
+ if (attrName === "*") {
1000
+ attrVals.push(...Object.values(cand.attributes));
1001
+ } else if (cand.attributes[attrName] !== void 0) {
1002
+ attrVals.push(cand.attributes[attrName]);
1003
+ }
1004
+ }
1005
+ return {
1006
+ items: currentCandidates,
1007
+ attributeValues: attrVals,
1008
+ fanned: hasFanned || attrVals.length > 1
1009
+ };
1010
+ }
1011
+ const nextCandidates = [];
1012
+ const evaluateOnCurrentNode = isAtRootLevel && !step.isRecursive && (step.tag === "*" || step.tag.toLowerCase() === root.tag.toLowerCase());
1013
+ isAtRootLevel = false;
1014
+ for (const cand of currentCandidates) {
1015
+ let targets;
1016
+ if (evaluateOnCurrentNode) {
1017
+ targets = [cand];
1018
+ } else if (step.isRecursive) {
1019
+ targets = [cand, ...getAllDescendants(cand)];
1020
+ } else {
1021
+ targets = cand.children;
1022
+ }
1023
+ let matching = targets.filter((c) => step.tag === "*" || c.tag.toLowerCase() === step.tag.toLowerCase());
1024
+ if (step.attributeFilter) {
1025
+ const af = step.attributeFilter;
1026
+ matching = matching.filter((c) => {
1027
+ const attrVal = c.attributes[af.name];
1028
+ if (attrVal !== void 0) {
1029
+ if (af.value === void 0) return true;
1030
+ return af.notEqual ? attrVal !== af.value : attrVal === af.value;
1031
+ }
1032
+ const childElem = c.children.find((child) => child.tag.toLowerCase() === af.name.toLowerCase());
1033
+ if (childElem !== void 0) {
1034
+ if (af.value === void 0) return true;
1035
+ return af.notEqual ? childElem.text !== af.value : childElem.text === af.value;
1036
+ }
1037
+ return af.notEqual === true;
1038
+ });
1039
+ }
1040
+ if (step.index !== void 0) {
1041
+ const idx = step.index < 0 ? matching.length + step.index : step.index;
1042
+ if (idx >= 0 && idx < matching.length) {
1043
+ nextCandidates.push(matching[idx]);
1044
+ }
1045
+ } else {
1046
+ if (step.allIndices || matching.length > 1 || step.attributeFilter !== void 0) {
1047
+ hasFanned = true;
1048
+ }
1049
+ nextCandidates.push(...matching);
1050
+ }
1051
+ }
1052
+ currentCandidates = nextCandidates;
1053
+ }
1054
+ return { items: currentCandidates, fanned: hasFanned || currentCandidates.length > 1 };
1055
+ }
1056
+
586
1057
  // node_modules/js-yaml/dist/js-yaml.mjs
587
1058
  var NOT_RESOLVED = /* @__PURE__ */ Symbol("NOT_RESOLVED");
588
1059
  function defineScalarTag(tagName, options) {
@@ -3094,7 +3565,7 @@ async function listZipEntries(data) {
3094
3565
  }
3095
3566
  function formatZipList(entries) {
3096
3567
  if (entries.length === 0) return "(no entries found)";
3097
- return entries.map((e) => `${String(e.size).padStart(10)} ${e.path}`).join("\n");
3568
+ return entries.map((e) => `${String(e.size).padStart(10)} ${displaySafeText(e.path)}`).join("\n");
3098
3569
  }
3099
3570
  async function extractZipEntry(data, entryPath) {
3100
3571
  const fflate = await requireFflate();
@@ -4733,8 +5204,8 @@ function readFileText(p) {
4733
5204
  verifyStillAbsent(p);
4734
5205
  return null;
4735
5206
  }
4736
- if (pinned !== void 0) return redactIfDotenv(p, readPinnedBytes(p, pinned).toString("utf-8"));
4737
- return redactIfDotenv(p, fs5.readFileSync(p, "utf-8"));
5207
+ if (pinned !== void 0) return redactIfDotenv(p, decodeSource(readPinnedBytes(p, pinned)));
5208
+ return redactIfDotenv(p, decodeSource(fs5.readFileSync(p)));
4738
5209
  } catch (err) {
4739
5210
  if (err instanceof ConfinementIdentityError) throw err;
4740
5211
  return null;
@@ -4764,11 +5235,21 @@ function resolveBody(entry) {
4764
5235
  return source.split(/\r?\n/).slice(Math.max(0, entry.lineStart - 1), entry.lineEnd).join("\n");
4765
5236
  }
4766
5237
  var STALE_WARNING = "\u26A0 STALE: index is older than the file on disk (worker hasn't reindexed yet \u2014 retry shortly, or read the file directly)";
5238
+ var DELETED_WARNING = "\u26A0 DELETED: this file is no longer on disk \u2014 what follows is what the index last saw of it";
5239
+ var DELETED_TAG = "\u26A0 DELETED: file no longer on disk";
5240
+ function fileIsGone(absPath) {
5241
+ if (!path5.isAbsolute(absPath)) return false;
5242
+ return fileIsAbsent(absPath);
5243
+ }
4767
5244
  function staleWarning(resolvedPath) {
4768
5245
  const entry = getFileEntry(resolvedPath);
4769
5246
  if (entry === null || entry.sha === "") return "";
4770
5247
  const diskSha = fingerprintFile(resolvedPath);
4771
- if (diskSha === null || diskSha === entry.sha) return "";
5248
+ if (diskSha === null) {
5249
+ return fileIsGone(resolvedPath) ? `${DELETED_WARNING}
5250
+ ` : "";
5251
+ }
5252
+ if (diskSha === entry.sha) return "";
4772
5253
  return `${STALE_WARNING}
4773
5254
  `;
4774
5255
  }
@@ -5055,18 +5536,14 @@ function runSymbol(opts) {
5055
5536
  }
5056
5537
  const matchesGrep = opts.grep !== void 0 ? compileGrepMatcher(opts.grep) : void 0;
5057
5538
  const excludeTests = opts.excludeTests === true;
5058
- const confineToProject = !loadConfig().indexing.cross_project_symbols;
5059
- const confinedRoot = confineToProject ? resolveProjectRoot() : null;
5539
+ const confinedRoot = confinedProjectRoot();
5060
5540
  if (confinedRoot !== null) {
5061
5541
  const requested = opts.projectRoot;
5062
- if (requested !== void 0 && !isInsideRoot(requested, confinedRoot)) {
5063
- return { text: `--project is outside this project root, and indexing.cross_project_symbols = false confines symbol lookups to it: ${toDisplayPath(confinedRoot, requested)}`, code: 1 };
5064
- }
5542
+ const projectDenial = requested === void 0 ? null : confinementRefusal("--project", requested, confinedRoot);
5543
+ if (projectDenial !== null) return { text: projectDenial, code: 1 };
5065
5544
  if (opts.file !== void 0) {
5066
- const resolved = resolveIndexPath(opts.file, requested ?? process.cwd());
5067
- if (!isInsideRoot(resolved, confinedRoot)) {
5068
- return { text: `--file is outside this project root, and indexing.cross_project_symbols = false confines symbol lookups to it: ${toDisplayPath(confinedRoot, resolved)}`, code: 1 };
5069
- }
5545
+ const fileDenial = confinementRefusal("--file", resolveIndexPath(opts.file, requested ?? process.cwd()), confinedRoot);
5546
+ if (fileDenial !== null) return { text: fileDenial, code: 1 };
5070
5547
  }
5071
5548
  }
5072
5549
  const queryOpts = {};
@@ -5151,6 +5628,9 @@ ${emptyIndexMessage(emptyIndexRoot)}`;
5151
5628
  const items = capped.items.map((s) => ({
5152
5629
  ...s,
5153
5630
  filePath: toDisplayPath(symbolDisplayRoot, s.filePath),
5631
+ // Only present when true, so a result set of live files stays byte-identical to what this
5632
+ // command has always emitted and only the genuinely-gone rows grow a field.
5633
+ ...fileIsGone(s.filePath) ? { deleted: true } : {},
5154
5634
  ...refCounts !== void 0 ? { refCount: refCounts.get(s.name) ?? 0, hasDoc: hasRealDocstring(s.docstring) } : {}
5155
5635
  }));
5156
5636
  const payload = { items, truncated: truncatedFlag, totalCount: trueTotal };
@@ -5160,7 +5640,8 @@ ${emptyIndexMessage(emptyIndexRoot)}`;
5160
5640
  }
5161
5641
  const blocks = results.map((sym) => {
5162
5642
  const statsStr = formatStatsSuffix(refCounts, sym);
5163
- const header = `# ${sym.name} (${sym.kind}) \u2014 ${toDisplayPath(symbolDisplayRoot, sym.filePath)}:${sym.lineStart}-${sym.lineEnd}${statsStr}`;
5643
+ const goneTag = fileIsGone(sym.filePath) ? ` ${DELETED_TAG}` : "";
5644
+ const header = `# ${sym.name} (${sym.kind}) \u2014 ${toDisplayPath(symbolDisplayRoot, sym.filePath)}:${sym.lineStart}-${sym.lineEnd}${statsStr}${goneTag}`;
5164
5645
  const body = resolveBody(sym);
5165
5646
  const preview = body.split(/\r?\n/).slice(0, 5).join("\n");
5166
5647
  return preview.trim() !== "" ? `${header}
@@ -5169,7 +5650,24 @@ ${preview}` : header;
5169
5650
  const warning = opts.file !== void 0 ? staleWarning(resolveIndexPath(opts.file, opts.projectRoot ?? process.cwd())) : "";
5170
5651
  const text = guardText(warning + blocks.join("\n\n"), "symbol");
5171
5652
  recordReadStat("symbol_lookup", fullSourceBytes, text, opts.name ?? opts.file ?? opts.grep);
5172
- return { text, code: 0 };
5653
+ const symbolTotal = () => anyClientFilter ? { count: filtered.length, exact: rawResults.length < FIND_SCAN_LIMIT } : { count: countSymbols(queryOpts), exact: true };
5654
+ return { text: text + truncationFooter(results.length, effectiveLimit, symbolTotal, "matches", "--limit"), code: 0 };
5655
+ }
5656
+ function truncationFooter(shown, limit, total, plural, flag) {
5657
+ const notice = truncationNotice(shown, limit, total, plural, flag);
5658
+ return notice === null ? "" : `
5659
+
5660
+ token-goat: ${notice}`;
5661
+ }
5662
+ function refsTotal(clientFiltered, filteredTotal, shown, countExact, preScanCount) {
5663
+ if (!clientFiltered) return { count: countExact(), exact: true };
5664
+ return { count: filteredTotal ?? shown, exact: preScanCount < REFS_TOP_SCAN_LIMIT };
5665
+ }
5666
+ function truncationNotice(shown, limit, total, plural, flag) {
5667
+ if (shown < limit) return null;
5668
+ const { count, exact } = total();
5669
+ if (count <= shown) return null;
5670
+ return exact ? `showing ${shown} of ${count} ${plural}; rerun with ${flag} ${count} to see them all` : `showing ${shown} of at least ${count} ${plural}; rerun with ${flag} ${count} and a narrower filter to see more`;
5173
5671
  }
5174
5672
  function parseReadSpec(spec) {
5175
5673
  const colonIdx = findSpecSeparator(spec);
@@ -5311,6 +5809,18 @@ function formatAmbiguity(symbol, file, candidates, explicitRoot, commandName = "
5311
5809
  }
5312
5810
  return lines.join("\n");
5313
5811
  }
5812
+ function confinedProjectRoot() {
5813
+ return loadConfig().indexing.cross_project_symbols ? null : resolveProjectRoot();
5814
+ }
5815
+ function confinementRefusal(label, resolved, root) {
5816
+ if (root === null || isInsideRoot(resolved, root)) return null;
5817
+ return `${label} is outside this project root, and indexing.cross_project_symbols = false confines symbol lookups to it: ${toDisplayPath(root, resolved)}`;
5818
+ }
5819
+ function fileConfinementRefusal(label, file, projectRoot) {
5820
+ const root = confinedProjectRoot();
5821
+ if (root === null) return null;
5822
+ return confinementRefusal(label, resolveIndexPath(file, projectRoot ?? process.cwd()), root);
5823
+ }
5314
5824
  function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
5315
5825
  const { file, symbol: rawSymbol } = parseReadSpec(spec);
5316
5826
  if (rawSymbol === void 0 || rawSymbol === "") return { kind: "none" };
@@ -5318,6 +5828,8 @@ function resolveSymbolSpec(spec, forceRefresh, projectRoot) {
5318
5828
  const symbol = anchorMatch !== null ? anchorMatch[1] : rawSymbol;
5319
5829
  const lineAnchor = anchorMatch !== null ? parseInt(anchorMatch[2], 10) : void 0;
5320
5830
  const resolved = resolveIndexPath(file, projectRoot ?? process.cwd());
5831
+ const confined = confinementRefusal("This file", resolved, confinedProjectRoot());
5832
+ if (confined !== null) return { kind: "confined", message: confined };
5321
5833
  if (forceRefresh === true) {
5322
5834
  indexFileSyncPinned(resolved, globalDbPath());
5323
5835
  enqueueDirtyPathSafe(resolved, { alreadyResolved: true });
@@ -5399,6 +5911,7 @@ function runRead(opts) {
5399
5911
  return { text: guardText(text2, "symbol"), code: 0 };
5400
5912
  }
5401
5913
  const resolution = resolveSymbolSpec(opts.spec, opts.forceRefresh, opts.projectRoot);
5914
+ if (resolution.kind === "confined") return { text: resolution.message, code: 1 };
5402
5915
  if (resolution.kind === "ambiguous") {
5403
5916
  return {
5404
5917
  text: formatAmbiguity(
@@ -5433,6 +5946,9 @@ function runRead(opts) {
5433
5946
  {
5434
5947
  ...match,
5435
5948
  body: resolveBody(match),
5949
+ // The text branch below prepends staleWarning's DELETED line; without this the JSON form
5950
+ // would be the one surface that still passes a deleted file's body off as a live read.
5951
+ ...fileIsGone(match.filePath) ? { deleted: true } : {},
5436
5952
  ...refCounts !== void 0 ? { refCount: refCounts.get(match.name) ?? 0 } : {}
5437
5953
  },
5438
5954
  null,
@@ -5619,6 +6135,15 @@ function parseMultiRefsSpec(spec) {
5619
6135
  const symbols = symPart.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
5620
6136
  return { file, symbols };
5621
6137
  }
6138
+ function refsRootDir(opts) {
6139
+ return opts.projectRoot ?? confinedProjectRoot() ?? void 0;
6140
+ }
6141
+ function refsSpecFiles(spec) {
6142
+ const crossFile = parseCrossFileMultiSpec(spec);
6143
+ if (crossFile !== null) return [...new Set(crossFile.map((p) => p.file))];
6144
+ const { file } = parseMultiRefsSpec(spec);
6145
+ return file === void 0 ? [] : [file];
6146
+ }
5622
6147
  function runRefs(opts) {
5623
6148
  if (opts.limit !== void 0 && opts.limit <= 0) {
5624
6149
  emitErr(`--limit must be a positive number, got: ${opts.limit}`);
@@ -5628,6 +6153,21 @@ function runRefs(opts) {
5628
6153
  emitErr(`--top must be a positive number, got: ${opts.top}`);
5629
6154
  return 1;
5630
6155
  }
6156
+ const confinedRoot = confinedProjectRoot();
6157
+ if (confinedRoot !== null) {
6158
+ const projectDenial = opts.projectRoot === void 0 ? null : confinementRefusal("--project", opts.projectRoot, confinedRoot);
6159
+ if (projectDenial !== null) {
6160
+ emitErr(projectDenial);
6161
+ return 1;
6162
+ }
6163
+ for (const file2 of refsSpecFiles(opts.spec)) {
6164
+ const denial = confinementRefusal("This file", resolveIndexPath(file2, opts.projectRoot ?? process.cwd()), confinedRoot);
6165
+ if (denial !== null) {
6166
+ emitErr(denial);
6167
+ return 1;
6168
+ }
6169
+ }
6170
+ }
5631
6171
  const crossFilePairs = parseCrossFileMultiSpec(opts.spec);
5632
6172
  if (crossFilePairs !== null) return runRefsCrossFile(crossFilePairs, opts);
5633
6173
  const { file, symbols } = parseMultiRefsSpec(opts.spec);
@@ -5641,8 +6181,11 @@ function runRefs(opts) {
5641
6181
  if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
5642
6182
  else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
5643
6183
  else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
5644
- if (opts.projectRoot !== void 0) queryOpts.rootDir = opts.projectRoot;
5645
- let results = applyTypedRefsTier(sym, file, queryRefs(queryOpts));
6184
+ const rootDir = refsRootDir(opts);
6185
+ if (rootDir !== void 0) queryOpts.rootDir = rootDir;
6186
+ const scanned = queryRefs(queryOpts);
6187
+ const preScanCount = scanned.length;
6188
+ let results = applyTypedRefsTier(sym, file, scanned);
5646
6189
  let suppressed = 0;
5647
6190
  if (opts.excludeTests === true) {
5648
6191
  const f = applyExcludeTestsFilter(results);
@@ -5689,6 +6232,16 @@ function runRefs(opts) {
5689
6232
  if (opts.excludeTests === true && suppressed > 0) lines.push(` ${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`);
5690
6233
  for (const ref of results) lines.push(...renderRefLines(ref, opts.context ?? 0));
5691
6234
  }
6235
+ if (opts.top === void 0) {
6236
+ const notice = truncationNotice(
6237
+ results.length,
6238
+ opts.limit ?? 100,
6239
+ () => refsTotal(opts.excludeTests === true || matchesGrep !== void 0, filteredTotal, results.length, () => countRefs(queryOpts), preScanCount),
6240
+ "references",
6241
+ "--limit"
6242
+ );
6243
+ if (notice !== null) lines.push(` token-goat: ${notice}`);
6244
+ }
5692
6245
  }
5693
6246
  const fullSourceBytes = sumFileSizes(refFilePaths);
5694
6247
  if (opts.json === true) {
@@ -5715,8 +6268,11 @@ function runRefsCrossFile(pairs, opts) {
5715
6268
  if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
5716
6269
  else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
5717
6270
  else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
5718
- if (opts.projectRoot !== void 0) queryOpts.rootDir = opts.projectRoot;
5719
- let results = applyTypedRefsTier(symbol, file, queryRefs(queryOpts));
6271
+ const rootDir = refsRootDir(opts);
6272
+ if (rootDir !== void 0) queryOpts.rootDir = rootDir;
6273
+ const scanned = queryRefs(queryOpts);
6274
+ const preScanCount = scanned.length;
6275
+ let results = applyTypedRefsTier(symbol, file, scanned);
5720
6276
  let suppressed = 0;
5721
6277
  if (opts.excludeTests === true) {
5722
6278
  const f = applyExcludeTestsFilter(results);
@@ -5761,6 +6317,16 @@ function runRefsCrossFile(pairs, opts) {
5761
6317
  if (opts.excludeTests === true && suppressed > 0) lines.push(` ${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`);
5762
6318
  for (const ref of results) lines.push(...renderRefLines(ref, opts.context ?? 0));
5763
6319
  }
6320
+ if (opts.top === void 0) {
6321
+ const notice = truncationNotice(
6322
+ results.length,
6323
+ opts.limit ?? 100,
6324
+ () => refsTotal(opts.excludeTests === true || matchesGrep !== void 0, filteredTotal, results.length, () => countRefs(queryOpts), preScanCount),
6325
+ "references",
6326
+ "--limit"
6327
+ );
6328
+ if (notice !== null) lines.push(` token-goat: ${notice}`);
6329
+ }
5764
6330
  }
5765
6331
  const fullSourceBytes = sumFileSizes(refFilePaths);
5766
6332
  if (opts.json === true) {
@@ -5782,8 +6348,11 @@ function runRefsSingle(opts) {
5782
6348
  if (opts.excludeTests === true || opts.grep !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
5783
6349
  else if (opts.limit !== void 0) queryOpts.limit = opts.limit;
5784
6350
  else if (opts.top !== void 0) queryOpts.limit = REFS_TOP_SCAN_LIMIT;
5785
- if (opts.projectRoot !== void 0) queryOpts.rootDir = opts.projectRoot;
5786
- let results = applyTypedRefsTier(symName, defFileHint, queryRefs(queryOpts));
6351
+ const rootDir = refsRootDir(opts);
6352
+ if (rootDir !== void 0) queryOpts.rootDir = rootDir;
6353
+ const scanned = queryRefs(queryOpts);
6354
+ const preScanCount = scanned.length;
6355
+ let results = applyTypedRefsTier(symName, defFileHint, scanned);
5787
6356
  let suppressed = 0;
5788
6357
  if (opts.excludeTests === true) {
5789
6358
  const f = applyExcludeTestsFilter(results);
@@ -5811,15 +6380,15 @@ function runRefsSingle(opts) {
5811
6380
  emitErr(`No non-test references found for '${symName}' (${excludeTestsHiddenNote(suppressed)})`);
5812
6381
  return 1;
5813
6382
  }
5814
- const rootDir = opts.projectRoot ?? resolveProjectRoot({ project: process.cwd() });
5815
- if (querySymbols({ name: symName, rootDir, limit: 1 }).length === 0) {
5816
- emitErr(`Symbol not found: ${symName}${unknownSymbolSuggestion(symName, rootDir)}`);
5817
- if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr(emptyIndexMessage(rootDir));
6383
+ const rootDir2 = opts.projectRoot ?? resolveProjectRoot({ project: process.cwd() });
6384
+ if (querySymbols({ name: symName, rootDir: rootDir2, limit: 1 }).length === 0) {
6385
+ emitErr(`Symbol not found: ${symName}${unknownSymbolSuggestion(symName, rootDir2)}`);
6386
+ if (opts.json !== true && isIndexEmptyForProject(globalDbPath(), rootDir2)) emitErr(emptyIndexMessage(rootDir2));
5818
6387
  return 1;
5819
6388
  }
5820
6389
  emitErr(`No references found for '${symName}'`);
5821
6390
  if (opts.json !== true) {
5822
- if (isIndexEmptyForProject(globalDbPath(), rootDir)) emitErr(emptyIndexMessage(rootDir));
6391
+ if (isIndexEmptyForProject(globalDbPath(), rootDir2)) emitErr(emptyIndexMessage(rootDir2));
5823
6392
  }
5824
6393
  return 1;
5825
6394
  }
@@ -5840,9 +6409,10 @@ function runRefsSingle(opts) {
5840
6409
  return 0;
5841
6410
  }
5842
6411
  const lines = opts.top !== void 0 ? renderTopFilesSummary(results, opts.top, suppressed) : opts.callers === true ? [...opts.excludeTests === true && suppressed > 0 ? [`${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`] : [], ...renderCallerGroups(results, opts.context ?? 0)] : [...opts.excludeTests === true && suppressed > 0 ? [`${countNoun(results.length, "reference")} (${excludeTestsHiddenNote(suppressed)})`] : [], ...results.flatMap((ref) => renderRefLines(ref, opts.context ?? 0, ""))];
6412
+ const refsFooter = opts.top !== void 0 ? "" : truncationFooter(results.length, opts.limit ?? 100, () => refsTotal(opts.excludeTests === true || matchesGrep !== void 0, filteredTotal, results.length, () => countRefs(queryOpts), preScanCount), "references", "--limit");
5843
6413
  const text = lines.join("\n");
5844
- emitGuarded(text, "symbol");
5845
- recordReadStat("symbol_read", fullSourceBytes, text, symName);
6414
+ emit(guardText(text, "symbol") + refsFooter);
6415
+ recordReadStat("symbol_read", fullSourceBytes, text + refsFooter, symName);
5846
6416
  return 0;
5847
6417
  }
5848
6418
  function applyExcludeTestsFilter(refs) {
@@ -5924,6 +6494,8 @@ function filteredToEmptyNotice(preFilterCount, minLines, grep) {
5924
6494
  }
5925
6495
  function prepareSymbolListing(file, opts) {
5926
6496
  const resolved = resolveIndexPath(file, opts.projectRoot ?? process.cwd());
6497
+ const confined = confinementRefusal("This file", resolved, confinedProjectRoot());
6498
+ if (confined !== null) return { kind: "confined", text: confined };
5927
6499
  if (opts.forceRefresh === true) {
5928
6500
  indexFileSyncPinned(resolved, globalDbPath());
5929
6501
  enqueueDirtyPathSafe(resolved, { alreadyResolved: true });
@@ -5984,7 +6556,7 @@ function runSkeleton(opts) {
5984
6556
  const multiFiles = parseMultiFileSpec(opts.file);
5985
6557
  if (multiFiles !== null) return runPerFileListing(multiFiles, (file) => runSkeleton({ ...opts, file, includeFilePath: true }), opts.json === true);
5986
6558
  const prep = prepareSymbolListing(opts.file, opts);
5987
- if (prep.kind === "empty") {
6559
+ if (prep.kind === "confined" || prep.kind === "empty") {
5988
6560
  return { text: prep.text, code: 1 };
5989
6561
  }
5990
6562
  const { resolved, displayRoot, filtered, preFilterCount, refCounts, fullSourceBytes, symbolsTruncated, trueSymbolCount } = prep;
@@ -6001,7 +6573,11 @@ function runSkeleton(opts) {
6001
6573
  const payload = {
6002
6574
  items: capped.items,
6003
6575
  truncated: capped.truncated || symbolsTruncated,
6004
- totalCount: symbolsTruncated ? Math.max(trueSymbolCount ?? 0, capped.totalCount) : capped.totalCount
6576
+ totalCount: symbolsTruncated ? Math.max(trueSymbolCount ?? 0, capped.totalCount) : capped.totalCount,
6577
+ // Envelope-level, not per row: this listing is one named file, so the fact belongs to the
6578
+ // whole payload. Only added when true, so live output keeps the exact three-key shape
6579
+ // tests/json_envelope_shape.test.ts pins.
6580
+ ...fileIsGone(resolved) ? { deleted: true } : {}
6005
6581
  };
6006
6582
  const text2 = JSON.stringify(payload, null, 2);
6007
6583
  recordReadStat("stub_view", fullSourceBytes, text2, opts.file);
@@ -6023,7 +6599,7 @@ function runOutline(opts) {
6023
6599
  const multiFiles = parseMultiFileSpec(opts.file);
6024
6600
  if (multiFiles !== null) return runPerFileListing(multiFiles, (file) => runOutline({ ...opts, file, includeFilePath: true }), opts.json === true);
6025
6601
  const prep = prepareSymbolListing(opts.file, opts);
6026
- if (prep.kind === "empty") {
6602
+ if (prep.kind === "confined" || prep.kind === "empty") {
6027
6603
  return { text: prep.text, code: 1 };
6028
6604
  }
6029
6605
  const { resolved, displayRoot, filtered, preFilterCount, refCounts, fullSourceBytes, symbolsTruncated, trueSymbolCount } = prep;
@@ -6042,7 +6618,9 @@ function runOutline(opts) {
6042
6618
  const payload = {
6043
6619
  items: capped.items,
6044
6620
  truncated: capped.truncated || symbolsTruncated,
6045
- totalCount: symbolsTruncated ? Math.max(trueSymbolCount ?? 0, capped.totalCount) : capped.totalCount
6621
+ totalCount: symbolsTruncated ? Math.max(trueSymbolCount ?? 0, capped.totalCount) : capped.totalCount,
6622
+ // Same envelope-level flag, and same reason, as runSkeleton's payload above.
6623
+ ...fileIsGone(resolved) ? { deleted: true } : {}
6046
6624
  };
6047
6625
  const text2 = JSON.stringify(payload, null, 2);
6048
6626
  recordReadStat("outline", fullSourceBytes, text2, opts.file);
@@ -6228,6 +6806,133 @@ function runYamlOutline(opts) {
6228
6806
  function runYamlQuery(opts) {
6229
6807
  return runQueryCommand(opts, parseYamlDocument, "YAML", "yaml-query", "yaml_query");
6230
6808
  }
6809
+ function runXmlOutline(opts) {
6810
+ const text = readFileText(opts.file);
6811
+ if (text === null) {
6812
+ emitErr(`Could not read: ${opts.file}`);
6813
+ return 1;
6814
+ }
6815
+ let summary;
6816
+ try {
6817
+ summary = outlineXml(text, { ...opts.maxDepth !== void 0 ? { maxDepth: opts.maxDepth } : {} });
6818
+ } catch (e) {
6819
+ emitErr(`Failed to parse XML: ${opts.file}
6820
+ ${extractErrorMessage(e)}`);
6821
+ return 1;
6822
+ }
6823
+ const fullSourceBytes = sumFileSizes([opts.file]);
6824
+ if (opts.json === true) {
6825
+ const jsonText = JSON.stringify(summary, null, 2);
6826
+ emit(jsonText);
6827
+ recordReadStat("xml_outline", fullSourceBytes, jsonText, opts.file);
6828
+ } else {
6829
+ const outlineText = formatXmlOutline(summary);
6830
+ emitGuarded(outlineText, "xml-outline");
6831
+ recordReadStat("xml_outline", fullSourceBytes, outlineText, opts.file);
6832
+ }
6833
+ return 0;
6834
+ }
6835
+ function runXmlQuery(opts) {
6836
+ const text = readFileText(opts.file);
6837
+ if (text === null) {
6838
+ emitErr(`Could not read: ${opts.file}`);
6839
+ return 1;
6840
+ }
6841
+ let head;
6842
+ try {
6843
+ head = opts.head !== void 0 ? requireNonNegativeStrictInt("--head", opts.head) : void 0;
6844
+ } catch (e) {
6845
+ emitErr(extractErrorMessage(e));
6846
+ return 1;
6847
+ }
6848
+ try {
6849
+ const result = queryXml(text, opts.path);
6850
+ const fullSourceBytes = sumFileSizes([opts.file]);
6851
+ if (result.attributeValues !== void 0) {
6852
+ if (result.attributeValues.length === 0) {
6853
+ if (opts.json === true) {
6854
+ const jsonText = JSON.stringify({ items: [], truncated: false, totalCount: 0 });
6855
+ emit(jsonText);
6856
+ recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
6857
+ } else {
6858
+ emit(`No attributes matched path: '${opts.path}'`);
6859
+ }
6860
+ return 0;
6861
+ }
6862
+ if (!result.fanned) {
6863
+ const val = result.attributeValues[0] ?? "";
6864
+ const outText = opts.json === true ? JSON.stringify(val) : val;
6865
+ emit(outText);
6866
+ recordReadStat("xml_query", fullSourceBytes, outText, opts.file);
6867
+ return 0;
6868
+ }
6869
+ const totalCount2 = result.attributeValues.length;
6870
+ const limited2 = head !== void 0 ? result.attributeValues.slice(0, head) : result.attributeValues;
6871
+ const headTruncated2 = limited2.length < totalCount2;
6872
+ if (opts.json === true) {
6873
+ const capped = guardJsonRows(limited2);
6874
+ const jsonText = JSON.stringify({ items: capped.items, truncated: capped.truncated || headTruncated2, totalCount: totalCount2 });
6875
+ emit(jsonText);
6876
+ recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
6877
+ } else {
6878
+ const lines = limited2.map((item) => item);
6879
+ if (headTruncated2) {
6880
+ lines.push(`...(${totalCount2 - limited2.length} more items elided; use --head to see more)`);
6881
+ }
6882
+ const plainText = lines.join("\n");
6883
+ emitGuarded(plainText, "xml-query");
6884
+ recordReadStat("xml_query", fullSourceBytes, plainText, opts.file);
6885
+ }
6886
+ return 0;
6887
+ }
6888
+ if (result.items.length === 0) {
6889
+ if (opts.json === true) {
6890
+ const jsonText = JSON.stringify({ items: [], truncated: false, totalCount: 0 });
6891
+ emit(jsonText);
6892
+ recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
6893
+ } else {
6894
+ emit(`No elements matched path: '${opts.path}'`);
6895
+ }
6896
+ return 0;
6897
+ }
6898
+ if (!result.fanned) {
6899
+ const node = result.items[0];
6900
+ if (opts.json === true) {
6901
+ const jsonVal = xmlNodeToJson(node);
6902
+ const jsonText = JSON.stringify(jsonVal, null, 2);
6903
+ emit(jsonText);
6904
+ recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
6905
+ } else {
6906
+ const xmlText = serializeXmlNode(node);
6907
+ emitGuarded(xmlText, "xml-query");
6908
+ recordReadStat("xml_query", fullSourceBytes, xmlText, opts.file);
6909
+ }
6910
+ return 0;
6911
+ }
6912
+ const totalCount = result.items.length;
6913
+ const limited = head !== void 0 ? result.items.slice(0, head) : result.items;
6914
+ const headTruncated = limited.length < totalCount;
6915
+ if (opts.json === true) {
6916
+ const jsonItems = limited.map(xmlNodeToJson);
6917
+ const capped = guardJsonRows(jsonItems);
6918
+ const jsonText = JSON.stringify({ items: capped.items, truncated: capped.truncated || headTruncated, totalCount });
6919
+ emit(jsonText);
6920
+ recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
6921
+ } else {
6922
+ const blocks = limited.map((node) => serializeXmlNode(node));
6923
+ if (headTruncated) {
6924
+ blocks.push(`...(${totalCount - limited.length} more elements elided; use --head to see more)`);
6925
+ }
6926
+ const plainText = blocks.join("\n");
6927
+ emitGuarded(plainText, "xml-query");
6928
+ recordReadStat("xml_query", fullSourceBytes, plainText, opts.file);
6929
+ }
6930
+ return 0;
6931
+ } catch (e) {
6932
+ emitErr(extractErrorMessage(e));
6933
+ return 1;
6934
+ }
6935
+ }
6231
6936
  function loadOpenApiOperations(file) {
6232
6937
  const text = readFileText(file);
6233
6938
  if (text === null) {
@@ -6646,6 +7351,7 @@ async function runScreenshot(url, destPath, opts) {
6646
7351
  }
6647
7352
  function runBriefCore(opts) {
6648
7353
  const resolution = resolveSymbolSpec(opts.spec, void 0, opts.projectRoot);
7354
+ if (resolution.kind === "confined") return { text: resolution.message, code: 1 };
6649
7355
  if (resolution.kind === "ambiguous") {
6650
7356
  return {
6651
7357
  // Name the command explicitly: formatAmbiguity defaults to 'read', so brief's retry lines would otherwise tell the user to run `token-goat read`, which answers a different question than the one they asked.
@@ -6819,8 +7525,10 @@ function runFind(opts) {
6819
7525
  fuzzyNames = rankSimilarNames(rawSymbols.map((s) => s.name), opts.pattern);
6820
7526
  }
6821
7527
  const matched = fuzzyNames.length > 0 ? fuzzyNames.flatMap((n) => rawSymbols.filter((s) => s.name === n)) : symbols;
6822
- const files = [...new Set(matched.map((s) => s.filePath))].slice(0, opts.limit ?? 50);
6823
- const truncated = rawSymbols.length === FIND_SCAN_LIMIT;
7528
+ const allFiles = [...new Set(matched.map((s) => s.filePath))];
7529
+ const files = allFiles.slice(0, opts.limit ?? 50);
7530
+ const limitDropped = allFiles.length - files.length;
7531
+ const truncated = rawSymbols.length === FIND_SCAN_LIMIT || limitDropped > 0;
6824
7532
  if (files.length === 0) {
6825
7533
  emitErr(`No indexed files match '${opts.pattern}'`);
6826
7534
  return 1;
@@ -6836,7 +7544,10 @@ function runFind(opts) {
6836
7544
  for (const f of files) {
6837
7545
  emit(toDisplayPath(rootDir, f));
6838
7546
  }
6839
- if (truncated) {
7547
+ if (limitDropped > 0) {
7548
+ emitErr(`Showing ${files.length} of ${allFiles.length} matching files; rerun with --limit ${allFiles.length} to see them all`);
7549
+ }
7550
+ if (rawSymbols.length === FIND_SCAN_LIMIT) {
6840
7551
  emitErr(`Results may be incomplete; index scan hit limit of ${FIND_SCAN_LIMIT} symbols`);
6841
7552
  }
6842
7553
  return 0;
@@ -7082,6 +7793,10 @@ function resolveSymbolSpecOrEmitError(commandName, spec, projectRoot) {
7082
7793
  return null;
7083
7794
  }
7084
7795
  const resolution = resolveSymbolSpec(spec, void 0, projectRoot);
7796
+ if (resolution.kind === "confined") {
7797
+ emitErr(resolution.message);
7798
+ return null;
7799
+ }
7085
7800
  if (resolution.kind === "ambiguous") {
7086
7801
  emitErr(
7087
7802
  formatAmbiguity(
@@ -7589,6 +8304,11 @@ function runPerFileEmitting(files, label, run) {
7589
8304
  function runExports(opts) {
7590
8305
  const multiFiles = parseMultiFileSpec(opts.file);
7591
8306
  if (multiFiles !== null) return runPerFileEmitting(multiFiles, "Exports", (file) => runExports({ ...opts, file }));
8307
+ const confined = fileConfinementRefusal("This file", opts.file, opts.projectRoot);
8308
+ if (confined !== null) {
8309
+ emitErr(confined);
8310
+ return 1;
8311
+ }
7592
8312
  const diskPath = resolveAgainstProjectRoot(opts.file, opts.projectRoot);
7593
8313
  const symbols = querySymbols({ filePath: resolveIndexPath(diskPath), limit: 500 });
7594
8314
  const kindOf = (name) => symbols.find((s) => s.name === name)?.kind ?? "export";
@@ -8612,29 +9332,49 @@ function hasAncestorDispatchRef(methodName, scopeName, classFile, rootDir) {
8612
9332
  }
8613
9333
  return false;
8614
9334
  }
8615
- var CORE_SYMBOL_KINDS = [
9335
+ var GENERIC_SYMBOL_KINDS = [
8616
9336
  "function",
8617
9337
  "method",
8618
9338
  "class",
8619
- "interface",
8620
- "enum",
8621
- "type",
8622
9339
  "variable",
8623
9340
  "const",
8624
- "struct",
8625
- "trait",
8626
9341
  "namespace",
8627
9342
  "module",
8628
9343
  "property",
8629
9344
  "field",
8630
9345
  "constructor",
8631
9346
  "macro",
8632
- "union",
8633
9347
  "alias",
8634
9348
  "directive",
8635
- "extension",
8636
- "mixin",
8637
- "protocol"
9349
+ "var",
9350
+ "impl"
9351
+ ];
9352
+ var OTHER_ADAPTER_SYMBOL_KINDS = [
9353
+ "heading",
9354
+ "selector",
9355
+ "env_key",
9356
+ "key",
9357
+ "section",
9358
+ "html_class",
9359
+ "html_id",
9360
+ "apex_method",
9361
+ "sf_record_type",
9362
+ "sf_apex_class",
9363
+ "sf_flow",
9364
+ "sf_flow_record_lookup",
9365
+ "sf_flow_action",
9366
+ "sf_flow_subflow",
9367
+ "sf_mystery_type",
9368
+ "sf_lightning_component_bundle",
9369
+ "sf_lwc_target",
9370
+ "sf_lwc_property",
9371
+ "liquid_schema",
9372
+ "liquid_section_file",
9373
+ "lwc_bundle",
9374
+ "lwc_component_alias",
9375
+ "lwc_api_property",
9376
+ "lwc_api_method",
9377
+ "lwc_ref"
8638
9378
  ];
8639
9379
  function runDead(opts) {
8640
9380
  if (opts.top !== void 0 && opts.top <= 0) {
@@ -8735,7 +9475,7 @@ var SOURCE_EXTENSIONS = [
8735
9475
  function runDeps(opts) {
8736
9476
  let text;
8737
9477
  try {
8738
- text = fs6.readFileSync(opts.file, "utf-8");
9478
+ text = decodeSource(fs6.readFileSync(opts.file));
8739
9479
  } catch {
8740
9480
  emitErr2(`Could not read: ${opts.file}`);
8741
9481
  return 1;
@@ -8884,6 +9624,9 @@ var TYPE_KINDS = [
8884
9624
  // Kotlin/Scala object.
8885
9625
  "graphql_extend"
8886
9626
  ];
9627
+ var CORE_SYMBOL_KINDS = [
9628
+ .../* @__PURE__ */ new Set([...GENERIC_SYMBOL_KINDS, ...TYPE_KINDS, ...OTHER_ADAPTER_SYMBOL_KINDS])
9629
+ ];
8887
9630
  var TYPES_EXCLUDE_TESTS_SCAN_LIMIT = 5e3;
8888
9631
  function runTypes(opts) {
8889
9632
  if (opts.limit !== void 0 && opts.limit <= 0) {
@@ -8991,6 +9734,11 @@ function runScope(opts) {
8991
9734
  emitErr2(`Invalid line number: ${lineStr}`);
8992
9735
  return 1;
8993
9736
  }
9737
+ const confined = fileConfinementRefusal("This file", file, void 0);
9738
+ if (confined !== null) {
9739
+ emitErr2(confined);
9740
+ return 1;
9741
+ }
8994
9742
  const filePath = resolveIndexPath(file);
8995
9743
  const syms = querySymbols({ filePath, limit: ALL_SYMBOLS_IN_FILE_LIMIT });
8996
9744
  const enclosing = syms.filter((s) => s.lineStart <= line && line <= s.lineEnd).sort((a, b) => b.lineStart - a.lineStart);
@@ -9600,6 +10348,8 @@ export {
9600
10348
  runJsonQuery,
9601
10349
  runYamlOutline,
9602
10350
  runYamlQuery,
10351
+ runXmlOutline,
10352
+ runXmlQuery,
9603
10353
  runOpenApiOutline,
9604
10354
  runOpenApiOp,
9605
10355
  runZipList,