archgraph-argo 0.12.6 → 0.13.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.
|
@@ -747,9 +747,46 @@ function fullProjection(graph, qeaPath, opts) {
|
|
|
747
747
|
// ---------------------------------------------------------------------------
|
|
748
748
|
// Export
|
|
749
749
|
// ---------------------------------------------------------------------------
|
|
750
|
-
//
|
|
751
|
-
//
|
|
752
|
-
//
|
|
750
|
+
// Read the EA diagram GEOMETRY for one KG view from a .qea model (read-only).
|
|
751
|
+
// The view maps to the diagram anchored by the deterministic ea_guid (diag:<viewId>)
|
|
752
|
+
// or the schema_view_id StyleEx token written by the sync. Returns:
|
|
753
|
+
// - element boxes = t_diagramobjects rects joined to t_object.Alias (schema id)
|
|
754
|
+
// - connector lines = t_diagramlinks rows joined to the connector's schema_id tag
|
|
755
|
+
// Returns null when the view has no matching EA diagram. Never writes EA geometry.
|
|
756
|
+
function readViewDiagramGeometry(qeaPath, viewId) {
|
|
757
|
+
const v = String(viewId === null || viewId === undefined ? '' : viewId).trim();
|
|
758
|
+
if (v === '') { return null; }
|
|
759
|
+
const db = openQea(qeaPath);
|
|
760
|
+
try {
|
|
761
|
+
ensureMetaTable(db);
|
|
762
|
+
const guid = deterministicGuid('diag:' + v);
|
|
763
|
+
const diag = db.prepare('SELECT Diagram_ID FROM t_diagram WHERE ea_guid = ? OR StyleEx LIKE ?')
|
|
764
|
+
.get(guid, '%schema_view_id=' + v + ';%');
|
|
765
|
+
if (!diag) { return null; }
|
|
766
|
+
const diagramId = Number(diag.Diagram_ID);
|
|
767
|
+
const elements = db.prepare(
|
|
768
|
+
'SELECT o.Alias AS id, d.RectLeft AS left, d.RectTop AS top, d.RectRight AS right, d.RectBottom AS bottom ' +
|
|
769
|
+
'FROM t_diagramobjects d JOIN t_object o ON o.Object_ID = d.Object_ID ' +
|
|
770
|
+
'WHERE d.Diagram_ID = ? AND o.Alias IS NOT NULL AND o.Alias <> ? ' +
|
|
771
|
+
'ORDER BY d.Sequence'
|
|
772
|
+
).all(diagramId, '').map((r) => ({
|
|
773
|
+
id: String(r.id),
|
|
774
|
+
left: Number(r.left), top: Number(r.top), right: Number(r.right), bottom: Number(r.bottom),
|
|
775
|
+
}));
|
|
776
|
+
const relationships = db.prepare(
|
|
777
|
+
'SELECT t.VALUE AS id, dl.Geometry AS path ' +
|
|
778
|
+
'FROM t_diagramlinks dl JOIN t_connectortag t ON t.ElementID = dl.ConnectorID AND t.Property = ? ' +
|
|
779
|
+
'WHERE dl.DiagramID = ? ORDER BY dl.ConnectorID'
|
|
780
|
+
).all('schema_id', diagramId).map((r) => ({
|
|
781
|
+
id: String(r.id),
|
|
782
|
+
path: String(r.path === null || r.path === undefined ? '' : r.path),
|
|
783
|
+
}));
|
|
784
|
+
return { diagramId, elements, relationships };
|
|
785
|
+
} finally {
|
|
786
|
+
try { db.close(); } catch { /* ignore */ }
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
753
790
|
function exportQeaToGraph(qeaPath) {
|
|
754
791
|
const db = openQea(qeaPath);
|
|
755
792
|
try {
|
|
@@ -789,6 +826,7 @@ module.exports = {
|
|
|
789
826
|
canonicalArchimateType,
|
|
790
827
|
syncGraphToQea,
|
|
791
828
|
exportQeaToGraph,
|
|
829
|
+
readViewDiagramGeometry,
|
|
792
830
|
snapshotQea,
|
|
793
831
|
readMetaKind,
|
|
794
832
|
fullProjection,
|
|
@@ -219,7 +219,7 @@ const TOOLS = [
|
|
|
219
219
|
},
|
|
220
220
|
{
|
|
221
221
|
name: 'getArchitectureViewContext',
|
|
222
|
-
description: 'read-only query that resolves one view by view_id into its complete membership: the view object, every member element (from included_elements), every member relationship (from included_relationships), the parent element, and optionally child sub-views declared by member elements. Resolves ids into full canonical objects instead of returning raw id lists.',
|
|
222
|
+
description: 'read-only query that resolves one view by view_id into its complete membership: the view object, every member element (from included_elements), every member relationship (from included_relationships), the parent element, and optionally child sub-views declared by member elements. Resolves ids into full canonical objects instead of returning raw id lists. Optional includeEaGeometry (default false) additionally returns the EA diagram geometry of the resolved view.',
|
|
223
223
|
inputSchema: viewContextInputSchema(),
|
|
224
224
|
},
|
|
225
225
|
{
|
|
@@ -441,6 +441,7 @@ function viewContextInputSchema() {
|
|
|
441
441
|
view_id: { type: 'string', description: 'The id of the view to resolve.' },
|
|
442
442
|
includeParentElement: { type: 'boolean', description: 'Default: true. Resolve the parent element referenced by the view.' },
|
|
443
443
|
includeChildViews: { type: 'boolean', description: 'Default: false. Include child views declared by member elements through subdiagram_views.' },
|
|
444
|
+
includeEaGeometry: { type: 'boolean', description: 'Default: false (opt-in). When true, additionally resolve the diagram GEOMETRY (element boxes + connector line paths) for this view from the workspace EA model (.qea) and return it under a `geometry` field aligned by schema id with the resolved members. By default the EA model is never touched and no `geometry` field is returned; a missing EA model/diagram yields geometry.present=false, never an error.' },
|
|
444
445
|
},
|
|
445
446
|
additionalProperties: false,
|
|
446
447
|
};
|
|
@@ -678,6 +679,50 @@ function buildIntentElementContext(context, args = {}) {
|
|
|
678
679
|
};
|
|
679
680
|
}
|
|
680
681
|
|
|
682
|
+
// ---------------------------------------------------------------------------
|
|
683
|
+
// Optional EA diagram GEOMETRY (opt-in, default off)
|
|
684
|
+
// ---------------------------------------------------------------------------
|
|
685
|
+
// getArchitectureViewContext is a canonical-graph read; it never touches the EA
|
|
686
|
+
// model unless the caller explicitly sets includeEaGeometry=true. The workspace's
|
|
687
|
+
// EA model (.qea, the SQLite carrier this toolchain can read) may hold human-laid-out
|
|
688
|
+
// diagram geometry for a view — element boxes (t_diagramobjects rects) and connector
|
|
689
|
+
// line paths (t_diagramlinks). When present it is returned under `geometry`, aligned
|
|
690
|
+
// by schema id with the resolved members, so an image-capable LLM can redraw the view
|
|
691
|
+
// faithfully. Absent model/diagram → present:false (never an error).
|
|
692
|
+
const EA_GEOMETRY_MODEL_EXTENSIONS = new Set(['.qea']);
|
|
693
|
+
function findEaGeometryModelPath(workspaceRoot) {
|
|
694
|
+
try {
|
|
695
|
+
const entries = fs.readdirSync(workspaceRoot, { withFileTypes: true });
|
|
696
|
+
const names = entries
|
|
697
|
+
.filter((entry) => entry.isFile() && EA_GEOMETRY_MODEL_EXTENSIONS.has(path.extname(entry.name).toLowerCase()))
|
|
698
|
+
.map((entry) => entry.name)
|
|
699
|
+
.sort();
|
|
700
|
+
return names.length > 0 ? path.join(workspaceRoot, names[0]) : null;
|
|
701
|
+
} catch {
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
function readEaViewGeometry(workspaceRoot, viewId) {
|
|
706
|
+
// Lazily require the .qea projection lib: it depends on node:sqlite (Node >= 22),
|
|
707
|
+
// so it is only loaded when geometry is actually requested, never for other calls.
|
|
708
|
+
let modelRel = null;
|
|
709
|
+
try {
|
|
710
|
+
const modelPath = findEaGeometryModelPath(workspaceRoot);
|
|
711
|
+
if (!modelPath) {
|
|
712
|
+
return { source: null, present: false, elements: [], relationships: [] };
|
|
713
|
+
}
|
|
714
|
+
modelRel = normalizeRelativePath(path.relative(workspaceRoot, modelPath)) || null;
|
|
715
|
+
const eaQeaLib = require('./ea-qea-sync-lib.js');
|
|
716
|
+
const geo = eaQeaLib.readViewDiagramGeometry(modelPath, viewId);
|
|
717
|
+
if (!geo) {
|
|
718
|
+
return { source: modelRel, present: false, elements: [], relationships: [] };
|
|
719
|
+
}
|
|
720
|
+
return { source: modelRel, present: true, elements: geo.elements, relationships: geo.relationships };
|
|
721
|
+
} catch {
|
|
722
|
+
return { source: modelRel, present: false, elements: [], relationships: [] };
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
681
726
|
function buildViewContext(context, args = {}) {
|
|
682
727
|
const viewId = typeof args.view_id === 'string' ? args.view_id.trim() : '';
|
|
683
728
|
if (!viewId) {
|
|
@@ -746,7 +791,7 @@ function buildViewContext(context, args = {}) {
|
|
|
746
791
|
}
|
|
747
792
|
}
|
|
748
793
|
|
|
749
|
-
|
|
794
|
+
const result = {
|
|
750
795
|
status: 'passed',
|
|
751
796
|
graphPath: context.graphPath.relativePath,
|
|
752
797
|
view: clone(view),
|
|
@@ -757,6 +802,10 @@ function buildViewContext(context, args = {}) {
|
|
|
757
802
|
parentElement,
|
|
758
803
|
childViews,
|
|
759
804
|
};
|
|
805
|
+
if (args.includeEaGeometry === true) {
|
|
806
|
+
result.geometry = readEaViewGeometry(context.workspaceRoot, viewId);
|
|
807
|
+
}
|
|
808
|
+
return result;
|
|
760
809
|
}
|
|
761
810
|
|
|
762
811
|
function resolveFocusElement(document, args) {
|
|
@@ -1844,11 +1893,11 @@ function summarizeDocument(document) {
|
|
|
1844
1893
|
};
|
|
1845
1894
|
}
|
|
1846
1895
|
|
|
1847
|
-
// --- WP2791: post-canonical-write .qea projection (parallel to Neo4j sync, non-fatal) ---
|
|
1848
|
-
// Target resolution (decision qea-full-wholefile-argo-scripts-no-config): env ARGO_EA_QEA >
|
|
1849
|
-
// the single *.qea at the workspace root (0/many -> no-op with an explicit log). NO config file.
|
|
1850
|
-
// Projection script runs from argo/scripts (same package as the MCP runtime), so a workspace
|
|
1851
|
-
// does not need to ship its own projection script (bundled argo/scripts module).
|
|
1896
|
+
// --- WP2791: post-canonical-write .qea projection (parallel to Neo4j sync, non-fatal) ---
|
|
1897
|
+
// Target resolution (decision qea-full-wholefile-argo-scripts-no-config): env ARGO_EA_QEA >
|
|
1898
|
+
// the single *.qea at the workspace root (0/many -> no-op with an explicit log). NO config file.
|
|
1899
|
+
// Projection script runs from argo/scripts (same package as the MCP runtime), so a workspace
|
|
1900
|
+
// does not need to ship its own projection script (bundled argo/scripts module).
|
|
1852
1901
|
function resolveQeaProjectionTarget(context) {
|
|
1853
1902
|
const none = (reason, hasEaSignals) => ({ target: null, reason, hasEaSignals: !!hasEaSignals });
|
|
1854
1903
|
try {
|
|
@@ -1889,34 +1938,34 @@ function resolveQeaProjectionTarget(context) {
|
|
|
1889
1938
|
console.log('[ea-qea] projection target resolution failed: ' + String(error && error.message ? error.message : error));
|
|
1890
1939
|
return none('EA .qea target resolution failed: ' + String(error && error.message ? error.message : error), true);
|
|
1891
1940
|
}
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
function runQeaProjection(target) {
|
|
1895
|
-
return new Promise((resolve) => {
|
|
1896
|
-
const script = path.join(__dirname, 'ea-qea-sync.js');
|
|
1897
|
-
if (!fs.existsSync(script)) {
|
|
1898
|
-
resolve({ ok: false, error: 'argo/scripts/ea-qea-sync.js missing', ms: 0 });
|
|
1899
|
-
return;
|
|
1900
|
-
}
|
|
1901
|
-
const snapshotDir = path.join(target.workspaceRoot, '.argo', 'temp', 'qea-backups');
|
|
1902
|
-
const args = [script, '--mode', 'sync', '--graph', target.graphPath, '--qea', target.qeaPath, '--snapshot-dir', snapshotDir];
|
|
1903
|
-
const started = Date.now();
|
|
1904
|
-
let stderr = '';
|
|
1905
|
-
let child;
|
|
1906
|
-
try {
|
|
1907
|
-
child = spawn(process.execPath, args, { cwd: target.workspaceRoot, windowsHide: true });
|
|
1908
|
-
} catch (error) {
|
|
1909
|
-
resolve({ ok: false, error: String(error && error.message ? error.message : error), ms: Date.now() - started });
|
|
1910
|
-
return;
|
|
1911
|
-
}
|
|
1912
|
-
child.stderr.on('data', (d) => { stderr += String(d); });
|
|
1913
|
-
child.on('error', (err) => resolve({ ok: false, error: String(err && err.message ? err.message : err), ms: Date.now() - started, stderr: stderr.slice(0, 600) }));
|
|
1914
|
-
child.on('close', (code) => {
|
|
1915
|
-
resolve({ ok: code === 0, code, ms: Date.now() - started, stderr: stderr.slice(0, 600) });
|
|
1916
|
-
});
|
|
1917
|
-
});
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
function runQeaProjection(target) {
|
|
1944
|
+
return new Promise((resolve) => {
|
|
1945
|
+
const script = path.join(__dirname, 'ea-qea-sync.js');
|
|
1946
|
+
if (!fs.existsSync(script)) {
|
|
1947
|
+
resolve({ ok: false, error: 'argo/scripts/ea-qea-sync.js missing', ms: 0 });
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
const snapshotDir = path.join(target.workspaceRoot, '.argo', 'temp', 'qea-backups');
|
|
1951
|
+
const args = [script, '--mode', 'sync', '--graph', target.graphPath, '--qea', target.qeaPath, '--snapshot-dir', snapshotDir];
|
|
1952
|
+
const started = Date.now();
|
|
1953
|
+
let stderr = '';
|
|
1954
|
+
let child;
|
|
1955
|
+
try {
|
|
1956
|
+
child = spawn(process.execPath, args, { cwd: target.workspaceRoot, windowsHide: true });
|
|
1957
|
+
} catch (error) {
|
|
1958
|
+
resolve({ ok: false, error: String(error && error.message ? error.message : error), ms: Date.now() - started });
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
child.stderr.on('data', (d) => { stderr += String(d); });
|
|
1962
|
+
child.on('error', (err) => resolve({ ok: false, error: String(err && err.message ? err.message : err), ms: Date.now() - started, stderr: stderr.slice(0, 600) }));
|
|
1963
|
+
child.on('close', (code) => {
|
|
1964
|
+
resolve({ ok: code === 0, code, ms: Date.now() - started, stderr: stderr.slice(0, 600) });
|
|
1965
|
+
});
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1920
1969
|
function writeGraph(graphPath, document) {
|
|
1921
1970
|
const tempPath = `${graphPath}.${process.pid}.${Date.now()}.tmp`;
|
|
1922
1971
|
fs.writeFileSync(tempPath, `${JSON.stringify(document, null, 2)}\n`, 'utf8');
|
package/package.json
CHANGED