@moldea.ai/core 7.0.4 → 8.0.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.
Files changed (57) hide show
  1. package/README.md +4 -4
  2. package/dist/analysis-worker.js +29 -11
  3. package/dist/analysis-worker.js.map +1 -1
  4. package/dist/chunks/{core-FyfprJqa.js → core-BCMf-ig9.js} +337 -63
  5. package/dist/chunks/core-BCMf-ig9.js.map +1 -0
  6. package/dist/chunks/{errors-Dw1ahMS1.js → errors-CvWn6gr2.js} +15 -5
  7. package/dist/chunks/errors-CvWn6gr2.js.map +1 -0
  8. package/dist/chunks/{lifecycle-h3DWn5vl.js → lifecycle-DwuevNoO.js} +2 -2
  9. package/dist/chunks/{lifecycle-h3DWn5vl.js.map → lifecycle-DwuevNoO.js.map} +1 -1
  10. package/dist/chunks/{options-AMqX7rx_.js → options-l3kVfA-K.js} +7 -3
  11. package/dist/chunks/options-l3kVfA-K.js.map +1 -0
  12. package/dist/chunks/{repository-rpc-ClAIx3-c.js → repository-rpc-DgYwMaGv.js} +29 -16
  13. package/dist/chunks/repository-rpc-DgYwMaGv.js.map +1 -0
  14. package/dist/chunks/{scope-e3-w9N7D.js → scope-D9BYchNI.js} +2 -2
  15. package/dist/chunks/{scope-e3-w9N7D.js.map → scope-D9BYchNI.js.map} +1 -1
  16. package/dist/constants/index.d.ts +1 -1
  17. package/dist/constants/index.d.ts.map +1 -1
  18. package/dist/contracts/index.d.ts +2 -1
  19. package/dist/contracts/index.d.ts.map +1 -1
  20. package/dist/diagnostic-utilities/index.d.ts.map +1 -1
  21. package/dist/diagnostics/index.d.ts +2 -2
  22. package/dist/diagnostics/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -2
  24. package/dist/integration-validation/agents-inspection.d.ts +19 -0
  25. package/dist/integration-validation/agents-inspection.d.ts.map +1 -0
  26. package/dist/integration-validation/claude-inspection.d.ts +22 -0
  27. package/dist/integration-validation/claude-inspection.d.ts.map +1 -0
  28. package/dist/integration-validation/inspection.d.ts +11 -3
  29. package/dist/integration-validation/inspection.d.ts.map +1 -1
  30. package/dist/integration-validation/parser.d.ts +8 -0
  31. package/dist/integration-validation/parser.d.ts.map +1 -1
  32. package/dist/integration-validation/types.d.ts +18 -0
  33. package/dist/integration-validation/types.d.ts.map +1 -1
  34. package/dist/integration-validation/utilities.d.ts +25 -0
  35. package/dist/integration-validation/utilities.d.ts.map +1 -0
  36. package/dist/node-inspection/client.d.ts.map +1 -1
  37. package/dist/node-inspection/protocol.d.ts.map +1 -1
  38. package/dist/node-inspection/repository-rpc.d.ts +7 -4
  39. package/dist/node-inspection/repository-rpc.d.ts.map +1 -1
  40. package/dist/node-inspection/types.d.ts +16 -0
  41. package/dist/node-inspection/types.d.ts.map +1 -1
  42. package/dist/node-runner.js +2 -2
  43. package/dist/node.js +31 -11
  44. package/dist/node.js.map +1 -1
  45. package/dist/project-validation/index.d.ts +1 -1
  46. package/dist/project-validation/index.d.ts.map +1 -1
  47. package/dist/repository-inspection-session/index.d.ts +8 -2
  48. package/dist/repository-inspection-session/index.d.ts.map +1 -1
  49. package/dist/retained-memory/index.d.ts +5 -4
  50. package/dist/retained-memory/index.d.ts.map +1 -1
  51. package/dist/scope.js +2 -2
  52. package/dist/universal-project-inspection/index.d.ts.map +1 -1
  53. package/package.json +2 -2
  54. package/dist/chunks/core-FyfprJqa.js.map +0 -1
  55. package/dist/chunks/errors-Dw1ahMS1.js.map +0 -1
  56. package/dist/chunks/options-AMqX7rx_.js.map +0 -1
  57. package/dist/chunks/repository-rpc-ClAIx3-c.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { E as parseDecisionIdFromPath, O as trimRepositoryFormatWhitespace, S as isRuntimeGuidancePath, T as isVariableId, a as escapeJsonPointerSegment, c as freezeRecursively, d as hasNonWhitespace, g as isContextPath, i as createCoreDiagnosticCollector, l as compareExactStrings, n as normalizeCoreOptions, o as normalizeDiagnostics, r as countDiagnosticsBySeverity, s as createNullPrototypeRecord, t as createCoreOperationOptionsSnapshot, u as countUnicodeScalars } from "./options-AMqX7rx_.js";
1
+ import { E as parseDecisionIdFromPath, O as trimRepositoryFormatWhitespace, S as isRuntimeGuidancePath, T as isVariableId, a as escapeJsonPointerSegment, c as freezeRecursively, d as hasNonWhitespace, g as isContextPath, i as createCoreDiagnosticCollector, l as compareExactStrings, n as normalizeCoreOptions, o as normalizeDiagnostics, r as countDiagnosticsBySeverity, s as createNullPrototypeRecord, t as createCoreOperationOptionsSnapshot, u as countUnicodeScalars } from "./options-l3kVfA-K.js";
2
2
  import { n as CoreOperationException } from "./exceptions-CYf1pCKf.js";
