archgraph-argo 0.26.1 → 0.27.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.
@@ -6,7 +6,6 @@ const readline = require('node:readline');
6
6
  const crypto = require('node:crypto');
7
7
 
8
8
  const {
9
- getArgoRoot,
10
9
  resolveCallWorkspaceRoot,
11
10
  } = require('./argo-paths.js');
12
11
 
@@ -132,6 +131,11 @@ const {
132
131
  validateArchiMateEndpointMatrix,
133
132
  validateViewElementLimits,
134
133
  } = require('./graph-semantics.js');
134
+ const {
135
+ loadSchemaBundleAndOntology,
136
+ resolveSchemaBundle,
137
+ resolveTypeEnums,
138
+ } = require('./argob-schema.js');
135
139
  const {
136
140
  createProductionGraphRagRuntime,
137
141
  } = require('./graph-rag/productionGraphRagRuntime.js');
@@ -168,6 +172,7 @@ const {
168
172
  } = require('./neo4j-system-architecture-store.js');
169
173
 
170
174
  const losslessWriteGate = require('./lossless-write-gate.js');
175
+ const { EXTERNAL_READ_TOOLS } = require('./external-graph-query.js');
171
176
 
172
177
  const HANDLED_MUTATION_TYPES = new Set([
173
178
  'addElement',
@@ -231,7 +236,7 @@ const TOOLS = [
231
236
  },
232
237
  {
233
238
  name: 'previewSystemArchitectureMutation',
234
- description: 'Use before apply for complex or risky changes. Performs a dry-run of one or more mutations, runs schema, graph, view, and ArchiMate 3.2 validation, and does not write the graph.',
239
+ description: 'Use before apply for complex or risky changes. Performs a dry-run of one or more mutations, runs schema, graph, view, and modeling-language validation, and does not write the graph.',
235
240
  inputSchema: mutationInputSchema(),
236
241
  },
237
242
  {
@@ -380,7 +385,7 @@ const TOOLS = [
380
385
  },
381
386
  {
382
387
  name: 'queryNeo4jGraph',
383
- description: 'Run a read-only Cypher query against the Neo4j structural projection of the intent architecture, or request the projection schema so an agent can construct its own Cypher. Pass {schema: true} to return node labels, relationship types, property keys, and the legal ArchiMate element/relationship type enums. Pass {cypher: "..."} to execute a read-only query; scope it with {graphKey: $graphKey}. Write clauses (CREATE/MERGE/DELETE/SET/REMOVE/DROP/LOAD CSV/FOREACH/IN TRANSACTIONS) are rejected.',
388
+ description: 'Run a read-only Cypher query against the Neo4j structural projection of the intent architecture, or request the projection schema so an agent can construct its own Cypher. Pass {schema: true} to return node labels, relationship types, property keys, and the legal element/relationship type enums of the workspace-resolved schema bundle. Pass {cypher: "..."} to execute a read-only query; scope it with {graphKey: $graphKey}. Write clauses (CREATE/MERGE/DELETE/SET/REMOVE/DROP/LOAD CSV/FOREACH/IN TRANSACTIONS) are rejected.',
384
389
  inputSchema: {
385
390
  type: 'object',
386
391
  properties: {
@@ -415,6 +420,13 @@ const WORKSPACE_ROOT_PARAM = Object.freeze({
415
420
  description:
416
421
  'Optional absolute workspace root for this call. When provided it is used as-is; otherwise the server launch directory is used.',
417
422
  });
423
+ // Cross-project graph query: the 5 read tools accept an optional `projectId`;
424
+ // provided => routed to the federation center, omitted => the local workspace.
425
+ const PROJECT_ID_PARAM = Object.freeze({
426
+ type: 'string',
427
+ description:
428
+ 'Optional external project id. When provided, the query is routed to the federation center to read that project\'s graph (this project must be registered in .argo/federation.json); omitted = the local workspace (unchanged).',
429
+ });
418
430
  // Lossless write gate: every update/remove helper accepts an explicit loss
419
431
  // acknowledgement (see lossless-write-gate.js). add* is purely additive and is
420
432
  // left alone. Without the acknowledgement a shrinking text edit or a destructive
@@ -436,6 +448,9 @@ for (const tool of TOOLS) {
436
448
  if (!Object.prototype.hasOwnProperty.call(inputSchema.properties, 'workspaceRoot')) {
437
449
  inputSchema.properties.workspaceRoot = WORKSPACE_ROOT_PARAM;
438
450
  }
451
+ if (EXTERNAL_READ_TOOLS.has(tool.name) && !Object.prototype.hasOwnProperty.call(inputSchema.properties, 'projectId')) {
452
+ inputSchema.properties.projectId = PROJECT_ID_PARAM;
453
+ }
439
454
  if (tool.name && /^(update|remove)Architecture/.test(tool.name)) {
440
455
  for (const [key, value] of Object.entries(LOSS_ACK_INPUT_PROPS)) {
441
456
  if (!Object.prototype.hasOwnProperty.call(inputSchema.properties, key)) {
@@ -559,22 +574,7 @@ function normalizeRelativePath(value) {
559
574
  }
560
575
 
561
576
  function resolveSchemaPath(workspaceRoot) {
562
- const bundledSchemaPath = path.join(getArgoRoot(), 'schema', 'SystemArchitecture.schema.json');
563
- if (fs.existsSync(bundledSchemaPath)) {
564
- return { absolutePath: bundledSchemaPath, relativePath: SCHEMA_PATH_CANDIDATES[0] };
565
- }
566
-
567
- for (const candidate of SCHEMA_PATH_CANDIDATES) {
568
- const absolutePath = path.join(workspaceRoot, candidate);
569
- if (fs.existsSync(absolutePath)) {
570
- return { absolutePath, relativePath: candidate };
571
- }
572
- const bundledPath = path.resolve(__dirname, '..', '..', candidate);
573
- if (fs.existsSync(bundledPath)) {
574
- return { absolutePath: bundledPath, relativePath: candidate };
575
- }
576
- }
577
- throw new Error(`Unable to locate SystemArchitecture schema. Checked: ${SCHEMA_PATH_CANDIDATES.join(', ')}`);
577
+ return resolveSchemaBundle(workspaceRoot).schema;
578
578
  }
579
579
 
580
580
  function readJson(filePath, label) {
@@ -588,13 +588,17 @@ function readJson(filePath, label) {
588
588
  async function loadContext(args = {}) {
589
589
  const workspaceRoot = resolveWorkspaceRoot(args);
590
590
  const graphPath = resolveWorkspacePath(workspaceRoot, args.architecturePath || DEFAULT_GRAPH_PATH);
591
- const schemaPath = resolveSchemaPath(workspaceRoot);
591
+ // Resolve the modeling language for THIS workspace: a repository that ships
592
+ // its own bundle under .argo/schema is validated against that schema.
593
+ const { bundle, ontology } = loadSchemaBundleAndOntology(workspaceRoot);
592
594
  const context = {
593
595
  workspaceRoot,
594
596
  graphPath,
595
- schemaPath,
597
+ schemaPath: bundle.schema,
598
+ schemaBundle: bundle,
599
+ ontology,
596
600
  document: readJson(graphPath.absolutePath, graphPath.relativePath),
597
- schema: readJson(schemaPath.absolutePath, schemaPath.relativePath),
601
+ schema: bundle.schemaDocument,
598
602
  };
599
603
  context.neo4jSyncRecovery = await recoverNeo4jSyncIfNeeded({
600
604
  architecturePath: graphPath.relativePath,
@@ -605,13 +609,19 @@ async function loadContext(args = {}) {
605
609
  }
606
610
 
607
611
  function validateDocument(document, schema, options = {}) {
612
+ const ontology = options.ontology;
608
613
  const errors = [];
614
+ if (ontology && ontology.bundleValidation && ontology.bundleValidation.status === 'failed') {
615
+ errors.push(...ontology.bundleValidation.errors.map(error => `schema bundle: ${error}`));
616
+ }
609
617
  validateAgainstSchema(document, schema, '#', errors, schema);
610
- validateGraphSemantics(document, errors);
618
+ validateGraphSemantics(document, errors, ontology);
611
619
  validateArchiMateEndpointMatrix(document, errors, {
620
+ ontology,
612
621
  touchedRelationshipIds: options.touchedRelationshipIds,
613
622
  });
614
623
  validateViewElementLimits(document, errors, {
624
+ ontology,
615
625
  touchedViewIds: options.validateAllViewElementLimits
616
626
  ? (document.views || []).map(view => view && view.view_id)
617
627
  : options.touchedViewIds,
@@ -629,29 +639,42 @@ function validateDocument(document, schema, options = {}) {
629
639
  // also NOT the semantic match surface — semantic retrieval embeds attributes and
630
640
  // testcase descriptions (semanticRecordText.js), so semantic results keep them;
631
641
  // and the focus element of an intent-element read always keeps its own.
642
+ function byteLenLocal(value) {
643
+ try { return Buffer.byteLength(JSON.stringify(value)); } catch (_) { return 0; }
644
+ }
645
+
632
646
  function projectAgentFields(record, opts = {}) {
633
647
  const value = clone(record);
634
648
  let omittedAttributes = 0;
635
649
  let omittedTestcases = 0;
650
+ let omittedBytes = 0;
636
651
  if (!opts.includeAttributes && Array.isArray(value.attributes) && value.attributes.length) {
637
652
  omittedAttributes = value.attributes.length;
653
+ omittedBytes += byteLenLocal(value.attributes);
638
654
  delete value.attributes;
639
655
  }
640
656
  if (!opts.includeTestcases && Array.isArray(value.testcases) && value.testcases.length) {
641
657
  omittedTestcases = value.testcases.length;
658
+ omittedBytes += byteLenLocal(value.testcases);
642
659
  delete value.testcases;
643
660
  }
644
- return { value, omittedAttributes, omittedTestcases };
661
+ return { value, omittedAttributes, omittedTestcases, omittedBytes };
645
662
  }
646
663
 
647
664
  function buildAgentProjection(omitted) {
648
665
  if (!omitted || (omitted.attributes === 0 && omitted.testcases === 0)) {
649
666
  return null;
650
667
  }
668
+ // Only attach the explanation when the omission actually saves more than the
669
+ // note costs, so trimming a read can NEVER make it larger (cross-scenario
670
+ // no-degradation). The opt-in flags are documented in the tool description.
671
+ if ((omitted.bytes || 0) < 160) {
672
+ return null;
673
+ }
651
674
  return {
652
675
  attributesOmitted: omitted.attributes,
653
676
  testcasesOmitted: omitted.testcases,
654
- note: 'Member attributes/testcases are bookkeeping (commit/session/release ledgers, acceptance cases) and are omitted from this structural read by default. Pass includeAttributes:true / includeTestcases:true to include them verbatim. Semantic retrieval already embeds their text, so semantic hits keep them.',
677
+ note: 'includeAttributes/includeTestcases=true to include (omitted by default; semantic hits keep them).',
655
678
  };
656
679
  }
657
680
 
@@ -666,7 +689,7 @@ function buildIntentElementContext(context, args = {}) {
666
689
  const dependentDepth = normalizeDepth(args.dependentDepth, 1);
667
690
  const associationDepth = Math.max(1, normalizeDepth(args.associationDepth, 1));
668
691
  const associationNeighborDependencyDepth = normalizeDepth(args.associationNeighborDependencyDepth, 0);
669
- const graphIndex = buildGraphIndex(context.document);
692
+ const graphIndex = buildGraphIndex(context.document, context.ontology);
670
693
  const focusElement = focusResult.element;
671
694
  const includedElementIds = new Set([focusElement.id]);
672
695
  const includedRelationshipIds = new Set();
@@ -740,7 +763,7 @@ function buildIntentElementContext(context, args = {}) {
740
763
  const subgraph = buildNativeSubgraph(context.document, includedElementIds, includedRelationshipIds);
741
764
  const includeAttributes = args.includeAttributes === true;
742
765
  const includeTestcases = args.includeTestcases === true;
743
- const omitted = { attributes: 0, testcases: 0 };
766
+ const omitted = { attributes: 0, testcases: 0, bytes: 0 };
744
767
  if (!(includeAttributes && includeTestcases)) {
745
768
  subgraph.elements = subgraph.elements.map((element) => {
746
769
  if (element.id === focusElement.id) {
@@ -749,11 +772,13 @@ function buildIntentElementContext(context, args = {}) {
749
772
  const projected = projectAgentFields(element, { includeAttributes, includeTestcases });
750
773
  omitted.attributes += projected.omittedAttributes;
751
774
  omitted.testcases += projected.omittedTestcases;
775
+ omitted.bytes += projected.omittedBytes;
752
776
  return projected.value;
753
777
  });
754
778
  subgraph.relationships = subgraph.relationships.map((relationship) => {
755
779
  const projected = projectAgentFields(relationship, { includeAttributes });
756
780
  omitted.attributes += projected.omittedAttributes;
781
+ omitted.bytes += projected.omittedBytes;
757
782
  return projected.value;
758
783
  });
759
784
  }
@@ -851,11 +876,12 @@ function buildViewContext(context, args = {}) {
851
876
 
852
877
  const includeAttributes = args.includeAttributes === true;
853
878
  const includeTestcases = args.includeTestcases === true;
854
- const omitted = { attributes: 0, testcases: 0 };
879
+ const omitted = { attributes: 0, testcases: 0, bytes: 0 };
855
880
  const project = (record) => {
856
881
  const projected = projectAgentFields(record, { includeAttributes, includeTestcases });
857
882
  omitted.attributes += projected.omittedAttributes;
858
883
  omitted.testcases += projected.omittedTestcases;
884
+ omitted.bytes += projected.omittedBytes;
859
885
  return projected.value;
860
886
  };
861
887
 
@@ -975,7 +1001,7 @@ function normalizeDepth(value, defaultValue) {
975
1001
  return Math.floor(numericValue);
976
1002
  }
977
1003
 
978
- function buildGraphIndex(document) {
1004
+ function buildGraphIndex(document, ontology) {
979
1005
  const relationshipById = new Map();
980
1006
  const elementById = new Map((document.elements || []).map(element => [element.id, element]));
981
1007
  const relationshipsByElementId = new Map();
@@ -984,9 +1010,19 @@ function buildGraphIndex(document) {
984
1010
  addIndexedRelationship(relationshipsByElementId, relationship.source_id, relationship);
985
1011
  addIndexedRelationship(relationshipsByElementId, relationship.target_id, relationship);
986
1012
  }
987
- return { elementById, relationshipById, relationshipsByElementId };
1013
+ const deliveryDependencies = ontology && ontology.deliveryDependencies
1014
+ ? ontology.deliveryDependencies
1015
+ : DEFAULT_DELIVERY_DEPENDENCIES;
1016
+ return { elementById, relationshipById, relationshipsByElementId, deliveryDependencies };
988
1017
  }
989
1018
 
1019
+ // Dependency direction for the semantic-edge walk comes from the active schema
1020
+ // bundle's deliveryDependencies; this is the ArgoBument fallback by default.
1021
+ const DEFAULT_DELIVERY_DEPENDENCIES = Object.freeze({
1022
+ sourceDependsOnTarget: Object.freeze(['Access', 'Assignment', 'Specialization', 'Composition', 'Aggregation']),
1023
+ targetDependsOnSource: Object.freeze(['Serving', 'Realization', 'Flow', 'Triggering', 'Influence']),
1024
+ });
1025
+
990
1026
  function addIndexedRelationship(index, elementId, relationship) {
991
1027
  if (!index.has(elementId)) {
992
1028
  index.set(elementId, []);
@@ -1041,8 +1077,9 @@ function resolveSemanticEdges(elementId, graphIndex) {
1041
1077
  continue;
1042
1078
  }
1043
1079
 
1044
- const sourceDependsOnTarget = ['Access', 'Assignment', 'Specialization', 'Composition', 'Aggregation'].includes(relationshipType);
1045
- const targetDependsOnSource = ['Serving', 'Realization', 'Flow', 'Triggering', 'Influence'].includes(relationshipType);
1080
+ const deliveryDependencies = graphIndex.deliveryDependencies || DEFAULT_DELIVERY_DEPENDENCIES;
1081
+ const sourceDependsOnTarget = deliveryDependencies.sourceDependsOnTarget.includes(relationshipType);
1082
+ const targetDependsOnSource = deliveryDependencies.targetDependsOnSource.includes(relationshipType);
1046
1083
  if (sourceDependsOnTarget) {
1047
1084
  edges.push({ kind: isSource ? 'dependency' : 'dependent', neighborId, relationship });
1048
1085
  continue;
@@ -1914,7 +1951,7 @@ async function buildMutationResult(context, mutations, write, dependencies, loss
1914
1951
  before: beforeSummary,
1915
1952
  after: beforeSummary,
1916
1953
  errors,
1917
- guidance: buildFailureGuidance(errors),
1954
+ guidance: buildFailureGuidance(errors, context.ontology),
1918
1955
  };
1919
1956
  if (error && Array.isArray(error.duplicateConflicts) && error.duplicateConflicts.length > 0) {
1920
1957
  failed.duplicateConflicts = error.duplicateConflicts;
@@ -1925,6 +1962,7 @@ async function buildMutationResult(context, mutations, write, dependencies, loss
1925
1962
  return failed;
1926
1963
  }
1927
1964
  const errors = validateDocument(mutationResult.document, context.schema, {
1965
+ ontology: context.ontology,
1928
1966
  touchedRelationshipIds: mutationResult.touchedRelationshipIds,
1929
1967
  touchedViewIds: mutationResult.viewLimitCheckIds,
1930
1968
  });
@@ -1944,7 +1982,7 @@ async function buildMutationResult(context, mutations, write, dependencies, loss
1944
1982
  errors,
1945
1983
  };
1946
1984
  if (errors.length > 0) {
1947
- result.guidance = buildFailureGuidance(errors);
1985
+ result.guidance = buildFailureGuidance(errors, context.ontology);
1948
1986
  }
1949
1987
 
1950
1988
  // Semantic dedup gate: a would-be element create is blocked when a same-type
@@ -2199,10 +2237,10 @@ function buildMutationEmbeddingLifecycleFailure(error, result) {
2199
2237
  });
2200
2238
  }
2201
2239
 
2202
- function buildFailureGuidance(errors) {
2240
+ function buildFailureGuidance(errors, ontology) {
2203
2241
  const guidance = [];
2204
2242
  for (const error of errors || []) {
2205
- addGuidanceForError(guidance, String(error));
2243
+ addGuidanceForError(guidance, String(error), ontology);
2206
2244
  }
2207
2245
  if (guidance.length === 0 && Array.isArray(errors) && errors.length > 0) {
2208
2246
  guidance.push('Inspect the error text, call getSystemArchitecture with an explicit semantic query to refresh relevant ids, use getIntentElementContext for focused dependency context when needed, then retry with previewSystemArchitectureMutation before writing. Use an omitted-query full snapshot only when exact complete view membership is required.');
@@ -2210,15 +2248,17 @@ function buildFailureGuidance(errors) {
2210
2248
  return guidance;
2211
2249
  }
2212
2250
 
2213
- function addGuidanceForError(guidance, error) {
2251
+ function addGuidanceForError(guidance, error, ontology) {
2252
+ const language = ontology && ontology.language ? ontology.language : 'ArchiMate 3.2';
2253
+ const matrixLabel = ontology && ontology.matrixErrorLabel ? ontology.matrixErrorLabel : 'ArchiMate 3.2 relationship matrix';
2214
2254
  if (error.includes('mutation.view_ids must contain at least one view id')) {
2215
2255
  pushUnique(guidance, 'Select the target view_ids explicitly. Prefer getSystemArchitecture with an explicit semantic query to find relevant views, then use getIntentElementContext for focused element dependencies when needed. Use a full snapshot only if exact complete view membership is required.');
2216
2256
  }
2217
- if (error.includes('violates ArchiMate 3.2 relationship matrix')) {
2218
- pushUnique(guidance, 'Check relationship.type and the source and target element types against ArchiMate 3.2. If the intended meaning is still valid, choose a compliant relationship type or change the endpoint element types by remove-and-add.');
2257
+ if (error.includes(`violates ${matrixLabel}`)) {
2258
+ pushUnique(guidance, `Check relationship.type and the source and target element types against ${language}. If the intended meaning is still valid, choose a compliant relationship type or change the endpoint element types by remove-and-add.`);
2219
2259
  }
2220
- if (error.includes('uses unsupported ArchiMate relationship type')) {
2221
- pushUnique(guidance, 'Use relationship.type for the ArchiMate relationship type and choose one of the schema-supported ArchiMate 3.2 relationship types.');
2260
+ if (error.includes('uses unsupported') && error.includes('relationship type')) {
2261
+ pushUnique(guidance, `Use relationship.type for the relationship type and choose one of the schema-supported ${language} relationship types.`);
2222
2262
  }
2223
2263
  if (error.includes('id cannot be updated') || error.includes('type cannot be updated')) {
2224
2264
  pushUnique(guidance, 'Do not patch immutable identity or type fields. To change an id or type, remove the existing element or relationship, then add the replacement with the desired id or type.');
@@ -2227,10 +2267,16 @@ function addGuidanceForError(guidance, error) {
2227
2267
  pushUnique(guidance, 'Every element and relationship must belong to at least one view. Add it with view_ids, or add the existing object to an appropriate view before validating again.');
2228
2268
  }
2229
2269
  if (error.includes('must declare parent_element_id') || error.includes('top-level view')) {
2230
- pushUnique(guidance, 'Keep exactly one top-level view named SystemArchitecture. For any sub-view, set parent_element_id to an existing element and keep parent_element_name aligned with that element name.');
2270
+ const rootViewName = ontology && ontology.invariants && ontology.invariants.rootViewName
2271
+ ? ontology.invariants.rootViewName
2272
+ : 'SystemArchitecture';
2273
+ pushUnique(guidance, `Keep exactly one top-level view${rootViewName ? ` named ${rootViewName}` : ''}. For any sub-view, set parent_element_id to an existing element and keep parent_element_name aligned with that element name.`);
2231
2274
  }
2232
- if (error.includes('must contain at most 15 elements')) {
2233
- pushUnique(guidance, 'Do not force more than 15 included_elements into one view. Pause and think about layered architecture: split the view into layered sub-views, attach each sub-view with parent_element_id, and move lower-level elements into the appropriate child view before retrying.');
2275
+ if (error.includes('must contain at most') && error.includes('elements')) {
2276
+ const maxElements = ontology && ontology.invariants && ontology.invariants.maxElementsPerView !== undefined && ontology.invariants.maxElementsPerView !== null
2277
+ ? ontology.invariants.maxElementsPerView
2278
+ : 15;
2279
+ pushUnique(guidance, `Do not force more than ${maxElements} included_elements into one view. Pause and think about layered architecture: split the view into layered sub-views, attach each sub-view with parent_element_id, and move lower-level elements into the appropriate child view before retrying.`);
2234
2280
  }
2235
2281
  if (error.includes('does not exist') || error.includes('references missing')) {
2236
2282
  pushUnique(guidance, 'Refresh current ids with getSystemArchitecture semantic query first, then call getIntentElementContext for any returned element that needs dependency context. Do not guess ids; use existing element, relationship, and view ids or create missing objects first.');
@@ -3086,13 +3132,25 @@ async function queryNeo4jGraphTool(args = {}) {
3086
3132
  function queryNeo4jGraphSchemaResult(architecturePath, workspaceRoot) {
3087
3133
  const schema = buildNeo4jGraphSchema(architecturePath);
3088
3134
  let typeEnums = {};
3135
+ let schemaBundleInfo = {};
3089
3136
  try {
3090
3137
  const resolvedRoot = workspaceRoot || resolveWorkspaceRoot({ architecturePath });
3091
- const schemaPath = resolveSchemaPath(resolvedRoot);
3092
- const jsonSchema = readJson(schemaPath.absolutePath, schemaPath.relativePath);
3138
+ const { bundle, ontology } = loadSchemaBundleAndOntology(resolvedRoot);
3139
+ const enums = resolveTypeEnums(bundle);
3093
3140
  typeEnums = {
3094
- archimateElementTypes: (jsonSchema.$defs.archimateElementType || {}).enum || [],
3095
- archimateRelationshipTypes: (jsonSchema.$defs.archimateRelationshipType || {}).enum || [],
3141
+ archimateElementTypes: enums.elementTypes,
3142
+ archimateRelationshipTypes: enums.relationshipTypes,
3143
+ schemaLanguage: (bundle.config && bundle.config.language) || null,
3144
+ schemaKind: bundle.kind,
3145
+ schemaDialect: ontology.dialect,
3146
+ actorElementType: ontology.actorElementType,
3147
+ bundleValidation: ontology.bundleValidation,
3148
+ };
3149
+ schemaBundleInfo = {
3150
+ schemaPath: bundle.schema.relativePath,
3151
+ schemaKind: bundle.kind,
3152
+ schemaDir: bundle.relativeDir,
3153
+ guidePath: bundle.guidePath ? bundle.guidePath.relativePath : null,
3096
3154
  };
3097
3155
  } catch (error) {
3098
3156
  typeEnums = {
@@ -3109,6 +3167,7 @@ function queryNeo4jGraphSchemaResult(architecturePath, workspaceRoot) {
3109
3167
  schema: {
3110
3168
  ...schema,
3111
3169
  ...typeEnums,
3170
+ ...schemaBundleInfo,
3112
3171
  },
3113
3172
  usage: {
3114
3173
  scopeGraph: 'MATCH (e:Element {graphKey: $graphKey}) ...',