3
- import { a as createSourceLocator, c as calculateContentDigest, i as parseStrictYaml, l as calculateNormalizedTextDigest, n as inspectManifestDocument, o as collectManifestScopeDeclarations, r as parseManifestDocument, s as matchesRepositoryGlob, t as matchManifestScope, u as normalizeTextDocument } from "./scope-e3-w9N7D.js";
4
- import { REPOSITORY_ROOT, RepositorySourceException, compareRepositoryPaths, createRepositoryIdentity, isRepositoryPath, parseRepositoryPath } from "@moldea.ai/repository";
3
+ import { a as createSourceLocator, c as calculateContentDigest, i as parseStrictYaml, l as calculateNormalizedTextDigest, n as inspectManifestDocument, o as collectManifestScopeDeclarations, r as parseManifestDocument, s as matchesRepositoryGlob, t as matchManifestScope, u as normalizeTextDocument } from "./scope-D9BYchNI.js";
4
+ import { REPOSITORY_LINK_TEXT_MAX_BYTES, REPOSITORY_ROOT, RepositorySourceException, compareRepositoryPaths, createRepositoryIdentity, isRepositoryPath, parseRepositoryPath, resolveRepositoryLink } from "@moldea.ai/repository";
5
5
  //#region \0rolldown/runtime.js
6
6
  var __defProp = Object.defineProperty;
7
7
  var __exportAll = (all, no_symbols) => {
@@ -495,6 +495,12 @@ var reserveRetainedBytesIfTracked = (reader, bytes) => {
495
495
  //#endregion
496
496
  //#region src/repository-inspection-session/index.ts
497
497
  var MANIFEST_PATH$3 = parseRepositoryPath("/moldea/moldea.yaml");
498
+ var INSTRUCTION_PATHS = new Set([
499
+ "/AGENTS.md",
500
+ "/CLAUDE.md",
501
+ "/.claude/CLAUDE.md",
502
+ "/CLAUDE.local.md"
503
+ ].map((path) => parseRepositoryPath(path)));
498
504
  var SOURCE_PAGE_ENTRIES = 256;
499
505
  var SOURCE_PAGE_BYTES = 65536;
500
506
  var CANONICAL_BYTE_RETENTION_MULTIPLIER = 4;
@@ -552,11 +558,12 @@ var createSourceOptions = (sessionSignal, operationSignal) => {
552
558
  * @param repository The source-neutral reader bound to one coherent snapshot.
553
559
  * @param limits The immutable resource limits for the inspection.
554
560
  * @param signal Optional cancellation shared by every inspection operation.
561
+ * @param instructions Optional reader of the four instruction paths, used only by the instruction view.
555
562
  * @returns A session that pages source metadata and range-reads bounded canonical files.
556
563
  * @throws
557
564
  * - ABORTED: The Core operation was aborted.
558
565
  */
559
- var createRepositoryInspectionSession = (repository, limits, signal) => {
566
+ var createRepositoryInspectionSession = (repository, limits, signal, instructions) => {
560
567
  const seenPaths = /* @__PURE__ */ new Set();
561
568
  let canonicalBytes = 0;
562
569
  let peakRetainedBytes = 0;
@@ -593,14 +600,19 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
593
600
  if (byteLength > limits.maxTotalBytesRead - totalBytesRead) return throwResourceLimitExceeded("maxTotalBytesRead", limits.maxTotalBytesRead, totalBytesRead + byteLength);
594
601
  totalBytesRead += byteLength;
595
602
  };
596
- const getEntry = async (path, options) => {
603
+ const repositoryPages = {
604
+ readPage: (path, options) => repository.readFilePage(path, options),
605
+ snapshotId: repository.snapshot.id
606
+ };
607
+ const getEntryFrom = async (source, path, options) => {
597
608
  const parsedPath = parseRepositoryPath(path);
598
609
  throwIfSignalAborted(options?.signal);
599
610
  registerPath(parsedPath);
600
- const candidate = await repository.getEntry(parsedPath, createSourceOptions(signal, options?.signal));
611
+ const candidate = await source.getEntry(parsedPath, createSourceOptions(signal, options?.signal));
601
612
  throwIfSignalAborted(options?.signal);
602
613
  return candidate === null ? null : copyReaderEntry$1(candidate, "get-entry", parsedPath, parsedPath);
603
614
  };
615
+ const getEntry = (path, options) => getEntryFrom(repository, path, options);
604
616
  const iterateEntries = (options) => {
605
617
  const prefix = options?.prefix === void 0 ? REPOSITORY_ROOT : parseRepositoryPath(options.prefix);
606
618
  return { async *[Symbol.asyncIterator]() {
@@ -635,20 +647,20 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
635
647
  } while (true);
636
648
  } };
637
649
  };
638
- const loadFile = async (path, operationSignal, isSource = false) => {
650
+ const loadFile = async (path, operationSignal, isSource = false, pages = repositoryPages) => {
639
651
  const multiplier = isSource ? 1 : CANONICAL_BYTE_RETENTION_MULTIPLIER;
640
652
  const fileLimit = path === MANIFEST_PATH$3 ? "maxManifestBytes" : "maxFileBytes";
641
653
  const availableRetainedBytes = limits.maxRetainedBytes - retainedBytes;
642
654
  if (availableRetainedBytes < 1) return throwResourceLimitExceeded("maxRetainedBytes", limits.maxRetainedBytes, retainedBytes + 1);
643
655
  const firstPageMaximumBytes = Math.min(SOURCE_PAGE_BYTES, Math.max(1, limits[fileLimit]), availableRetainedBytes);
644
656
  observeTransientBytes(firstPageMaximumBytes);
645
- const firstPage = await repository.readFilePage(path, {
657
+ const firstPage = await pages.readPage(path, {
646
658
  maxBytes: firstPageMaximumBytes,
647
659
  offset: 0,
648
660
  ...createSourceOptions(signal, operationSignal) ?? {}
649
661
  });
650
662
  throwIfSignalAborted(operationSignal);
651
- if (firstPage.snapshot.id !== repository.snapshot.id || firstPage.offset !== 0 || !(firstPage.bytes instanceof Uint8Array) || !Number.isSafeInteger(firstPage.totalBytes) || firstPage.totalBytes < 0 || firstPage.bytes.byteLength > SOURCE_PAGE_BYTES || firstPage.bytes.byteLength > firstPage.totalBytes || firstPage.bytes.byteLength === 0 && !firstPage.isComplete) return invalidSourceData$3("read-file-page", path);
663
+ if (firstPage.snapshot.id !== pages.snapshotId || firstPage.offset !== 0 || !(firstPage.bytes instanceof Uint8Array) || !Number.isSafeInteger(firstPage.totalBytes) || firstPage.totalBytes < 0 || firstPage.bytes.byteLength > SOURCE_PAGE_BYTES || firstPage.bytes.byteLength > firstPage.totalBytes || firstPage.bytes.byteLength === 0 && !firstPage.isComplete) return invalidSourceData$3("read-file-page", path);
652
664
  if (firstPage.totalBytes > limits[fileLimit]) return throwResourceLimitExceeded(fileLimit, limits[fileLimit], firstPage.totalBytes);
653
665
  const firstPageBytes = firstPage.bytes.byteLength;
654
666
  const retainedBytesAvailableForCanonicalContent = limits.maxRetainedBytes - retainedBytes - firstPageBytes;
@@ -667,7 +679,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
667
679
  let isFirstPage = true;
668
680
  while (isFirstPage || offset < content.byteLength) {
669
681
  isFirstPage = false;
670
- if (page.snapshot.id !== repository.snapshot.id || page.offset !== offset || page.totalBytes !== content.byteLength || !(page.bytes instanceof Uint8Array) || page.bytes.byteLength > SOURCE_PAGE_BYTES || page.bytes.byteLength > content.byteLength - offset || page.bytes.byteLength === 0 && !page.isComplete) return invalidSourceData$3("read-file-page", path);
682
+ if (page.snapshot.id !== pages.snapshotId || page.offset !== offset || page.totalBytes !== content.byteLength || !(page.bytes instanceof Uint8Array) || page.bytes.byteLength > SOURCE_PAGE_BYTES || page.bytes.byteLength > content.byteLength - offset || page.bytes.byteLength === 0 && !page.isComplete) return invalidSourceData$3("read-file-page", path);
671
683
  content.set(page.bytes, offset);
672
684
  offset += page.bytes.byteLength;
673
685
  if (page.isComplete) {
@@ -675,7 +687,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
675
687
  break;
676
688
  }
677
689
  if (page.nextOffset !== offset) return invalidSourceData$3("read-file-page", path);
678
- page = await repository.readFilePage(path, {
690
+ page = await pages.readPage(path, {
679
691
  maxBytes: Math.min(SOURCE_PAGE_BYTES, Math.max(1, content.byteLength - offset)),
680
692
  offset,
681
693
  ...createSourceOptions(signal, operationSignal) ?? {}
@@ -718,6 +730,50 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
718
730
  throwIfSignalAborted(options?.signal);
719
731
  return content.slice();
720
732
  };
733
+ /** Loads one complete temporary source buffer whose reservation the caller releases. */
734
+ const readRetainedSource = async (path, pages) => {
735
+ throwIfSignalAborted();
736
+ registerPath(path);
737
+ const bytes = await awaitWithCancellation(loadFile(path, void 0, true, pages));
738
+ throwIfSignalAborted();
739
+ let isReleased = false;
740
+ return {
741
+ bytes,
742
+ release: () => {
743
+ if (!isReleased) retainedBytes -= bytes.byteLength;
744
+ isReleased = true;
745
+ }
746
+ };
747
+ };
748
+ const readerFor = (path) => instructions !== void 0 && INSTRUCTION_PATHS.has(path) ? instructions : repository;
749
+ const pagesFor = (path) => {
750
+ const source = readerFor(path);
751
+ return source === repository ? repositoryPages : {
752
+ readPage: (pagePath, options) => source.readFilePage(pagePath, options),
753
+ snapshotId: source.snapshot.id
754
+ };
755
+ };
756
+ /** Reads one link's stored text from the routed source, rejecting text past the shared limit. */
757
+ const readLinkText = async (path, options) => {
758
+ const parsedPath = parseRepositoryPath(path);
759
+ throwIfSignalAborted(options?.signal);
760
+ registerPath(parsedPath);
761
+ const text = await awaitWithCancellation(readerFor(parsedPath).readLinkText(parsedPath, createSourceOptions(signal, options?.signal)), options?.signal);
762
+ throwIfSignalAborted(options?.signal);
763
+ if (text !== null && (typeof text !== "string" || encoder$1.encode(text).byteLength > REPOSITORY_LINK_TEXT_MAX_BYTES)) return invalidSourceData$3("read-link-text", parsedPath);
764
+ return text;
765
+ };
766
+ const instructionView = Object.freeze({
767
+ getEntry: (path, options) => {
768
+ const parsedPath = parseRepositoryPath(path);
769
+ return getEntryFrom(readerFor(parsedPath), parsedPath, options);
770
+ },
771
+ readLinkText,
772
+ readSourceFile: (path) => {
773
+ const parsedPath = parseRepositoryPath(path);
774
+ return readRetainedSource(parsedPath, pagesFor(parsedPath));
775
+ }
776
+ });
721
777
  const reader = Object.freeze({
722
778
  getEntry,
723
779
  iterateEntries,
@@ -734,20 +790,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
734
790
  if (!Number.isSafeInteger(bytes) || bytes < 0 || bytes > retainedBytes) throw new TypeError("The retained-memory release is invalid.");
735
791
  retainedBytes -= bytes;
736
792
  },
737
- readSourceFile: async (path) => {
738
- throwIfSignalAborted();
739
- registerPath(path);
740
- const bytes = await awaitWithCancellation(loadFile(path, void 0, true));
741
- throwIfSignalAborted();
742
- let isReleased = false;
743
- return {
744
- bytes,
745
- release: () => {
746
- if (!isReleased) retainedBytes -= bytes.byteLength;
747
- isReleased = true;
748
- }
749
- };
750
- }
793
+ readSourceFile: (path) => readRetainedSource(path, repositoryPages)
751
794
  });
752
795
  return Object.freeze({
753
796
  getResourceUsage: () => Object.freeze({
@@ -756,6 +799,7 @@ var createRepositoryInspectionSession = (repository, limits, signal) => {
756
799
  retainedBytes,
757
800
  totalBytesRead
758
801
  }),
802
+ instructions: instructionView,
759
803
  reader,
760
804
  throwIfAborted: throwIfSignalAborted
761
805
  });
@@ -1879,11 +1923,34 @@ var parseIntegrationBlock = (text) => {
1879
1923
  skillPath
1880
1924
  };
1881
1925
  };
1926
+ var ROOT_AGENTS_IMPORTS = /* @__PURE__ */ new Set(["@AGENTS.md", "@./AGENTS.md"]);
1927
+ var NESTED_AGENTS_IMPORTS = /* @__PURE__ */ new Set(["@../AGENTS.md"]);
1928
+ /**
1929
+ * Reports whether Claude Code instruction text imports the root AGENTS.md on a line of its own.
1930
+ * Lines inside fenced code blocks and imports embedded in other text do not count.
1931
+ * @param text The decoded instruction text.
1932
+ * @param isNested Whether the text belongs to `/.claude/CLAUDE.md` rather than a root file.
1933
+ * @returns `true` when one unfenced line, trimmed of spaces and tabs, is an AGENTS.md import.
1934
+ */
1935
+ var hasAgentsImport = (text, isNested) => {
1936
+ const imports = isNested ? NESTED_AGENTS_IMPORTS : ROOT_AGENTS_IMPORTS;
1937
+ let fence = null;
1938
+ for (const rawLine of text.replace(/^\uFEFF/u, "").split(/\r\n|\n|\r/u)) {
1939
+ const line = rawLine.replace(/^[ \t]+|[ \t]+$/gu, "");
1940
+ const marker = /^(`{3,}|~{3,})/u.exec(line)?.[1];
1941
+ if (fence !== null) {
1942
+ if (marker !== void 0 && marker[0] === fence[0] && marker.length >= fence.length) fence = null;
1943
+ } else if (marker !== void 0) fence = marker;
1944
+ else if (imports.has(line)) return true;
1945
+ }
1946
+ return false;
1947
+ };
1882
1948
  //#endregion
1883
- //#region src/integration-validation/inspection.ts
1884
- var AGENTS_PATH = parseRepositoryPath("/AGENTS.md");
1949
+ //#region src/integration-validation/utilities.ts
1885
1950
  /**
1886
1951
  * Reads bounded untrusted text with a reservation for decoding and temporary string copies.
1952
+ * @param ledger The session ledger that owns the reservation.
1953
+ * @param load Loads the complete bounded source buffer.
1887
1954
  * @returns Decoded text or null for invalid UTF-8, with its reservation-release operation.
1888
1955
  * @throws
1889
1956
  * - ENTRY_NOT_FOUND: The requested repository entry was not found.
@@ -1897,8 +1964,8 @@ var AGENTS_PATH = parseRepositoryPath("/AGENTS.md");
1897
1964
  * - SNAPSHOT_CHANGED: The repository snapshot changed during the operation.
1898
1965
  * - INVALID_SOURCE_DATA: The repository source returned invalid data.
1899
1966
  */
1900
- var readText = async (ledger, path) => {
1901
- const source = await ledger.readSourceFile(path);
1967
+ var readRetainedText = async (ledger, load) => {
1968
+ const source = await load();
1902
1969
  const reservation = source.bytes.byteLength * 8;
1903
1970
  let isReserved = false;
1904
1971
  try {
@@ -1923,9 +1990,17 @@ var readText = async (ledger, path) => {
1923
1990
  };
1924
1991
  /** Distinguishes missing and non-regular files without an extra entry lookup or following symlinks. */
1925
1992
  var getAbsentKind = (error) => error instanceof RepositorySourceException ? error.code === "ENTRY_NOT_FOUND" ? "missing" : error.code === "ENTRY_NOT_FILE" ? "non-regular" : null : null;
1993
+ //#endregion
1994
+ //#region src/integration-validation/agents-inspection.ts
1995
+ var AGENTS_PATH$1 = parseRepositoryPath("/AGENTS.md");
1996
+ /** Reports an AGENTS.md link that doesn't lead to a file, with its reason as the qualifier. */
1997
+ var reportUnresolvedLink = (report, reason) => report("MOLDEA_INTEGRATION_LINK_UNRESOLVED", {
1998
+ entryKind: "symlink",
1999
+ reason
2000
+ }, reason);
1926
2001
  /**
1927
- * Checks the root integration against its installed asset with at most two reads and two lookups.
1928
- * @returns Completed content-free integration warnings, never an execution or runtime-wiring claim.
2002
+ * Checks one readable AGENTS.md text against its referenced skill installation.
2003
+ * @returns A promise that settles after at most one integration warning was recorded.
1929
2004
  * @throws
1930
2005
  * - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
1931
2006
  * - RESOURCE_LIMIT_EXCEEDED: A named repository resource limit was exceeded.
@@ -1936,52 +2011,40 @@ var getAbsentKind = (error) => error instanceof RepositorySourceException ? erro
1936
2011
  * - SNAPSHOT_CHANGED: The repository snapshot changed during the operation.
1937
2012
  * - INVALID_SOURCE_DATA: The repository source returned invalid data.
1938
2013
  */
1939
- var inspectCodingAgentIntegration = async (reader, limits, signal) => {
1940
- const ledger = getRetainedMemory(reader);
1941
- const collector = createCoreDiagnosticCollector(limits, "validate-project");
1942
- const report = (code, details) => {
1943
- collector.add({
1944
- code,
1945
- path: AGENTS_PATH,
1946
- subject: AGENTS_PATH,
1947
- qualifier: null,
1948
- entity: { integrationPath: AGENTS_PATH },
1949
- details
1950
- });
1951
- return collector.finalize();
1952
- };
2014
+ var checkAgentsDocument = async (context, report, load, entryDetails) => {
2015
+ const { ledger, reader, signal } = context;
1953
2016
  let document;
1954
2017
  try {
1955
- document = await readText(ledger, AGENTS_PATH);
2018
+ document = await readRetainedText(ledger, load);
1956
2019
  } catch (error) {
1957
- const entryKind = getAbsentKind(error);
1958
- if (entryKind !== null) return report("MOLDEA_INTEGRATION_BLOCK_MISSING", { entryKind });
1959
- throw error;
2020
+ const absentKind = getAbsentKind(error);
2021
+ if (absentKind === null) throw error;
2022
+ return entryDetails["entryKind"] === "symlink" ? reportUnresolvedLink(report, absentKind === "missing" ? "missing" : "not-file") : report("MOLDEA_INTEGRATION_BLOCK_MISSING", { entryKind: absentKind });
1960
2023
  }
1961
2024
  try {
1962
2025
  if (document.text === null) return report("MOLDEA_INTEGRATION_BLOCK_MISSING", {
1963
- entryKind: "file",
2026
+ ...entryDetails,
1964
2027
  reason: "invalid-utf8"
1965
2028
  });
1966
2029
  const parsed = parseIntegrationBlock(document.text);
1967
2030
  if (parsed.kind === "missing") return report(parsed.reason.startsWith("block-") ? "MOLDEA_INTEGRATION_BLOCK_MISSING" : "MOLDEA_INTEGRATION_SKILL_MISSING", {
1968
- entryKind: "file",
2031
+ ...entryDetails,
1969
2032
  reason: parsed.reason
1970
2033
  });
1971
2034
  const { skillPath } = parsed;
1972
2035
  for (const file of ["SKILL.md", "scripts/relevance-gate.mjs"]) {
1973
2036
  const path = parseRepositoryPath(`/${skillPath}/${file}`);
1974
- const entry = await reader.getEntry(path, signal === void 0 ? void 0 : { signal });
1975
- if (entry?.type !== "file") return report("MOLDEA_INTEGRATION_SKILL_MISSING", {
2037
+ const skillEntry = await reader.getEntry(path, signal === void 0 ? void 0 : { signal });
2038
+ if (skillEntry?.type !== "file") return report("MOLDEA_INTEGRATION_SKILL_MISSING", {
1976
2039
  referencedPath: `/${skillPath}`,
1977
2040
  missingFile: path,
1978
- entryKind: entry?.type ?? "missing"
2041
+ entryKind: skillEntry?.type ?? "missing"
1979
2042
  });
1980
2043
  }
1981
2044
  const assetPath = parseRepositoryPath(`/${skillPath}/assets/managed-agents-block.md`);
1982
2045
  let asset;
1983
2046
  try {
1984
- asset = await readText(ledger, assetPath);
2047
+ asset = await readRetainedText(ledger, () => ledger.readSourceFile(assetPath));
1985
2048
  } catch (error) {
1986
2049
  const entryKind = getAbsentKind(error);
1987
2050
  if (entryKind !== null) return report("MOLDEA_INTEGRATION_SKILL_MISSING", {
@@ -2011,7 +2074,6 @@ var inspectCodingAgentIntegration = async (reader, limits, signal) => {
2011
2074
  } finally {
2012
2075
  ledger.release(renderedReservation);
2013
2076
  }
2014
- return collector.finalize();
2015
2077
  } finally {
2016
2078
  asset.release();
2017
2079
  }
@@ -2019,6 +2081,214 @@ var inspectCodingAgentIntegration = async (reader, limits, signal) => {
2019
2081
  document.release();
2020
2082
  }
2021
2083
  };
2084
+ /**
2085
+ * Checks root AGENTS.md against its referenced skill installation. A symlinked AGENTS.md that
2086
+ * leads to a regular file in the repository is checked on that file's text; any other link is
2087
+ * reported with its reason, without reading anything outside the repository.
2088
+ * @param context The instruction view, repository reader, ledger, and warning recorder.
2089
+ * @returns What AGENTS.md is, including its link resolution, for the Claude Code check.
2090
+ * @throws
2091
+ * - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
2092
+ * - RESOURCE_LIMIT_EXCEEDED: A named repository resource limit was exceeded.
2093
+ * - ABORTED: The Core operation was aborted.
2094
+ * - ABORTED: The repository operation was aborted.
2095
+ * - ACCESS_DENIED: Access to the repository source was denied.
2096
+ * - SOURCE_UNAVAILABLE: The repository source is unavailable.
2097
+ * - SNAPSHOT_CHANGED: The repository snapshot changed during the operation.
2098
+ * - INVALID_SOURCE_DATA: The repository source returned invalid data.
2099
+ */
2100
+ var inspectAgentsIntegration = async (context) => {
2101
+ const { instructions } = context;
2102
+ const report = (code, details, qualifier) => context.report(code, AGENTS_PATH$1, details, qualifier);
2103
+ const entry = await instructions.getEntry(AGENTS_PATH$1);
2104
+ if (entry === null) {
2105
+ report("MOLDEA_INTEGRATION_BLOCK_MISSING", { entryKind: "missing" });
2106
+ return { kind: "missing" };
2107
+ }
2108
+ if (entry.type === "directory") {
2109
+ report("MOLDEA_INTEGRATION_BLOCK_MISSING", { entryKind: "directory" });
2110
+ return { kind: "directory" };
2111
+ }
2112
+ if (entry.type === "file") {
2113
+ await checkAgentsDocument(context, report, () => instructions.readSourceFile(AGENTS_PATH$1), { entryKind: "file" });
2114
+ return { kind: "file" };
2115
+ }
2116
+ const resolution = await resolveRepositoryLink(instructions, AGENTS_PATH$1);
2117
+ if (resolution.state === "file") {
2118
+ const { targetPath } = resolution;
2119
+ await checkAgentsDocument(context, report, () => instructions.readSourceFile(targetPath), { entryKind: "symlink" });
2120
+ } else reportUnresolvedLink(report, resolution.state);
2121
+ return {
2122
+ kind: "link",
2123
+ resolution
2124
+ };
2125
+ };
2126
+ //#endregion
2127
+ //#region src/integration-validation/claude-inspection.ts
2128
+ var AGENTS_PATH = parseRepositoryPath("/AGENTS.md");
2129
+ var CLAUDE_PATH = parseRepositoryPath("/CLAUDE.md");
2130
+ var NESTED_CLAUDE_PATH = parseRepositoryPath("/.claude/CLAUDE.md");
2131
+ var LOCAL_CLAUDE_PATH = parseRepositoryPath("/CLAUDE.local.md");
2132
+ /** Returns the regular file AGENTS.md leads to, or null when it leads to none. */
2133
+ var getAgentsTargetPath = (agents) => {
2134
+ if (agents.kind === "file") return AGENTS_PATH;
2135
+ return agents.kind === "link" && agents.resolution.state === "file" ? agents.resolution.targetPath : null;
2136
+ };
2137
+ /** Recognizes a link between a Claude file and AGENTS.md by final target path, without reading. */
2138
+ var isLinkedToAgents = (path, resolution, agents) => {
2139
+ const agentsTargetPath = getAgentsTargetPath(agents);
2140
+ if (agents.kind === "link" && agentsTargetPath === path) return true;
2141
+ return resolution?.state === "file" && resolution.targetPath === agentsTargetPath;
2142
+ };
2143
+ /**
2144
+ * Determines whether one Claude Code file loads AGENTS.md through an import line, a link to the
2145
+ * same final target, or AGENTS.md linking to it. A linked Claude file with a regular-file target
2146
+ * is checked for an import line as if the file were at the link's own location.
2147
+ * @returns The assessment, reading at most the file or its link target once.
2148
+ * @throws
2149
+ * - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
2150
+ * - RESOURCE_LIMIT_EXCEEDED: A named repository resource limit was exceeded.
2151
+ * - ABORTED: The Core operation was aborted.
2152
+ * - ABORTED: The repository operation was aborted.
2153
+ * - ACCESS_DENIED: Access to the repository source was denied.
2154
+ * - SOURCE_UNAVAILABLE: The repository source is unavailable.
2155
+ * - SNAPSHOT_CHANGED: The repository snapshot changed during the operation.
2156
+ * - INVALID_SOURCE_DATA: The repository source returned invalid data.
2157
+ */
2158
+ var assessClaudeFile = async (context, path, agents) => {
2159
+ const { instructions, ledger } = context;
2160
+ const entry = await instructions.getEntry(path);
2161
+ if (entry === null) return { kind: "absent" };
2162
+ if (isLinkedToAgents(path, null, agents)) return { kind: "loads" };
2163
+ if (entry.type === "directory") return {
2164
+ entryKind: "directory",
2165
+ kind: "not-loading"
2166
+ };
2167
+ let load = () => instructions.readSourceFile(path);
2168
+ if (entry.type === "symlink") {
2169
+ const resolution = await resolveRepositoryLink(instructions, path);
2170
+ if (isLinkedToAgents(path, resolution, agents)) return { kind: "loads" };
2171
+ if (resolution.state !== "file") return {
2172
+ kind: "unresolved",
2173
+ reason: resolution.state
2174
+ };
2175
+ const { targetPath } = resolution;
2176
+ load = () => instructions.readSourceFile(targetPath);
2177
+ }
2178
+ let document;
2179
+ try {
2180
+ document = await readRetainedText(ledger, load);
2181
+ } catch (error) {
2182
+ const absentKind = getAbsentKind(error);
2183
+ if (absentKind === null) throw error;
2184
+ if (entry.type !== "symlink") return { kind: "absent" };
2185
+ return {
2186
+ kind: "unresolved",
2187
+ reason: absentKind === "missing" ? "missing" : "not-file"
2188
+ };
2189
+ }
2190
+ try {
2191
+ return document.text !== null && hasAgentsImport(document.text, path === NESTED_CLAUDE_PATH) ? { kind: "loads" } : {
2192
+ entryKind: entry.type,
2193
+ kind: "not-loading"
2194
+ };
2195
+ } finally {
2196
+ document.release();
2197
+ }
2198
+ };
2199
+ /**
2200
+ * Checks whether Claude Code loads AGENTS.md. When `/CLAUDE.md` or `/.claude/CLAUDE.md` exists,
2201
+ * only those shared files count; `/CLAUDE.local.md` counts only when neither exists. At most one
2202
+ * Claude warning is recorded: an unresolved link for the first counted file whose link doesn't
2203
+ * lead to a file in the repository when none loads AGENTS.md, otherwise a missing import for the
2204
+ * governing file.
2205
+ * @param context The instruction view, ledger, and warning recorder.
2206
+ * @param agents What root AGENTS.md is, as the AGENTS.md check observed it.
2207
+ * @returns A promise that settles after at most one Claude warning was recorded.
2208
+ * @throws
2209
+ * - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
2210
+ * - RESOURCE_LIMIT_EXCEEDED: A named repository resource limit was exceeded.
2211
+ * - ABORTED: The Core operation was aborted.
2212
+ * - ABORTED: The repository operation was aborted.
2213
+ * - ACCESS_DENIED: Access to the repository source was denied.
2214
+ * - SOURCE_UNAVAILABLE: The repository source is unavailable.
2215
+ * - SNAPSHOT_CHANGED: The repository snapshot changed during the operation.
2216
+ * - INVALID_SOURCE_DATA: The repository source returned invalid data.
2217
+ */
2218
+ var inspectClaudeIntegration = async (context, agents) => {
2219
+ const counted = [];
2220
+ for (const path of [CLAUDE_PATH, NESTED_CLAUDE_PATH]) {
2221
+ const assessment = await assessClaudeFile(context, path, agents);
2222
+ if (assessment.kind === "loads") return;
2223
+ if (assessment.kind !== "absent") counted.push({
2224
+ assessment,
2225
+ path
2226
+ });
2227
+ }
2228
+ if (counted.length === 0) {
2229
+ const assessment = await assessClaudeFile(context, LOCAL_CLAUDE_PATH, agents);
2230
+ if (assessment.kind === "absent" || assessment.kind === "loads") return;
2231
+ counted.push({
2232
+ assessment,
2233
+ path: LOCAL_CLAUDE_PATH
2234
+ });
2235
+ }
2236
+ const unresolved = counted.find(({ assessment }) => assessment.kind === "unresolved");
2237
+ if (unresolved?.assessment.kind === "unresolved") {
2238
+ const { reason } = unresolved.assessment;
2239
+ context.report("MOLDEA_INTEGRATION_LINK_UNRESOLVED", unresolved.path, {
2240
+ entryKind: "symlink",
2241
+ reason
2242
+ }, reason);
2243
+ return;
2244
+ }
2245
+ const governing = counted[0];
2246
+ if (governing?.assessment.kind === "not-loading") context.report("MOLDEA_INTEGRATION_CLAUDE_IMPORT_MISSING", governing.path, { entryKind: governing.assessment.entryKind });
2247
+ };
2248
+ //#endregion
2249
+ //#region src/integration-validation/inspection.ts
2250
+ /**
2251
+ * Checks the coding-agent integration: root AGENTS.md and the Claude Code instruction files
2252
+ * through the instruction view, and the referenced skill installation through the repository
2253
+ * reader. It reads at most four instruction files, follows at most 32 links for each with one
2254
+ * lookup and one link-text read per link, and makes at most five bounded reads, all inside the
2255
+ * repository.
2256
+ * @param reader The repository inspection reader for skill files and the managed asset.
2257
+ * @param instructions The instruction view that routes the four instruction paths and links.
2258
+ * @param limits The immutable resource limits for the inspection.
2259
+ * @param signal Optional cancellation for repository lookups.
2260
+ * @returns Completed content-free integration warnings, never an execution or runtime-wiring claim.
2261
+ * @throws
2262
+ * - RESOURCE_LIMIT_EXCEEDED: A Core resource limit was exceeded.
2263
+ * - RESOURCE_LIMIT_EXCEEDED: A named repository resource limit was exceeded.
2264
+ * - ABORTED: The Core operation was aborted.
2265
+ * - ABORTED: The repository operation was aborted.
2266
+ * - ACCESS_DENIED: Access to the repository source was denied.
2267
+ * - SOURCE_UNAVAILABLE: The repository source is unavailable.
2268
+ * - SNAPSHOT_CHANGED: The repository snapshot changed during the operation.
2269
+ * - INVALID_SOURCE_DATA: The repository source returned invalid data.
2270
+ */
2271
+ var inspectCodingAgentIntegration = async (reader, instructions, limits, signal) => {
2272
+ const collector = createCoreDiagnosticCollector(limits, "validate-project");
2273
+ const context = {
2274
+ instructions,
2275
+ ledger: getRetainedMemory(reader),
2276
+ reader,
2277
+ report: (code, subject, details, qualifier) => {
2278
+ collector.add({
2279
+ code,
2280
+ path: subject,
2281
+ subject,
2282
+ qualifier: qualifier ?? null,
2283
+ entity: { integrationPath: subject },
2284
+ details
2285
+ });
2286
+ },
2287
+ ...signal === void 0 ? {} : { signal }
2288
+ };
2289
+ await inspectClaudeIntegration(context, await inspectAgentsIntegration(context));
2290
+ return collector.finalize();
2291
+ };
2022
2292
  //#endregion
2023
2293
  //#region src/impact-pattern-validation/inspection.ts
2024
2294
  /** Identifies the canonical document owning an impact relationship. */
@@ -2899,7 +3169,7 @@ var inspectUniversalProject = async (context, options) => {
2899
3169
  addDiagnostics(collector, mirrorInspection.diagnostics);
2900
3170
  }
2901
3171
  session.throwIfAborted();
2902
- addDiagnostics(collector, await inspectCodingAgentIntegration(session.reader, options.limits, signal));
3172
+ addDiagnostics(collector, await inspectCodingAgentIntegration(session.reader, session.instructions, options.limits, signal));
2903
3173
  const patterns = indexedManifest === null ? {
2904
3174
  completed: false,
2905
3175
  diagnostics: []
@@ -2909,7 +3179,9 @@ var inspectUniversalProject = async (context, options) => {
2909
3179
  const diagnostics = applyWarningAcceptances(collector.finalize(), indexedManifest?.value.acceptedWarnings ?? [], /* @__PURE__ */ new Set([...[
2910
3180
  "MOLDEA_INTEGRATION_BLOCK_MISSING",
2911
3181
  "MOLDEA_INTEGRATION_SKILL_MISSING",
2912
- "MOLDEA_INTEGRATION_BLOCK_STALE"
3182
+ "MOLDEA_INTEGRATION_BLOCK_STALE",
3183
+ "MOLDEA_INTEGRATION_LINK_UNRESOLVED",
3184
+ "MOLDEA_INTEGRATION_CLAUDE_IMPORT_MISSING"
2913
3185
  ], ...patterns.completed ? ["MOLDEA_IMPACT_PATTERN_UNMATCHED"] : []]), options.limits);
2914
3186
  const project = !diagnostics.some((diagnostic) => diagnostic.severity === "error") && indexedManifest !== null && projectAssets.project !== null ? createProjectIndex({
2915
3187
  agentMirrors,
@@ -2939,7 +3211,7 @@ var invalidArgument$1 = () => {
2939
3211
  });
2940
3212
  };
2941
3213
  var isRepositoryReader = (candidate) => {
2942
- return isRecord(candidate) && isRecord(candidate["snapshot"]) && typeof candidate["snapshot"]["id"] === "string" && typeof candidate["snapshot"]["sourceKind"] === "string" && typeof candidate["compare"] === "function" && typeof candidate["getEntry"] === "function" && typeof candidate["listEntriesPage"] === "function" && typeof candidate["readFilePage"] === "function";
3214
+ return isRecord(candidate) && isRecord(candidate["snapshot"]) && typeof candidate["snapshot"]["id"] === "string" && typeof candidate["snapshot"]["sourceKind"] === "string" && typeof candidate["compare"] === "function" && typeof candidate["getEntry"] === "function" && typeof candidate["listEntriesPage"] === "function" && typeof candidate["readFilePage"] === "function" && typeof candidate["readLinkText"] === "function";
2943
3215
  };
2944
3216
  var isAbortSignal = (candidate) => {
2945
3217
  return isRecord(candidate) && typeof candidate["aborted"] === "boolean" && typeof candidate["addEventListener"] === "function" && typeof candidate["removeEventListener"] === "function";
@@ -2948,9 +3220,11 @@ var validateInput = (candidate) => {
2948
3220
  try {
2949
3221
  if (!isRecord(candidate)) return invalidArgument$1();
2950
3222
  const repository = candidate["repository"];
3223
+ const instructions = candidate["instructions"];
2951
3224
  const signal = candidate["signal"];
2952
- if (Reflect.ownKeys(candidate).some((key) => key !== "repository" && key !== "signal") || !isRepositoryReader(repository) || signal !== void 0 && !isAbortSignal(signal)) return invalidArgument$1();
3225
+ if (Reflect.ownKeys(candidate).some((key) => key !== "repository" && key !== "instructions" && key !== "signal") || !isRepositoryReader(repository) || instructions !== void 0 && !isRepositoryReader(instructions) || signal !== void 0 && !isAbortSignal(signal)) return invalidArgument$1();
2953
3226
  return {
3227
+ ...instructions === void 0 ? {} : { instructions },
2954
3228
  repository,
2955
3229
  ...signal === void 0 ? {} : { signal }
2956
3230
  };
@@ -2961,7 +3235,7 @@ var validateInput = (candidate) => {
2961
3235
  };
2962
3236
  /**
2963
3237
  * Inspects one coherent repository snapshot through universal file and export validation.
2964
- * @param input The untrusted source-neutral reader and optional cancellation signal.
3238
+ * @param input The untrusted source-neutral reader, optional instruction reader, and signal.
2965
3239
  * @param options The immutable Core configuration snapshot.
2966
3240
  * @returns A promise resolving to the frozen all-or-nothing project inspection result.
2967
3241
  * @throws
@@ -2983,7 +3257,7 @@ var validateInput = (candidate) => {
2983
3257
  var validateProjectState = async (input, options) => {
2984
3258
  const validatedInput = validateInput(input);
2985
3259
  const operationOptions = createCoreOperationOptionsSnapshot(options);
2986
- const session = createRepositoryInspectionSession(validatedInput.repository, operationOptions.limits, validatedInput.signal);
3260
+ const session = createRepositoryInspectionSession(validatedInput.repository, operationOptions.limits, validatedInput.signal, validatedInput.instructions);
2987
3261
  const universal = await inspectUniversalProject({
2988
3262
  session,
2989
3263
  ...validatedInput.signal === void 0 ? {} : { signal: validatedInput.signal }
@@ -3363,4 +3637,4 @@ var createCore = (options) => {
3363
3637
  //#endregion
3364
3638
  export { createCore as n, parseIntegrationBlock as r, core_exports as t };
3365
3639
 
3366
- //# sourceMappingURL=core-FyfprJqa.js.map
3640
+ //# sourceMappingURL=core-BCMf-ig9.js.map