@documentation-robotics/cli 0.1.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -2
- package/dist/analysis/relationship-classifier.js +2 -9
- package/dist/analysis/relationship-classifier.js.map +2 -2
- package/dist/analyzers/call-graph-utils.js +2 -2
- package/dist/analyzers/call-graph-utils.js.map +2 -2
- package/dist/analyzers/cards/build.js +171 -0
- package/dist/analyzers/cards/build.js.map +7 -0
- package/dist/analyzers/cards/classify.js +42 -0
- package/dist/analyzers/cards/classify.js.map +7 -0
- package/dist/analyzers/cards/edge-index.js +44 -0
- package/dist/analyzers/cards/edge-index.js.map +7 -0
- package/dist/analyzers/cards/facts.js +46 -0
- package/dist/analyzers/cards/facts.js.map +7 -0
- package/dist/analyzers/cards/graph.js +42 -0
- package/dist/analyzers/cards/graph.js.map +7 -0
- package/dist/analyzers/cards/index.js +8 -0
- package/dist/analyzers/cards/index.js.map +7 -0
- package/dist/analyzers/cards/tier0.js +18 -0
- package/dist/analyzers/cards/tier0.js.map +7 -0
- package/dist/analyzers/cards/types.js +1 -0
- package/dist/analyzers/cards/types.js.map +7 -0
- package/dist/analyzers/cbm-analyzer.js +195 -171
- package/dist/analyzers/cbm-analyzer.js.map +2 -2
- package/dist/analyzers/cbm-responses.js +336 -0
- package/dist/analyzers/cbm-responses.js.map +7 -0
- package/dist/analyzers/edge-relationships.js +45 -0
- package/dist/analyzers/edge-relationships.js.map +7 -0
- package/dist/analyzers/mapping-loader.js +9 -0
- package/dist/analyzers/mapping-loader.js.map +2 -2
- package/dist/analyzers/tier0-rules.js +153 -0
- package/dist/analyzers/tier0-rules.js.map +7 -0
- package/dist/analyzers/types.js.map +1 -1
- package/dist/analyzers/verify-engine.js +22 -19
- package/dist/analyzers/verify-engine.js.map +2 -2
- package/dist/audit/relationships/ai/prompts.js +10 -10
- package/dist/audit/relationships/ai/prompts.js.map +2 -2
- package/dist/audit/relationships/analysis/layer-templates.js +52 -52
- package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
- package/dist/cli.js +112 -28
- package/dist/cli.js.map +3 -3
- package/dist/coding-agents/agents/claude-code.js +3 -1
- package/dist/coding-agents/agents/claude-code.js.map +2 -2
- package/dist/coding-agents/claude-code-client.js +2 -1
- package/dist/coding-agents/claude-code-client.js.map +2 -2
- package/dist/coding-agents/copilot-client.js +17 -12
- package/dist/coding-agents/copilot-client.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +152 -0
- package/dist/coding-agents/default-permissions.js.map +7 -0
- package/dist/coding-agents/drbot-mcp.js +167 -0
- package/dist/coding-agents/drbot-mcp.js.map +7 -0
- package/dist/commands/add.js +12 -7
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +134 -23
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/audit.js +6 -6
- package/dist/commands/audit.js.map +2 -2
- package/dist/commands/changeset-verify.js +55 -0
- package/dist/commands/changeset-verify.js.map +7 -0
- package/dist/commands/changeset.js +261 -147
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +52 -19
- package/dist/commands/delete.js.map +3 -3
- package/dist/commands/element.js +3 -2
- package/dist/commands/element.js.map +2 -2
- package/dist/commands/farm.js +773 -0
- package/dist/commands/farm.js.map +7 -0
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/mcp.js +122 -0
- package/dist/commands/mcp.js.map +7 -0
- package/dist/commands/relationship.js +251 -47
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/reports.js +1 -1
- package/dist/commands/reports.js.map +2 -2
- package/dist/commands/update.js +56 -24
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/upgrade.js +99 -11
- package/dist/commands/upgrade.js.map +3 -3
- package/dist/commands/validate.js +41 -3
- package/dist/commands/validate.js.map +2 -2
- package/dist/commands/version.js +2 -2
- package/dist/commands/version.js.map +2 -2
- package/dist/commands/visualize.js +4 -3
- package/dist/commands/visualize.js.map +2 -2
- package/dist/config.js +83 -0
- package/dist/config.js.map +7 -0
- package/dist/core/annotation-store.js +138 -0
- package/dist/core/annotation-store.js.map +7 -0
- package/dist/core/catalog-migration.js +33 -0
- package/dist/core/catalog-migration.js.map +7 -0
- package/dist/core/changeset-analysis.js +421 -0
- package/dist/core/changeset-analysis.js.map +7 -0
- package/dist/core/changeset-base-index.js +153 -0
- package/dist/core/changeset-base-index.js.map +7 -0
- package/dist/core/changeset-brief-delta.js +102 -0
- package/dist/core/changeset-brief-delta.js.map +7 -0
- package/dist/core/changeset-graph.js +281 -0
- package/dist/core/changeset-graph.js.map +7 -0
- package/dist/core/changeset-only-guard.js +32 -0
- package/dist/core/changeset-only-guard.js.map +7 -0
- package/dist/core/changeset-summary.js +68 -0
- package/dist/core/changeset-summary.js.map +7 -0
- package/dist/core/changeset-validator.js +66 -20
- package/dist/core/changeset-validator.js.map +2 -2
- package/dist/core/changeset-verify.js +518 -0
- package/dist/core/changeset-verify.js.map +7 -0
- package/dist/core/changeset.js +56 -9
- package/dist/core/changeset.js.map +2 -2
- package/dist/core/element-type-migration.js +145 -0
- package/dist/core/element-type-migration.js.map +7 -0
- package/dist/core/farm-manifest.js +140 -0
- package/dist/core/farm-manifest.js.map +7 -0
- package/dist/core/farm-sync-engine.js +346 -0
- package/dist/core/farm-sync-engine.js.map +7 -0
- package/dist/core/farm-sync-state.js +167 -0
- package/dist/core/farm-sync-state.js.map +7 -0
- package/dist/core/manifest.js +6 -3
- package/dist/core/manifest.js.map +2 -2
- package/dist/core/migration-registry.js +84 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model-upgrade.js +102 -0
- package/dist/core/model-upgrade.js.map +7 -0
- package/dist/core/model.js +257 -44
- package/dist/core/model.js.map +3 -3
- package/dist/core/mutation-handler.js +71 -6
- package/dist/core/mutation-handler.js.map +2 -2
- package/dist/core/relationship-migration-table.js +6639 -0
- package/dist/core/relationship-migration-table.js.map +7 -0
- package/dist/core/relationship-migration.js +272 -0
- package/dist/core/relationship-migration.js.map +7 -0
- package/dist/core/relationship-prune.js +49 -0
- package/dist/core/relationship-prune.js.map +7 -0
- package/dist/core/relationship-staging.js +68 -0
- package/dist/core/relationship-staging.js.map +7 -0
- package/dist/core/relationships.js +26 -4
- package/dist/core/relationships.js.map +2 -2
- package/dist/core/report-data-model.js +7 -13
- package/dist/core/report-data-model.js.map +2 -2
- package/dist/core/staged-changeset-storage.js +64 -17
- package/dist/core/staged-changeset-storage.js.map +2 -2
- package/dist/core/staged-source-reference.js +28 -0
- package/dist/core/staged-source-reference.js.map +7 -0
- package/dist/core/staging-area.js +83 -18
- package/dist/core/staging-area.js.map +2 -2
- package/dist/core/virtual-projection.js +14 -5
- package/dist/core/virtual-projection.js.map +2 -2
- package/dist/export/archimate-exporter.js +1 -0
- package/dist/export/archimate-exporter.js.map +2 -2
- package/dist/generated/compiled-validators.js +2 -2
- package/dist/generated/compiled-validators.js.map +2 -2
- package/dist/generated/layer-registry.js +16 -16
- package/dist/generated/layer-registry.js.map +2 -2
- package/dist/generated/node-types.js +186 -168
- package/dist/generated/node-types.js.map +2 -2
- package/dist/generated/relationship-index.js +2209 -7727
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
- package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
- package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
- package/dist/integrations/claude_code/commands/dr-map.md +13 -0
- package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
- package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
- package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
- package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +79 -0
- package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
- package/dist/integrations/claude_code/docs/README.md +52 -1
- package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
- package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
- package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
- package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
- package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
- package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
- package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
- package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
- package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
- package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
- package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
- package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
- package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
- package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
- package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
- package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
- package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
- package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
- package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
- package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
- package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
- package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
- package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
- package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
- package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
- package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
- package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
- package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
- package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/hash-utils.js.map +2 -2
- package/dist/mcp/api-key-manager.js +111 -0
- package/dist/mcp/api-key-manager.js.map +7 -0
- package/dist/mcp/changeset-only.js +34 -0
- package/dist/mcp/changeset-only.js.map +7 -0
- package/dist/mcp/http-transport.js +176 -0
- package/dist/mcp/http-transport.js.map +7 -0
- package/dist/mcp/resource-registry.js +97 -0
- package/dist/mcp/resource-registry.js.map +7 -0
- package/dist/mcp/tool-registry.js +60 -0
- package/dist/mcp/tool-registry.js.map +7 -0
- package/dist/mcp/tools/annotation-create.js +39 -0
- package/dist/mcp/tools/annotation-create.js.map +7 -0
- package/dist/mcp/tools/annotation-delete.js +29 -0
- package/dist/mcp/tools/annotation-delete.js.map +7 -0
- package/dist/mcp/tools/annotation-get.js +29 -0
- package/dist/mcp/tools/annotation-get.js.map +7 -0
- package/dist/mcp/tools/annotation-list.js +24 -0
- package/dist/mcp/tools/annotation-list.js.map +7 -0
- package/dist/mcp/tools/annotation-reply.js +34 -0
- package/dist/mcp/tools/annotation-reply.js.map +7 -0
- package/dist/mcp/tools/annotation-shared.js +10 -0
- package/dist/mcp/tools/annotation-shared.js.map +7 -0
- package/dist/mcp/tools/annotation-update.js +39 -0
- package/dist/mcp/tools/annotation-update.js.map +7 -0
- package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
- package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
- package/dist/mcp/tools/changeset-editing.js +184 -0
- package/dist/mcp/tools/changeset-editing.js.map +7 -0
- package/dist/mcp/tools/changeset-lifecycle.js +83 -0
- package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
- package/dist/mcp/tools/changeset-list.js +33 -0
- package/dist/mcp/tools/changeset-list.js.map +7 -0
- package/dist/mcp/tools/changeset-shared.js +31 -0
- package/dist/mcp/tools/changeset-shared.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +55 -0
- package/dist/mcp/tools/changeset-show.js.map +7 -0
- package/dist/mcp/tools/changeset-verify.js +43 -0
- package/dist/mcp/tools/changeset-verify.js.map +7 -0
- package/dist/mcp/tools/chat-launch.js +50 -0
- package/dist/mcp/tools/chat-launch.js.map +7 -0
- package/dist/mcp/tools/chat-status.js +26 -0
- package/dist/mcp/tools/chat-status.js.map +7 -0
- package/dist/mcp/tools/index.js +65 -0
- package/dist/mcp/tools/index.js.map +7 -0
- package/dist/mcp/tools/model-add.js +107 -0
- package/dist/mcp/tools/model-add.js.map +7 -0
- package/dist/mcp/tools/model-delete.js +99 -0
- package/dist/mcp/tools/model-delete.js.map +7 -0
- package/dist/mcp/tools/model-export.js +72 -0
- package/dist/mcp/tools/model-export.js.map +7 -0
- package/dist/mcp/tools/model-info.js +51 -0
- package/dist/mcp/tools/model-info.js.map +7 -0
- package/dist/mcp/tools/model-list.js +53 -0
- package/dist/mcp/tools/model-list.js.map +7 -0
- package/dist/mcp/tools/model-reload.js +34 -0
- package/dist/mcp/tools/model-reload.js.map +7 -0
- package/dist/mcp/tools/model-search.js +102 -0
- package/dist/mcp/tools/model-search.js.map +7 -0
- package/dist/mcp/tools/model-show.js +68 -0
- package/dist/mcp/tools/model-show.js.map +7 -0
- package/dist/mcp/tools/model-stats.js +22 -0
- package/dist/mcp/tools/model-stats.js.map +7 -0
- package/dist/mcp/tools/model-trace.js +118 -0
- package/dist/mcp/tools/model-trace.js.map +7 -0
- package/dist/mcp/tools/model-update.js +121 -0
- package/dist/mcp/tools/model-update.js.map +7 -0
- package/dist/mcp/tools/model-validate.js +78 -0
- package/dist/mcp/tools/model-validate.js.map +7 -0
- package/dist/mcp/tools/relationship-tools.js +98 -0
- package/dist/mcp/tools/relationship-tools.js.map +7 -0
- package/dist/mcp/tools/shared.js +126 -0
- package/dist/mcp/tools/shared.js.map +7 -0
- package/dist/mcp/tools/spec-type.js +90 -0
- package/dist/mcp/tools/spec-type.js.map +7 -0
- package/dist/reports/model-layer-report-generator.js +5 -3
- package/dist/reports/model-layer-report-generator.js.map +2 -2
- package/dist/reports/model-readme-generator.js +116 -0
- package/dist/reports/model-readme-generator.js.map +7 -0
- package/dist/reports/model-report-data.js +37 -1
- package/dist/reports/model-report-data.js.map +2 -2
- package/dist/reports/model-report-orchestrator.js +29 -6
- package/dist/reports/model-report-orchestrator.js.map +2 -2
- package/dist/reports/model-report-utils.js +9 -0
- package/dist/reports/model-report-utils.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
- package/dist/schemas/bundled/analyzers/manifest.json +2 -2
- package/dist/schemas/bundled/api.json +1029 -1658
- package/dist/schemas/bundled/apm.json +499 -734
- package/dist/schemas/bundled/application.json +745 -370
- package/dist/schemas/bundled/base.json +3 -220
- package/dist/schemas/bundled/business.json +452 -366
- package/dist/schemas/bundled/data-model.json +403 -383
- package/dist/schemas/bundled/data-store.json +334 -558
- package/dist/schemas/bundled/manifest.json +18 -18
- package/dist/schemas/bundled/motivation.json +349 -368
- package/dist/schemas/bundled/navigation.json +415 -731
- package/dist/schemas/bundled/product.json +325 -159
- package/dist/schemas/bundled/security.json +864 -1232
- package/dist/schemas/bundled/technology.json +574 -703
- package/dist/schemas/bundled/testing.json +718 -1643
- package/dist/schemas/bundled/ux.json +739 -1352
- package/dist/server/schemas.js +8 -0
- package/dist/server/schemas.js.map +2 -2
- package/dist/server/server.js +234 -31
- package/dist/server/server.js.map +2 -2
- package/dist/server-entry.js +2 -1
- package/dist/server-entry.js.map +2 -2
- package/dist/telemetry/config.js +2 -40
- package/dist/telemetry/config.js.map +2 -2
- package/dist/telemetry/index.js +1 -1
- package/dist/types/index.js.map +2 -2
- package/dist/utils/ansi-suppressor.js +23 -0
- package/dist/utils/ansi-suppressor.js.map +7 -0
- package/dist/utils/attribute-patch.js +41 -0
- package/dist/utils/attribute-patch.js.map +7 -0
- package/dist/utils/color-utils.js +34 -0
- package/dist/utils/color-utils.js.map +7 -0
- package/dist/utils/element-reference-shape.js +15 -1
- package/dist/utils/element-reference-shape.js.map +3 -3
- package/dist/utils/error-hints.js +29 -0
- package/dist/utils/error-hints.js.map +7 -0
- package/dist/utils/errors.js +75 -10
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/globals.js +17 -1
- package/dist/utils/globals.js.map +2 -2
- package/dist/utils/project-paths.js +50 -3
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/reference-path-parser.js +49 -0
- package/dist/utils/reference-path-parser.js.map +7 -0
- package/dist/utils/source-reference.js +17 -10
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +10 -2
- package/dist/utils/spec-version.js.map +2 -2
- package/dist/validators/composed-reference-validator.js +240 -0
- package/dist/validators/composed-reference-validator.js.map +7 -0
- package/dist/validators/reference-validator.js +57 -13
- package/dist/validators/reference-validator.js.map +3 -3
- package/dist/validators/relationship-schema-validator.js +33 -5
- package/dist/validators/relationship-schema-validator.js.map +3 -3
- package/dist/validators/schema-validator.js +27 -9
- package/dist/validators/schema-validator.js.map +2 -2
- package/dist/validators/types.js.map +1 -1
- package/dist/viewer/README.md +6 -6
- package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
- package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/manifest.json +10 -10
- package/dist/viewer/version.json +1 -1
- package/package.json +7 -3
- package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
- package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ChangesetGraph } from "../../core/changeset-graph.js";
|
|
3
|
+
import { StagingAreaManager } from "../../core/staging-area.js";
|
|
4
|
+
import { activeStagedChangeset } from "./changeset-shared.js";
|
|
5
|
+
import { getAllLayerIds, isValidLayer } from "../../generated/layer-registry.js";
|
|
6
|
+
import { NODE_TYPES } from "../../generated/node-types.js";
|
|
7
|
+
import { CLIError, ErrorCategory, findSimilar, formatValidOptions } from "../../utils/errors.js";
|
|
8
|
+
import { jsonResult, loadModel, rootPathSchema, runTool } from "./shared.js";
|
|
9
|
+
const inputSchema = {
|
|
10
|
+
query: z.string().optional().describe("Text to match against element ID and name (case-insensitive substring). Ignored when sourceFile is set."),
|
|
11
|
+
layer: z.string().optional().describe("Restrict search to a single layer."),
|
|
12
|
+
type: z.string().optional().describe("Restrict search to a single element type."),
|
|
13
|
+
sourceFile: z.string().optional().describe("Find elements whose source_reference points at this file path."),
|
|
14
|
+
rootPath: rootPathSchema
|
|
15
|
+
};
|
|
16
|
+
function normalizePath(filePath) {
|
|
17
|
+
return filePath.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
18
|
+
}
|
|
19
|
+
function matchesSourceFile(element, sourceFilePath) {
|
|
20
|
+
const sourceRef = element.getSourceReference();
|
|
21
|
+
if (!sourceRef)
|
|
22
|
+
return !1;
|
|
23
|
+
const normalizedQuery = normalizePath(sourceFilePath);
|
|
24
|
+
return (sourceRef.locations ?? []).some((loc) => normalizePath(loc.file) === normalizedQuery);
|
|
25
|
+
}
|
|
26
|
+
async function modelSearchHandler(args) {
|
|
27
|
+
return runTool(async () => {
|
|
28
|
+
if (!args.query && !args.sourceFile && !args.layer && !args.type)
|
|
29
|
+
throw new CLIError("At least one of query, sourceFile, layer, or type must be provided", ErrorCategory.USER, ['Use "model_list" to list all elements without filtering']);
|
|
30
|
+
if (args.layer && !isValidLayer(args.layer)) {
|
|
31
|
+
const validLayers = getAllLayerIds(), similar = findSimilar(args.layer, validLayers, 3), suggestions = [`Use a valid layer name: ${formatValidOptions(validLayers)}`];
|
|
32
|
+
throw similar.length > 0 && suggestions.unshift(`Did you mean: ${similar.join(" or ")}?`), new CLIError(`Unknown layer "${args.layer}"`, ErrorCategory.USER, suggestions);
|
|
33
|
+
}
|
|
34
|
+
if (args.type) {
|
|
35
|
+
const candidateTypes = args.layer ? Array.from(NODE_TYPES.values()).filter((t) => t.layer === args.layer) : Array.from(NODE_TYPES.values());
|
|
36
|
+
if (!candidateTypes.some((t) => t.type === args.type)) {
|
|
37
|
+
const validTypeNames = Array.from(new Set(candidateTypes.map((t) => t.type))).sort(), similar = findSimilar(args.type, validTypeNames, 3), suggestions = [
|
|
38
|
+
args.layer ? `Valid types for ${args.layer}: ${formatValidOptions(validTypeNames)}` : `Valid types: ${formatValidOptions(validTypeNames)}`
|
|
39
|
+
];
|
|
40
|
+
throw similar.length > 0 && suggestions.unshift(`Did you mean: ${similar.join(" or ")}?`), new CLIError(`Unknown element type "${args.type}"${args.layer ? ` for layer "${args.layer}"` : ""}`, ErrorCategory.USER, suggestions);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const model = await loadModel(args.rootPath), isSourceFileSearch = !!args.sourceFile, queryLower = (args.query ?? "").toLowerCase(), results = [];
|
|
44
|
+
for (const layerName of model.getLayerNames()) {
|
|
45
|
+
if (args.layer && layerName !== args.layer)
|
|
46
|
+
continue;
|
|
47
|
+
const layerObj = await model.getLayer(layerName);
|
|
48
|
+
if (layerObj)
|
|
49
|
+
for (const element of layerObj.listElements()) {
|
|
50
|
+
if (args.type && element.type !== args.type)
|
|
51
|
+
continue;
|
|
52
|
+
if (isSourceFileSearch) {
|
|
53
|
+
if (!matchesSourceFile(element, args.sourceFile))
|
|
54
|
+
continue;
|
|
55
|
+
} else {
|
|
56
|
+
const idMatch = (element.path || element.id).toLowerCase().includes(queryLower), nameMatch = element.name.toLowerCase().includes(queryLower);
|
|
57
|
+
if (!idMatch && !nameMatch)
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
let sourceFile, sourceSymbol;
|
|
61
|
+
const sourceRef = element.getSourceReference();
|
|
62
|
+
sourceRef?.locations && sourceRef.locations.length > 0 && (sourceFile = sourceRef.locations[0].file, sourceSymbol = sourceRef.locations[0].symbol), results.push({
|
|
63
|
+
layer: layerName,
|
|
64
|
+
id: element.path || element.id,
|
|
65
|
+
type: element.type,
|
|
66
|
+
name: element.name,
|
|
67
|
+
description: element.description,
|
|
68
|
+
sourceFile,
|
|
69
|
+
sourceSymbol
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const active = await activeStagedChangeset(new StagingAreaManager(model.rootPath, model));
|
|
74
|
+
if (active) {
|
|
75
|
+
const graph = await ChangesetGraph.build(model, active);
|
|
76
|
+
return jsonResult({
|
|
77
|
+
query: args.query ?? "",
|
|
78
|
+
sourceFile: args.sourceFile,
|
|
79
|
+
changeset: { id: active.id, name: active.name },
|
|
80
|
+
count: results.length,
|
|
81
|
+
results: results.map((r) => ({ ...r, changeStatus: graph.getElement(r.id)?.status ?? "unchanged" }))
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return jsonResult({
|
|
85
|
+
query: args.query ?? "",
|
|
86
|
+
sourceFile: args.sourceFile,
|
|
87
|
+
count: results.length,
|
|
88
|
+
results
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const modelSearchTool = {
|
|
93
|
+
name: "model_search",
|
|
94
|
+
description: "Search for elements across the model by name/ID substring or by source file reference.",
|
|
95
|
+
inputSchema,
|
|
96
|
+
handler: modelSearchHandler
|
|
97
|
+
};
|
|
98
|
+
export {
|
|
99
|
+
modelSearchHandler,
|
|
100
|
+
modelSearchTool
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=model-search.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/model-search.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * model_search \u2014 search for elements across the model by name/ID substring or\n * source file reference. Mirrors `dr search`.\n */\n\nimport { z } from \"zod\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Element } from \"../../core/element.js\";\nimport { ChangesetGraph } from \"../../core/changeset-graph.js\";\nimport { StagingAreaManager } from \"../../core/staging-area.js\";\nimport { activeStagedChangeset } from \"./changeset-shared.js\";\nimport { getAllLayerIds, isValidLayer } from \"../../generated/layer-registry.js\";\nimport { NODE_TYPES } from \"../../generated/node-types.js\";\nimport { CLIError, ErrorCategory, findSimilar, formatValidOptions } from \"../../utils/errors.js\";\nimport { jsonResult, loadModel, rootPathSchema, runTool, type McpToolDefinition } from \"./shared.js\";\n\nconst inputSchema = {\n query: z\n .string()\n .optional()\n .describe(\"Text to match against element ID and name (case-insensitive substring). Ignored when sourceFile is set.\"),\n layer: z.string().optional().describe(\"Restrict search to a single layer.\"),\n type: z.string().optional().describe(\"Restrict search to a single element type.\"),\n sourceFile: z.string().optional().describe(\"Find elements whose source_reference points at this file path.\"),\n rootPath: rootPathSchema,\n};\n\nexport type ModelSearchArgs = z.infer<z.ZodObject<typeof inputSchema>>;\n\nfunction normalizePath(filePath: string): string {\n return filePath.replace(/^\\.\\//, \"\").replace(/\\\\/g, \"/\");\n}\n\nfunction matchesSourceFile(element: Element, sourceFilePath: string): boolean {\n const sourceRef = element.getSourceReference();\n if (!sourceRef) return false;\n\n const normalizedQuery = normalizePath(sourceFilePath);\n return (sourceRef.locations ?? []).some((loc) => normalizePath(loc.file) === normalizedQuery);\n}\n\nexport async function modelSearchHandler(args: ModelSearchArgs): Promise<CallToolResult> {\n return runTool(async () => {\n if (!args.query && !args.sourceFile && !args.layer && !args.type) {\n throw new CLIError(\n \"At least one of query, sourceFile, layer, or type must be provided\",\n ErrorCategory.USER,\n ['Use \"model_list\" to list all elements without filtering']\n );\n }\n\n if (args.layer && !isValidLayer(args.layer)) {\n const validLayers = getAllLayerIds();\n const similar = findSimilar(args.layer, validLayers, 3);\n const suggestions: string[] = [`Use a valid layer name: ${formatValidOptions(validLayers)}`];\n if (similar.length > 0) suggestions.unshift(`Did you mean: ${similar.join(\" or \")}?`);\n throw new CLIError(`Unknown layer \"${args.layer}\"`, ErrorCategory.USER, suggestions);\n }\n\n if (args.type) {\n const candidateTypes = args.layer\n ? Array.from(NODE_TYPES.values()).filter((t) => t.layer === args.layer)\n : Array.from(NODE_TYPES.values());\n const isKnownType = candidateTypes.some((t) => t.type === args.type);\n if (!isKnownType) {\n const validTypeNames = Array.from(new Set(candidateTypes.map((t) => t.type))).sort();\n const similar = findSimilar(args.type, validTypeNames, 3);\n const suggestions: string[] = [\n args.layer\n ? `Valid types for ${args.layer}: ${formatValidOptions(validTypeNames)}`\n : `Valid types: ${formatValidOptions(validTypeNames)}`,\n ];\n if (similar.length > 0) suggestions.unshift(`Did you mean: ${similar.join(\" or \")}?`);\n throw new CLIError(\n `Unknown element type \"${args.type}\"${args.layer ? ` for layer \"${args.layer}\"` : \"\"}`,\n ErrorCategory.USER,\n suggestions\n );\n }\n }\n\n const model = await loadModel(args.rootPath);\n const isSourceFileSearch = !!args.sourceFile;\n const queryLower = (args.query ?? \"\").toLowerCase();\n\n const results: Array<{\n layer: string;\n id: string;\n type: string;\n name: string;\n description?: string;\n sourceFile?: string;\n sourceSymbol?: string;\n }> = [];\n\n for (const layerName of model.getLayerNames()) {\n if (args.layer && layerName !== args.layer) continue;\n\n const layerObj = await model.getLayer(layerName);\n if (!layerObj) continue;\n\n for (const element of layerObj.listElements()) {\n if (args.type && element.type !== args.type) continue;\n\n if (isSourceFileSearch) {\n if (!matchesSourceFile(element, args.sourceFile!)) continue;\n } else {\n const displayId = element.path || element.id;\n const idMatch = displayId.toLowerCase().includes(queryLower);\n const nameMatch = element.name.toLowerCase().includes(queryLower);\n if (!idMatch && !nameMatch) continue;\n }\n\n let sourceFile: string | undefined;\n let sourceSymbol: string | undefined;\n const sourceRef = element.getSourceReference();\n if (sourceRef?.locations && sourceRef.locations.length > 0) {\n sourceFile = sourceRef.locations[0].file;\n sourceSymbol = sourceRef.locations[0].symbol;\n }\n\n results.push({\n layer: layerName,\n id: element.path || element.id,\n type: element.type,\n name: element.name,\n description: element.description,\n sourceFile,\n sourceSymbol,\n });\n }\n }\n\n // Inside an active changeset, say which results the changeset adds or updates.\n const active = await activeStagedChangeset(new StagingAreaManager(model.rootPath, model));\n if (active) {\n const graph = await ChangesetGraph.build(model, active);\n return jsonResult({\n query: args.query ?? \"\",\n sourceFile: args.sourceFile,\n changeset: { id: active.id, name: active.name },\n count: results.length,\n results: results.map((r) => ({ ...r, changeStatus: graph.getElement(r.id)?.status ?? \"unchanged\" })),\n });\n }\n\n return jsonResult({\n query: args.query ?? \"\",\n sourceFile: args.sourceFile,\n count: results.length,\n results,\n });\n });\n}\n\nexport const modelSearchTool: McpToolDefinition<typeof inputSchema> = {\n name: \"model_search\",\n description: \"Search for elements across the model by name/ID substring or by source file reference.\",\n inputSchema,\n handler: modelSearchHandler,\n};\n"],
|
|
5
|
+
"mappings": "AAKA,SAAS,SAAS;AAGlB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,gBAAgB,oBAAoB;AAC7C,SAAS,kBAAkB;AAC3B,SAAS,UAAU,eAAe,aAAa,0BAA0B;AACzE,SAAS,YAAY,WAAW,gBAAgB,eAAuC;AAEvF,MAAM,cAAc;EAClB,OAAO,EACJ,OAAM,EACN,SAAQ,EACR,SAAS,yGAAyG;EACrH,OAAO,EAAE,OAAM,EAAG,SAAQ,EAAG,SAAS,oCAAoC;EAC1E,MAAM,EAAE,OAAM,EAAG,SAAQ,EAAG,SAAS,2CAA2C;EAChF,YAAY,EAAE,OAAM,EAAG,SAAQ,EAAG,SAAS,gEAAgE;EAC3G,UAAU;;AAKZ,SAAS,cAAc,UAAgB;AACrC,SAAO,SAAS,QAAQ,SAAS,EAAE,EAAE,QAAQ,OAAO,GAAG;AACzD;AAEA,SAAS,kBAAkB,SAAkB,gBAAsB;AACjE,QAAM,YAAY,QAAQ,mBAAkB;AAC5C,MAAI,CAAC;AAAW,WAAO;AAEvB,QAAM,kBAAkB,cAAc,cAAc;AACpD,UAAQ,UAAU,aAAa,CAAA,GAAI,KAAK,CAAC,QAAQ,cAAc,IAAI,IAAI,MAAM,eAAe;AAC9F;AAEA,eAAsB,mBAAmB,MAAqB;AAC5D,SAAO,QAAQ,YAAW;AACxB,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,cAAc,CAAC,KAAK,SAAS,CAAC,KAAK;AAC1D,YAAM,IAAI,SACR,sEACA,cAAc,MACd,CAAC,yDAAyD,CAAC;AAI/D,QAAI,KAAK,SAAS,CAAC,aAAa,KAAK,KAAK,GAAG;AAC3C,YAAM,cAAc,eAAc,GAC5B,UAAU,YAAY,KAAK,OAAO,aAAa,CAAC,GAChD,cAAwB,CAAC,2BAA2B,mBAAmB,WAAW,CAAC,EAAE;AAC3F,YAAI,QAAQ,SAAS,KAAG,YAAY,QAAQ,iBAAiB,QAAQ,KAAK,MAAM,CAAC,GAAG,GAC9E,IAAI,SAAS,kBAAkB,KAAK,KAAK,KAAK,cAAc,MAAM,WAAW;IACrF;AAEA,QAAI,KAAK,MAAM;AACb,YAAM,iBAAiB,KAAK,QACxB,MAAM,KAAK,WAAW,OAAM,CAAE,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,KAAK,IACpE,MAAM,KAAK,WAAW,OAAM,CAAE;AAElC,UAAI,CADgB,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,GACjD;AAChB,cAAM,iBAAiB,MAAM,KAAK,IAAI,IAAI,eAAe,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,KAAI,GAC5E,UAAU,YAAY,KAAK,MAAM,gBAAgB,CAAC,GAClD,cAAwB;UAC5B,KAAK,QACD,mBAAmB,KAAK,KAAK,KAAK,mBAAmB,cAAc,CAAC,KACpE,gBAAgB,mBAAmB,cAAc,CAAC;;AAExD,cAAI,QAAQ,SAAS,KAAG,YAAY,QAAQ,iBAAiB,QAAQ,KAAK,MAAM,CAAC,GAAG,GAC9E,IAAI,SACR,yBAAyB,KAAK,IAAI,IAAI,KAAK,QAAQ,eAAe,KAAK,KAAK,MAAM,EAAE,IACpF,cAAc,MACd,WAAW;MAEf;IACF;AAEA,UAAM,QAAQ,MAAM,UAAU,KAAK,QAAQ,GACrC,qBAAqB,CAAC,CAAC,KAAK,YAC5B,cAAc,KAAK,SAAS,IAAI,YAAW,GAE3C,UAQD,CAAA;AAEL,eAAW,aAAa,MAAM,cAAa,GAAI;AAC7C,UAAI,KAAK,SAAS,cAAc,KAAK;AAAO;AAE5C,YAAM,WAAW,MAAM,MAAM,SAAS,SAAS;AAC/C,UAAK;AAEL,mBAAW,WAAW,SAAS,aAAY,GAAI;AAC7C,cAAI,KAAK,QAAQ,QAAQ,SAAS,KAAK;AAAM;AAE7C,cAAI;AACF,gBAAI,CAAC,kBAAkB,SAAS,KAAK,UAAW;AAAG;iBAC9C;AAEL,kBAAM,WADY,QAAQ,QAAQ,QAAQ,IAChB,YAAW,EAAG,SAAS,UAAU,GACrD,YAAY,QAAQ,KAAK,YAAW,EAAG,SAAS,UAAU;AAChE,gBAAI,CAAC,WAAW,CAAC;AAAW;UAC9B;AAEA,cAAI,YACA;AACJ,gBAAM,YAAY,QAAQ,mBAAkB;AAC5C,UAAI,WAAW,aAAa,UAAU,UAAU,SAAS,MACvD,aAAa,UAAU,UAAU,CAAC,EAAE,MACpC,eAAe,UAAU,UAAU,CAAC,EAAE,SAGxC,QAAQ,KAAK;YACX,OAAO;YACP,IAAI,QAAQ,QAAQ,QAAQ;YAC5B,MAAM,QAAQ;YACd,MAAM,QAAQ;YACd,aAAa,QAAQ;YACrB;YACA;WACD;QACH;IACF;AAGA,UAAM,SAAS,MAAM,sBAAsB,IAAI,mBAAmB,MAAM,UAAU,KAAK,CAAC;AACxF,QAAI,QAAQ;AACV,YAAM,QAAQ,MAAM,eAAe,MAAM,OAAO,MAAM;AACtD,aAAO,WAAW;QAChB,OAAO,KAAK,SAAS;QACrB,YAAY,KAAK;QACjB,WAAW,EAAE,IAAI,OAAO,IAAI,MAAM,OAAO,KAAI;QAC7C,OAAO,QAAQ;QACf,SAAS,QAAQ,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,cAAc,MAAM,WAAW,EAAE,EAAE,GAAG,UAAU,YAAW,EAAG;OACpG;IACH;AAEA,WAAO,WAAW;MAChB,OAAO,KAAK,SAAS;MACrB,YAAY,KAAK;MACjB,OAAO,QAAQ;MACf;KACD;EACH,CAAC;AACH;AAEO,MAAM,kBAAyD;EACpE,MAAM;EACN,aAAa;EACb;EACA,SAAS;;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ChangesetGraph } from "../../core/changeset-graph.js";
|
|
3
|
+
import { StagingAreaManager } from "../../core/staging-area.js";
|
|
4
|
+
import { findElementLayer } from "../../utils/element-utils.js";
|
|
5
|
+
import { activeStagedChangeset } from "./changeset-shared.js";
|
|
6
|
+
import { CLIError, ErrorCategory } from "../../utils/errors.js";
|
|
7
|
+
import { jsonResult, loadModel, rootPathSchema, runTool } from "./shared.js";
|
|
8
|
+
const inputSchema = {
|
|
9
|
+
id: z.string().describe("Element ID or path (e.g. 'api.operation.create-order')."),
|
|
10
|
+
rootPath: rootPathSchema
|
|
11
|
+
};
|
|
12
|
+
async function modelShowHandler(args) {
|
|
13
|
+
return runTool(async () => {
|
|
14
|
+
const model = await loadModel(args.rootPath), layerName = await findElementLayer(model, args.id);
|
|
15
|
+
if (!layerName)
|
|
16
|
+
throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [
|
|
17
|
+
'Use "model_search" to find similar elements',
|
|
18
|
+
'Use "model_list" to list all elements in a layer'
|
|
19
|
+
]);
|
|
20
|
+
const layer = await model.getLayer(layerName), element = layer?.getElement(args.id);
|
|
21
|
+
if (!layer || !element)
|
|
22
|
+
throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [
|
|
23
|
+
'Use "model_search" to find similar elements',
|
|
24
|
+
'Use "model_list" to list all elements in a layer'
|
|
25
|
+
]);
|
|
26
|
+
const elementId = element.path || element.id, active = await activeStagedChangeset(new StagingAreaManager(model.rootPath, model));
|
|
27
|
+
if (active) {
|
|
28
|
+
const graph = await ChangesetGraph.build(model, active), graphElement = graph.getElement(elementId), neighbors = graph.neighbors(elementId, { includeDeleted: !0 }), row = (n) => {
|
|
29
|
+
const rel = n.relationship;
|
|
30
|
+
return {
|
|
31
|
+
source: rel.source,
|
|
32
|
+
predicate: rel.predicate,
|
|
33
|
+
target: rel.target,
|
|
34
|
+
layer: rel.layer,
|
|
35
|
+
...rel.targetLayer ? { targetLayer: rel.targetLayer } : {},
|
|
36
|
+
...rel.properties ? { properties: rel.properties } : {},
|
|
37
|
+
status: rel.status
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
return jsonResult({
|
|
41
|
+
layer: layerName,
|
|
42
|
+
element: element.toJSON(),
|
|
43
|
+
changeset: { id: active.id, name: active.name, elementStatus: graphElement?.status ?? "unchanged" },
|
|
44
|
+
relationships: {
|
|
45
|
+
outgoing: neighbors.filter((n) => n.direction === "out").map(row),
|
|
46
|
+
incoming: neighbors.filter((n) => n.direction === "in").map(row)
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const { outgoing, incoming } = model.relationships.getForElement(elementId);
|
|
51
|
+
return jsonResult({
|
|
52
|
+
layer: layerName,
|
|
53
|
+
element: element.toJSON(),
|
|
54
|
+
relationships: { outgoing, incoming }
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const modelShowTool = {
|
|
59
|
+
name: "model_show",
|
|
60
|
+
description: "Show a single element's full details, attributes, and relationships. Inside an active changeset the relationships include staged ones, each with a status (added, deleted, unchanged).",
|
|
61
|
+
inputSchema,
|
|
62
|
+
handler: modelShowHandler
|
|
63
|
+
};
|
|
64
|
+
export {
|
|
65
|
+
modelShowHandler,
|
|
66
|
+
modelShowTool
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=model-show.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/model-show.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * model_show \u2014 show a single element with its relationships. Mirrors `dr show`.\n */\n\nimport { z } from \"zod\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { ChangesetGraph } from \"../../core/changeset-graph.js\";\nimport { StagingAreaManager } from \"../../core/staging-area.js\";\nimport { findElementLayer } from \"../../utils/element-utils.js\";\nimport { activeStagedChangeset } from \"./changeset-shared.js\";\nimport { CLIError, ErrorCategory } from \"../../utils/errors.js\";\nimport { jsonResult, loadModel, rootPathSchema, runTool, type McpToolDefinition } from \"./shared.js\";\n\nconst inputSchema = {\n id: z.string().describe(\"Element ID or path (e.g. 'api.operation.create-order').\"),\n rootPath: rootPathSchema,\n};\n\nexport type ModelShowArgs = z.infer<z.ZodObject<typeof inputSchema>>;\n\nexport async function modelShowHandler(args: ModelShowArgs): Promise<CallToolResult> {\n return runTool(async () => {\n const model = await loadModel(args.rootPath);\n\n const layerName = await findElementLayer(model, args.id);\n if (!layerName) {\n throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [\n 'Use \"model_search\" to find similar elements',\n 'Use \"model_list\" to list all elements in a layer',\n ]);\n }\n\n const layer = await model.getLayer(layerName);\n const element = layer?.getElement(args.id);\n if (!layer || !element) {\n throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [\n 'Use \"model_search\" to find similar elements',\n 'Use \"model_list\" to list all elements in a layer',\n ]);\n }\n\n const elementId = element.path || element.id;\n\n // Inside an active changeset, show the element's relationships as the changeset leaves them:\n // staged additions included, staged removals marked, each with its change status.\n const active = await activeStagedChangeset(new StagingAreaManager(model.rootPath, model));\n if (active) {\n const graph = await ChangesetGraph.build(model, active);\n const graphElement = graph.getElement(elementId);\n const neighbors = graph.neighbors(elementId, { includeDeleted: true });\n const row = (n: (typeof neighbors)[number]) => {\n const rel = n.relationship;\n return {\n source: rel.source,\n predicate: rel.predicate,\n target: rel.target,\n layer: rel.layer,\n ...(rel.targetLayer ? { targetLayer: rel.targetLayer } : {}),\n ...(rel.properties ? { properties: rel.properties } : {}),\n status: rel.status,\n };\n };\n return jsonResult({\n layer: layerName,\n element: element.toJSON(),\n changeset: { id: active.id, name: active.name, elementStatus: graphElement?.status ?? \"unchanged\" },\n relationships: {\n outgoing: neighbors.filter((n) => n.direction === \"out\").map(row),\n incoming: neighbors.filter((n) => n.direction === \"in\").map(row),\n },\n });\n }\n\n const { outgoing, incoming } = model.relationships.getForElement(elementId);\n\n return jsonResult({\n layer: layerName,\n element: element.toJSON(),\n relationships: { outgoing, incoming },\n });\n });\n}\n\nexport const modelShowTool: McpToolDefinition<typeof inputSchema> = {\n name: \"model_show\",\n description:\n \"Show a single element's full details, attributes, and relationships. Inside an active changeset the \" +\n \"relationships include staged ones, each with a status (added, deleted, unchanged).\",\n inputSchema,\n handler: modelShowHandler,\n};\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,SAAS;AAElB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,6BAA6B;AACtC,SAAS,UAAU,qBAAqB;AACxC,SAAS,YAAY,WAAW,gBAAgB,eAAuC;AAEvF,MAAM,cAAc;EAClB,IAAI,EAAE,OAAM,EAAG,SAAS,yDAAyD;EACjF,UAAU;;AAKZ,eAAsB,iBAAiB,MAAmB;AACxD,SAAO,QAAQ,YAAW;AACxB,UAAM,QAAQ,MAAM,UAAU,KAAK,QAAQ,GAErC,YAAY,MAAM,iBAAiB,OAAO,KAAK,EAAE;AACvD,QAAI,CAAC;AACH,YAAM,IAAI,SAAS,WAAW,KAAK,EAAE,cAAc,cAAc,WAAW;QAC1E;QACA;OACD;AAGH,UAAM,QAAQ,MAAM,MAAM,SAAS,SAAS,GACtC,UAAU,OAAO,WAAW,KAAK,EAAE;AACzC,QAAI,CAAC,SAAS,CAAC;AACb,YAAM,IAAI,SAAS,WAAW,KAAK,EAAE,cAAc,cAAc,WAAW;QAC1E;QACA;OACD;AAGH,UAAM,YAAY,QAAQ,QAAQ,QAAQ,IAIpC,SAAS,MAAM,sBAAsB,IAAI,mBAAmB,MAAM,UAAU,KAAK,CAAC;AACxF,QAAI,QAAQ;AACV,YAAM,QAAQ,MAAM,eAAe,MAAM,OAAO,MAAM,GAChD,eAAe,MAAM,WAAW,SAAS,GACzC,YAAY,MAAM,UAAU,WAAW,EAAE,gBAAgB,GAAI,CAAE,GAC/D,MAAM,CAAC,MAAiC;AAC5C,cAAM,MAAM,EAAE;AACd,eAAO;UACL,QAAQ,IAAI;UACZ,WAAW,IAAI;UACf,QAAQ,IAAI;UACZ,OAAO,IAAI;UACX,GAAI,IAAI,cAAc,EAAE,aAAa,IAAI,YAAW,IAAK,CAAA;UACzD,GAAI,IAAI,aAAa,EAAE,YAAY,IAAI,WAAU,IAAK,CAAA;UACtD,QAAQ,IAAI;;MAEhB;AACA,aAAO,WAAW;QAChB,OAAO;QACP,SAAS,QAAQ,OAAM;QACvB,WAAW,EAAE,IAAI,OAAO,IAAI,MAAM,OAAO,MAAM,eAAe,cAAc,UAAU,YAAW;QACjG,eAAe;UACb,UAAU,UAAU,OAAO,CAAC,MAAM,EAAE,cAAc,KAAK,EAAE,IAAI,GAAG;UAChE,UAAU,UAAU,OAAO,CAAC,MAAM,EAAE,cAAc,IAAI,EAAE,IAAI,GAAG;;OAElE;IACH;AAEA,UAAM,EAAE,UAAU,SAAQ,IAAK,MAAM,cAAc,cAAc,SAAS;AAE1E,WAAO,WAAW;MAChB,OAAO;MACP,SAAS,QAAQ,OAAM;MACvB,eAAe,EAAE,UAAU,SAAQ;KACpC;EACH,CAAC;AACH;AAEO,MAAM,gBAAuD;EAClE,MAAM;EACN,aACE;EAEF;EACA,SAAS;;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StatsCollector } from "../../core/stats-collector.js";
|
|
2
|
+
import { jsonResult, loadModel, rootPathSchema, runTool } from "./shared.js";
|
|
3
|
+
const inputSchema = {
|
|
4
|
+
rootPath: rootPathSchema
|
|
5
|
+
};
|
|
6
|
+
async function modelStatsHandler(args) {
|
|
7
|
+
return runTool(async () => {
|
|
8
|
+
const model = await loadModel(args.rootPath), stats = await new StatsCollector(model).collect();
|
|
9
|
+
return jsonResult(stats);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
const modelStatsTool = {
|
|
13
|
+
name: "model_stats",
|
|
14
|
+
description: "Get aggregate statistics about the architecture model: element counts, relationships, coverage, and orphans.",
|
|
15
|
+
inputSchema,
|
|
16
|
+
handler: modelStatsHandler
|
|
17
|
+
};
|
|
18
|
+
export {
|
|
19
|
+
modelStatsHandler,
|
|
20
|
+
modelStatsTool
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=model-stats.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/model-stats.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * model_stats \u2014 aggregate statistics about the architecture model. Mirrors `dr stats`.\n */\n\nimport type { z } from \"zod\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { StatsCollector } from \"../../core/stats-collector.js\";\nimport { jsonResult, loadModel, rootPathSchema, runTool, type McpToolDefinition } from \"./shared.js\";\n\nconst inputSchema = {\n rootPath: rootPathSchema,\n};\n\nexport type ModelStatsArgs = z.infer<z.ZodObject<typeof inputSchema>>;\n\nexport async function modelStatsHandler(args: ModelStatsArgs): Promise<CallToolResult> {\n return runTool(async () => {\n const model = await loadModel(args.rootPath);\n const collector = new StatsCollector(model);\n const stats = await collector.collect();\n return jsonResult(stats);\n });\n}\n\nexport const modelStatsTool: McpToolDefinition<typeof inputSchema> = {\n name: \"model_stats\",\n description: \"Get aggregate statistics about the architecture model: element counts, relationships, coverage, and orphans.\",\n inputSchema,\n handler: modelStatsHandler,\n};\n"],
|
|
5
|
+
"mappings": "AAMA,SAAS,sBAAsB;AAC/B,SAAS,YAAY,WAAW,gBAAgB,eAAuC;AAEvF,MAAM,cAAc;EAClB,UAAU;;AAKZ,eAAsB,kBAAkB,MAAoB;AAC1D,SAAO,QAAQ,YAAW;AACxB,UAAM,QAAQ,MAAM,UAAU,KAAK,QAAQ,GAErC,QAAQ,MADI,IAAI,eAAe,KAAK,EACZ,QAAO;AACrC,WAAO,WAAW,KAAK;EACzB,CAAC;AACH;AAEO,MAAM,iBAAwD;EACnE,MAAM;EACN,aAAa;EACb;EACA,SAAS;;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ChangesetGraph } from "../../core/changeset-graph.js";
|
|
3
|
+
import { DependencyTracker, TraceDirection } from "../../core/dependency-tracker.js";
|
|
4
|
+
import { ReferenceRegistry } from "../../core/reference-registry.js";
|
|
5
|
+
import { StagingAreaManager } from "../../core/staging-area.js";
|
|
6
|
+
import { findElementLayer } from "../../utils/element-utils.js";
|
|
7
|
+
import { CLIError, ErrorCategory, getErrorMessage } from "../../utils/errors.js";
|
|
8
|
+
import { jsonResult, loadModel, rootPathSchema, runTool } from "./shared.js";
|
|
9
|
+
import { activeStagedChangeset, resolveChangeset } from "./changeset-shared.js";
|
|
10
|
+
const inputSchema = {
|
|
11
|
+
id: z.string().describe("Element ID or path to trace."),
|
|
12
|
+
direction: z.enum(["up", "down", "both"]).optional().describe("'up' = what depends on this element (dependents), 'down' = what this element depends on (dependencies), 'both' (default)."),
|
|
13
|
+
depth: z.number().int().positive().optional().describe("Maximum trace depth. Omit for unlimited."),
|
|
14
|
+
changesetId: z.string().optional().describe("Trace through this changeset's projected graph (ID or name). Defaults to the active changeset, if any."),
|
|
15
|
+
base: z.boolean().optional().describe("Trace the committed base model only, ignoring any active changeset."),
|
|
16
|
+
rootPath: rootPathSchema
|
|
17
|
+
};
|
|
18
|
+
async function modelTraceHandler(args) {
|
|
19
|
+
return runTool(async () => {
|
|
20
|
+
const model = await loadModel(args.rootPath);
|
|
21
|
+
if (args.base && args.changesetId)
|
|
22
|
+
throw new CLIError('Pass either "base" or "changesetId", not both', ErrorCategory.USER);
|
|
23
|
+
const manager = new StagingAreaManager(model.rootPath, model), changeset = args.base ? null : args.changesetId ? await resolveChangeset(manager, args.changesetId) : await activeStagedChangeset(manager);
|
|
24
|
+
return jsonResult(changeset ? await traceChangeset(model, changeset, args) : await traceBase(model, args));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async function traceBase(model, args) {
|
|
28
|
+
const layerName = await findElementLayer(model, args.id);
|
|
29
|
+
if (!layerName)
|
|
30
|
+
throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [
|
|
31
|
+
'Use "model_search" to find similar elements'
|
|
32
|
+
]);
|
|
33
|
+
const graph = referenceGraph(model.layers.values());
|
|
34
|
+
for (const rel of model.getBaseRelationships())
|
|
35
|
+
addEdge(graph, rel.source, rel.target, rel.predicate);
|
|
36
|
+
return { id: args.id, layer: layerName, ...runTrace(graph, model, args.id, args) };
|
|
37
|
+
}
|
|
38
|
+
async function traceChangeset(model, changeset, args) {
|
|
39
|
+
const projected = await ChangesetGraph.build(model, changeset), root = projected.getElement(args.id);
|
|
40
|
+
if (!root)
|
|
41
|
+
throw new CLIError(`Element ${args.id} not found in changeset '${changeset.name}' or the base model`, ErrorCategory.NOT_FOUND, [
|
|
42
|
+
'Use "model_search" to find similar elements',
|
|
43
|
+
'Pass "base": true to trace the committed model only'
|
|
44
|
+
]);
|
|
45
|
+
const warnings = [], followDeleted = root.status === "deleted";
|
|
46
|
+
followDeleted && warnings.push(`${root.id} is deleted by this changeset; the trace follows the relationships it has today`);
|
|
47
|
+
const layers = await projectedLayers(model, changeset, warnings), graph = referenceGraph(layers);
|
|
48
|
+
for (const rel of projected.relationships())
|
|
49
|
+
rel.status === "deleted" && !(followDeleted && (rel.source === root.id || rel.target === root.id)) || addEdge(graph, rel.source, rel.target, rel.predicate);
|
|
50
|
+
const trace = runTrace(graph, model, root.id, args), reached = /* @__PURE__ */ new Set([root.id]);
|
|
51
|
+
for (const side of [trace.dependents, trace.dependencies])
|
|
52
|
+
for (const id of [...side?.direct ?? [], ...side?.transitive ?? []])
|
|
53
|
+
reached.add(id);
|
|
54
|
+
const changedElements = {};
|
|
55
|
+
for (const id of reached) {
|
|
56
|
+
const status = projected.getElement(id)?.status;
|
|
57
|
+
status && status !== "unchanged" && (changedElements[id] = status);
|
|
58
|
+
}
|
|
59
|
+
const changedEdges = projected.changedRelationships().filter((rel) => reached.has(rel.source) || reached.has(rel.target)).map((rel) => ({ source: rel.source, predicate: rel.predicate, target: rel.target, status: rel.status }));
|
|
60
|
+
return {
|
|
61
|
+
id: root.id,
|
|
62
|
+
layer: root.layer,
|
|
63
|
+
...trace,
|
|
64
|
+
changeset: { id: changeset.id, name: changeset.name, elementStatus: root.status },
|
|
65
|
+
changedElements,
|
|
66
|
+
changedEdges,
|
|
67
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function projectedLayers(model, changeset, warnings) {
|
|
71
|
+
const names = new Set(model.getLayerNames());
|
|
72
|
+
for (const change of changeset.changes)
|
|
73
|
+
change.type.startsWith("relationship-") || names.add(change.layerName);
|
|
74
|
+
const isActive = model.getActiveChangesetId() === changeset.id, layers = [];
|
|
75
|
+
for (const name of names)
|
|
76
|
+
try {
|
|
77
|
+
const layer = isActive ? await model.getLayer(name) : await model.getVirtualProjectionEngine().projectLayer(model, changeset.id, name);
|
|
78
|
+
layer && layers.push(layer);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
warnings.push(`Layer ${name} could not be projected (${getErrorMessage(error)}); its base references were used`);
|
|
81
|
+
const base = await model.getBaseLayer(name);
|
|
82
|
+
base && layers.push(base);
|
|
83
|
+
}
|
|
84
|
+
return layers;
|
|
85
|
+
}
|
|
86
|
+
function referenceGraph(layers) {
|
|
87
|
+
const registry = new ReferenceRegistry();
|
|
88
|
+
for (const layer of layers)
|
|
89
|
+
for (const element of layer.listElements())
|
|
90
|
+
registry.registerElement(element);
|
|
91
|
+
return registry.getDependencyGraph();
|
|
92
|
+
}
|
|
93
|
+
function addEdge(graph, source, target, predicate) {
|
|
94
|
+
graph.hasNode(source) || graph.addNode(source), graph.hasNode(target) || graph.addNode(target), graph.hasEdge(source, target) || graph.addEdge(source, target, { type: predicate });
|
|
95
|
+
}
|
|
96
|
+
function runTrace(graph, model, id, args) {
|
|
97
|
+
const tracker = new DependencyTracker(graph, model), direction = args.direction ?? "both", maxDepth = args.depth ?? null, result = { direction };
|
|
98
|
+
if (direction === "up" || direction === "both") {
|
|
99
|
+
const direct = tracker.traceDependencies(id, TraceDirection.DOWN, 1), transitiveAll = tracker.traceDependencies(id, TraceDirection.DOWN, maxDepth);
|
|
100
|
+
result.dependents = { direct, transitive: transitiveAll.filter((d) => !direct.includes(d)) };
|
|
101
|
+
}
|
|
102
|
+
if (direction === "down" || direction === "both") {
|
|
103
|
+
const direct = tracker.traceDependencies(id, TraceDirection.UP, 1), transitiveAll = tracker.traceDependencies(id, TraceDirection.UP, maxDepth);
|
|
104
|
+
result.dependencies = { direct, transitive: transitiveAll.filter((d) => !direct.includes(d)) };
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
const modelTraceTool = {
|
|
109
|
+
name: "model_trace",
|
|
110
|
+
description: "Trace dependents and/or dependencies of an element across cross-layer references and relationships. With an active changeset (or changesetId) it traces the changeset's projected graph: staged relationships are followed, staged removals are not, and changedEdges / changedElements mark what the changeset adds or deletes.",
|
|
111
|
+
inputSchema,
|
|
112
|
+
handler: modelTraceHandler
|
|
113
|
+
};
|
|
114
|
+
export {
|
|
115
|
+
modelTraceHandler,
|
|
116
|
+
modelTraceTool
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=model-trace.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/model-trace.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * model_trace \u2014 dependency trace for an element. Mirrors `dr trace`.\n *\n * Changeset-aware: when a changeset is active (or one is named with `changesetId`), the trace runs\n * over that changeset's projected graph (`ChangesetGraph`): relationships it stages are followed,\n * relationships it deletes are not, and the result lists the added and deleted edges that touch\n * the traced elements. Pass `base: true` to trace the committed model only.\n */\n\nimport { z } from \"zod\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type Graph from \"graphology\";\nimport { ChangesetGraph } from \"../../core/changeset-graph.js\";\nimport type { Changeset } from \"../../core/changeset.js\";\nimport { DependencyTracker, TraceDirection } from \"../../core/dependency-tracker.js\";\nimport type { Layer } from \"../../core/layer.js\";\nimport type { Model } from \"../../core/model.js\";\nimport { ReferenceRegistry } from \"../../core/reference-registry.js\";\nimport { StagingAreaManager } from \"../../core/staging-area.js\";\nimport { findElementLayer } from \"../../utils/element-utils.js\";\nimport { CLIError, ErrorCategory, getErrorMessage } from \"../../utils/errors.js\";\nimport { jsonResult, loadModel, rootPathSchema, runTool, type McpToolDefinition } from \"./shared.js\";\nimport { activeStagedChangeset, resolveChangeset } from \"./changeset-shared.js\";\n\nconst inputSchema = {\n id: z.string().describe(\"Element ID or path to trace.\"),\n direction: z\n .enum([\"up\", \"down\", \"both\"])\n .optional()\n .describe(\"'up' = what depends on this element (dependents), 'down' = what this element depends on (dependencies), 'both' (default).\"),\n depth: z.number().int().positive().optional().describe(\"Maximum trace depth. Omit for unlimited.\"),\n changesetId: z\n .string()\n .optional()\n .describe(\"Trace through this changeset's projected graph (ID or name). Defaults to the active changeset, if any.\"),\n base: z.boolean().optional().describe(\"Trace the committed base model only, ignoring any active changeset.\"),\n rootPath: rootPathSchema,\n};\n\nexport type ModelTraceArgs = z.infer<z.ZodObject<typeof inputSchema>>;\n\ninterface TraceResult {\n id: string;\n layer: string;\n direction: string;\n dependents?: { direct: string[]; transitive: string[] };\n dependencies?: { direct: string[]; transitive: string[] };\n changeset?: { id: string; name: string; elementStatus: string };\n changedElements?: Record<string, string>;\n changedEdges?: Array<{ source: string; predicate: string; target: string; status: string }>;\n warnings?: string[];\n}\n\nexport async function modelTraceHandler(args: ModelTraceArgs): Promise<CallToolResult> {\n return runTool(async () => {\n const model = await loadModel(args.rootPath);\n if (args.base && args.changesetId) {\n throw new CLIError('Pass either \"base\" or \"changesetId\", not both', ErrorCategory.USER);\n }\n\n const manager = new StagingAreaManager(model.rootPath, model);\n const changeset = args.base\n ? null\n : args.changesetId\n ? await resolveChangeset(manager, args.changesetId)\n : await activeStagedChangeset(manager);\n\n return jsonResult(changeset ? await traceChangeset(model, changeset, args) : await traceBase(model, args));\n });\n}\n\n/** The committed model: cross-layer references plus relationships.yaml. */\nasync function traceBase(model: Model, args: ModelTraceArgs): Promise<TraceResult> {\n const layerName = await findElementLayer(model, args.id);\n if (!layerName) {\n throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [\n 'Use \"model_search\" to find similar elements',\n ]);\n }\n\n // Build a reference registry from cross-layer references, then extend the\n // dependency graph with intra-layer relationships from relationships.yaml.\n const graph = referenceGraph(model.layers.values());\n for (const rel of model.getBaseRelationships()) {\n addEdge(graph, rel.source, rel.target, rel.predicate);\n }\n\n return { id: args.id, layer: layerName, ...runTrace(graph, model, args.id, args) };\n}\n\n/** A changeset's projection: its elements' references plus base and staged relationships, minus staged removals. */\nasync function traceChangeset(model: Model, changeset: Changeset, args: ModelTraceArgs): Promise<TraceResult> {\n const projected = await ChangesetGraph.build(model, changeset);\n const root = projected.getElement(args.id);\n if (!root) {\n throw new CLIError(`Element ${args.id} not found in changeset '${changeset.name}' or the base model`, ErrorCategory.NOT_FOUND, [\n 'Use \"model_search\" to find similar elements',\n 'Pass \"base\": true to trace the committed model only',\n ]);\n }\n\n const warnings: string[] = [];\n // Tracing an element the changeset deletes follows the edges it had: that is what the delete breaks.\n const followDeleted = root.status === \"deleted\";\n if (followDeleted) {\n warnings.push(`${root.id} is deleted by this changeset; the trace follows the relationships it has today`);\n }\n\n const layers = await projectedLayers(model, changeset, warnings);\n const graph = referenceGraph(layers);\n for (const rel of projected.relationships()) {\n // Only the root's own removed edges come back; removals elsewhere in the changeset stay removed.\n if (rel.status === \"deleted\" && !(followDeleted && (rel.source === root.id || rel.target === root.id))) continue;\n addEdge(graph, rel.source, rel.target, rel.predicate);\n }\n\n const trace = runTrace(graph, model, root.id, args);\n const reached = new Set<string>([root.id]);\n for (const side of [trace.dependents, trace.dependencies]) {\n for (const id of [...(side?.direct ?? []), ...(side?.transitive ?? [])]) reached.add(id);\n }\n\n const changedElements: Record<string, string> = {};\n for (const id of reached) {\n const status = projected.getElement(id)?.status;\n if (status && status !== \"unchanged\") changedElements[id] = status;\n }\n const changedEdges = projected\n .changedRelationships()\n .filter((rel) => reached.has(rel.source) || reached.has(rel.target))\n .map((rel) => ({ source: rel.source, predicate: rel.predicate, target: rel.target, status: rel.status }));\n\n return {\n id: root.id,\n layer: root.layer,\n ...trace,\n changeset: { id: changeset.id!, name: changeset.name, elementStatus: root.status },\n changedElements,\n changedEdges,\n ...(warnings.length > 0 ? { warnings } : {}),\n };\n}\n\n/**\n * The changeset's projected layers, for the cross-layer references its elements carry. A layer\n * that fails to project falls back to its base version, with a warning.\n */\nasync function projectedLayers(model: Model, changeset: Changeset, warnings: string[]): Promise<Layer[]> {\n const names = new Set<string>(model.getLayerNames());\n for (const change of changeset.changes) {\n if (!change.type.startsWith(\"relationship-\")) names.add(change.layerName);\n }\n const isActive = model.getActiveChangesetId() === changeset.id;\n const layers: Layer[] = [];\n for (const name of names) {\n try {\n const layer = isActive\n ? await model.getLayer(name)\n : await model.getVirtualProjectionEngine().projectLayer(model, changeset.id!, name);\n if (layer) layers.push(layer);\n } catch (error) {\n warnings.push(`Layer ${name} could not be projected (${getErrorMessage(error)}); its base references were used`);\n const base = await model.getBaseLayer(name);\n if (base) layers.push(base);\n }\n }\n return layers;\n}\n\nfunction referenceGraph(layers: Iterable<Layer>): Graph {\n const registry = new ReferenceRegistry();\n for (const layer of layers) {\n for (const element of layer.listElements()) {\n registry.registerElement(element);\n }\n }\n return registry.getDependencyGraph();\n}\n\nfunction addEdge(graph: Graph, source: string, target: string, predicate: string): void {\n if (!graph.hasNode(source)) graph.addNode(source);\n if (!graph.hasNode(target)) graph.addNode(target);\n if (!graph.hasEdge(source, target)) {\n graph.addEdge(source, target, { type: predicate });\n }\n}\n\nfunction runTrace(\n graph: Graph,\n model: Model,\n id: string,\n args: ModelTraceArgs\n): Pick<TraceResult, \"direction\" | \"dependents\" | \"dependencies\"> {\n const tracker = new DependencyTracker(graph, model);\n const direction = args.direction ?? \"both\";\n const maxDepth = args.depth ?? null;\n const result: Pick<TraceResult, \"direction\" | \"dependents\" | \"dependencies\"> = { direction };\n\n if (direction === \"up\" || direction === \"both\") {\n const direct = tracker.traceDependencies(id, TraceDirection.DOWN, 1);\n const transitiveAll = tracker.traceDependencies(id, TraceDirection.DOWN, maxDepth);\n result.dependents = { direct, transitive: transitiveAll.filter((d) => !direct.includes(d)) };\n }\n\n if (direction === \"down\" || direction === \"both\") {\n const direct = tracker.traceDependencies(id, TraceDirection.UP, 1);\n const transitiveAll = tracker.traceDependencies(id, TraceDirection.UP, maxDepth);\n result.dependencies = { direct, transitive: transitiveAll.filter((d) => !direct.includes(d)) };\n }\n\n return result;\n}\n\nexport const modelTraceTool: McpToolDefinition<typeof inputSchema> = {\n name: \"model_trace\",\n description:\n \"Trace dependents and/or dependencies of an element across cross-layer references and relationships. \" +\n \"With an active changeset (or changesetId) it traces the changeset's projected graph: staged relationships are \" +\n \"followed, staged removals are not, and changedEdges / changedElements mark what the changeset adds or deletes.\",\n inputSchema,\n handler: modelTraceHandler,\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Element } from "../../core/element.js";
|
|
3
|
+
import { Layer } from "../../core/layer.js";
|
|
4
|
+
import { MutationHandler } from "../../core/mutation-handler.js";
|
|
5
|
+
import { setStagedSourceReference } from "../../core/staged-source-reference.js";
|
|
6
|
+
import { getNodeTypesForLayer, isValidNodeType, normalizeNodeType } from "../../generated/node-types.js";
|
|
7
|
+
import { findElementLayer } from "../../utils/element-utils.js";
|
|
8
|
+
import { buildSourceReference, validateSourceReferenceOptions } from "../../utils/source-reference.js";
|
|
9
|
+
import { schemaLookupHint, undefinedTypeWarning } from "../../utils/error-hints.js";
|
|
10
|
+
import { SchemaValidator, suggestNodeTypes } from "../../validators/schema-validator.js";
|
|
11
|
+
import { CLIError, ErrorCategory, findSimilar, formatValidOptions } from "../../utils/errors.js";
|
|
12
|
+
import { effectiveAttributePatch, resolveAttributeUpdate } from "../../utils/attribute-patch.js";
|
|
13
|
+
import { jsonResult, loadModel, reloadModel, rootPathSchema, runTool } from "./shared.js";
|
|
14
|
+
const inputSchema = {
|
|
15
|
+
id: z.string().describe("Element ID or path to update."),
|
|
16
|
+
name: z.string().optional(),
|
|
17
|
+
description: z.string().optional(),
|
|
18
|
+
type: z.string().optional().describe("New element type within the current layer."),
|
|
19
|
+
attributes: z.record(z.string(), z.unknown()).optional().describe("Replaces the element's attributes, validated against its spec schema."),
|
|
20
|
+
attributePatch: z.record(z.string(), z.unknown()).optional().describe("Merged into the element's current attributes (as the active changeset leaves them), so required attributes need not be repeated; a null value removes that key. The merged result is validated against the spec schema. Cannot be combined with attributes."),
|
|
21
|
+
sourceFile: z.string().optional(),
|
|
22
|
+
sourceSymbol: z.string().optional(),
|
|
23
|
+
sourceProvenance: z.enum(["extracted", "manual", "inferred", "generated"]).optional(),
|
|
24
|
+
sourceRepoRemote: z.string().optional(),
|
|
25
|
+
sourceRepoCommit: z.string().optional().describe("Full 40-character Git commit SHA."),
|
|
26
|
+
clearSourceReference: z.boolean().optional().describe("Remove the element's source reference. Cannot be combined with other source options."),
|
|
27
|
+
rootPath: rootPathSchema
|
|
28
|
+
};
|
|
29
|
+
function validateUpdateSourceReferenceOptions(args) {
|
|
30
|
+
const hasSourceOptions = args.sourceFile || args.sourceSymbol || args.sourceProvenance || args.sourceRepoRemote || args.sourceRepoCommit;
|
|
31
|
+
if (args.clearSourceReference && hasSourceOptions)
|
|
32
|
+
throw new CLIError("Cannot use clearSourceReference with other source reference options", ErrorCategory.USER, [
|
|
33
|
+
"Specify either clearSourceReference or other source options, not both"
|
|
34
|
+
]);
|
|
35
|
+
hasSourceOptions && validateSourceReferenceOptions(args, "mcp");
|
|
36
|
+
}
|
|
37
|
+
async function modelUpdateHandler(args) {
|
|
38
|
+
return runTool(async () => {
|
|
39
|
+
validateUpdateSourceReferenceOptions(args), resolveAttributeUpdate(void 0, { attributes: args.attributes, attributePatch: args.attributePatch });
|
|
40
|
+
const attributePatch = effectiveAttributePatch(args.attributePatch), model = await loadModel(args.rootPath), layerName = await findElementLayer(model, args.id);
|
|
41
|
+
if (!layerName)
|
|
42
|
+
throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [
|
|
43
|
+
'Use "model_search" to find similar elements'
|
|
44
|
+
]);
|
|
45
|
+
const layer = await model.getLayer(layerName), element = layer?.getElement(args.id);
|
|
46
|
+
if (!layer || !element)
|
|
47
|
+
throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [
|
|
48
|
+
'Use "model_search" to find similar elements'
|
|
49
|
+
]);
|
|
50
|
+
if (!(args.name || args.description !== void 0 || args.type || args.attributes || attributePatch || args.sourceFile || args.sourceSymbol || args.sourceProvenance || args.sourceRepoRemote || args.sourceRepoCommit || args.clearSourceReference))
|
|
51
|
+
return jsonResult({ status: "no-op", id: args.id, message: "No fields specified for update" });
|
|
52
|
+
const mutationHandler = new MutationHandler(model, args.id, layerName), warnings = [];
|
|
53
|
+
await mutationHandler.executeUpdate(element, async (elem, after) => {
|
|
54
|
+
let resolvedNewType;
|
|
55
|
+
if (args.type) {
|
|
56
|
+
if (!isValidNodeType(layerName, args.type)) {
|
|
57
|
+
const typeNames = getNodeTypesForLayer(layerName).map((t) => t.type).sort(), similar = findSimilar(args.type, typeNames, 3), suggestions = [`Valid types for ${layerName}: ${formatValidOptions(typeNames)}`];
|
|
58
|
+
throw similar.length > 0 && suggestions.unshift(`Did you mean: ${similar.join(" or ")}?`), new CLIError(`Invalid element type "${args.type}" for layer "${layerName}"`, ErrorCategory.USER, suggestions);
|
|
59
|
+
}
|
|
60
|
+
resolvedNewType = normalizeNodeType(layerName, args.type);
|
|
61
|
+
}
|
|
62
|
+
const newAttributes = resolveAttributeUpdate(elem.attributes, {
|
|
63
|
+
attributes: args.attributes,
|
|
64
|
+
attributePatch
|
|
65
|
+
});
|
|
66
|
+
if (resolvedNewType || newAttributes) {
|
|
67
|
+
const candidateType = resolvedNewType ?? elem.type, tempElem = new Element({
|
|
68
|
+
...elem.toJSON(),
|
|
69
|
+
type: candidateType,
|
|
70
|
+
spec_node_id: `${layerName}.${candidateType}`,
|
|
71
|
+
attributes: newAttributes ?? elem.attributes
|
|
72
|
+
}), schemaValidator = new SchemaValidator(), tempLayer = new Layer(layerName);
|
|
73
|
+
tempLayer.addElement(tempElem);
|
|
74
|
+
const attrValidation = await schemaValidator.validateLayer(tempLayer), undefinedType = !isValidNodeType(layerName, candidateType), blocking = undefinedType ? attrValidation.errors.filter((e) => e.location !== "/type") : attrValidation.errors;
|
|
75
|
+
if (undefinedType) {
|
|
76
|
+
const { message, hint } = undefinedTypeWarning("mcp", layerName, args.id, candidateType, suggestNodeTypes(layerName, candidateType));
|
|
77
|
+
warnings.push(`${message}. ${hint}`);
|
|
78
|
+
}
|
|
79
|
+
if (blocking.length > 0) {
|
|
80
|
+
const errorMessages = blocking.map((e) => ` ${e.message}`).join(`
|
|
81
|
+
`);
|
|
82
|
+
throw new CLIError(`Updated attributes fail schema validation:
|
|
83
|
+
${errorMessages}`, ErrorCategory.USER, [
|
|
84
|
+
...undefinedType ? warnings : [schemaLookupHint("mcp", layerName, candidateType)]
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
resolvedNewType && (elem.type = resolvedNewType, after.type = resolvedNewType, elem.spec_node_id = `${layerName}.${resolvedNewType}`, after.spec_node_id = `${layerName}.${resolvedNewType}`), args.name && (elem.name = args.name, after.name = args.name), args.description !== void 0 && (elem.description = args.description || void 0, after.description = args.description || void 0), newAttributes && (elem.attributes = newAttributes, after.attributes = newAttributes);
|
|
89
|
+
const hasSourceReferenceUpdate = args.sourceFile || args.sourceSymbol || args.sourceProvenance || args.sourceRepoRemote || args.sourceRepoCommit;
|
|
90
|
+
if (args.clearSourceReference)
|
|
91
|
+
elem.setSourceReference(void 0), setStagedSourceReference(after, void 0);
|
|
92
|
+
else if (hasSourceReferenceUpdate) {
|
|
93
|
+
const newRef = buildSourceReference(args);
|
|
94
|
+
newRef && (elem.setSourceReference(newRef), setStagedSourceReference(after, newRef));
|
|
95
|
+
}
|
|
96
|
+
layer.updateElement(elem);
|
|
97
|
+
}), await reloadModel(args.rootPath);
|
|
98
|
+
const activeChangeset = await mutationHandler.getStagingManager().getActive(), staged = !!(activeChangeset && activeChangeset.status === "staged");
|
|
99
|
+
return jsonResult({
|
|
100
|
+
status: staged ? "staged" : "updated",
|
|
101
|
+
changeset: staged ? activeChangeset.name : void 0,
|
|
102
|
+
id: args.id,
|
|
103
|
+
layer: layerName,
|
|
104
|
+
changedFields: mutationHandler.getChangedFields(),
|
|
105
|
+
// The changeset adds this element: the update was folded into that add (no separate change).
|
|
106
|
+
...mutationHandler.wasFoldedIntoAdd() ? { foldedIntoAdd: !0 } : {},
|
|
107
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const modelUpdateTool = {
|
|
112
|
+
name: "model_update",
|
|
113
|
+
description: "Update an existing element's name, description, type, attributes, or source reference. attributes replaces all attributes; attributePatch changes only the keys it names (null removes one).",
|
|
114
|
+
inputSchema,
|
|
115
|
+
handler: modelUpdateHandler
|
|
116
|
+
};
|
|
117
|
+
export {
|
|
118
|
+
modelUpdateHandler,
|
|
119
|
+
modelUpdateTool
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=model-update.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/model-update.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * model_update \u2014 update an existing element. Mirrors `dr update`, operating\n * through the shared MutationHandler.\n */\n\nimport { z } from \"zod\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { Element } from \"../../core/element.js\";\nimport { Layer } from \"../../core/layer.js\";\nimport { MutationHandler } from \"../../core/mutation-handler.js\";\nimport { setStagedSourceReference } from \"../../core/staged-source-reference.js\";\nimport { getNodeTypesForLayer, isValidNodeType, normalizeNodeType } from \"../../generated/node-types.js\";\nimport { findElementLayer } from \"../../utils/element-utils.js\";\nimport { buildSourceReference, validateSourceReferenceOptions } from \"../../utils/source-reference.js\";\nimport { schemaLookupHint, undefinedTypeWarning } from \"../../utils/error-hints.js\";\nimport { SchemaValidator, suggestNodeTypes } from \"../../validators/schema-validator.js\";\nimport { CLIError, ErrorCategory, findSimilar, formatValidOptions } from \"../../utils/errors.js\";\nimport { effectiveAttributePatch, resolveAttributeUpdate } from \"../../utils/attribute-patch.js\";\nimport { jsonResult, loadModel, reloadModel, rootPathSchema, runTool, type McpToolDefinition } from \"./shared.js\";\n\nconst inputSchema = {\n id: z.string().describe(\"Element ID or path to update.\"),\n name: z.string().optional(),\n description: z.string().optional(),\n type: z.string().optional().describe(\"New element type within the current layer.\"),\n attributes: z.record(z.string(), z.unknown()).optional().describe(\"Replaces the element's attributes, validated against its spec schema.\"),\n attributePatch: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n \"Merged into the element's current attributes (as the active changeset leaves them), so required attributes \" +\n \"need not be repeated; a null value removes that key. The merged result is validated against the spec \" +\n \"schema. Cannot be combined with attributes.\"\n ),\n sourceFile: z.string().optional(),\n sourceSymbol: z.string().optional(),\n sourceProvenance: z.enum([\"extracted\", \"manual\", \"inferred\", \"generated\"]).optional(),\n sourceRepoRemote: z.string().optional(),\n sourceRepoCommit: z.string().optional().describe(\"Full 40-character Git commit SHA.\"),\n clearSourceReference: z.boolean().optional().describe(\"Remove the element's source reference. Cannot be combined with other source options.\"),\n rootPath: rootPathSchema,\n};\n\nexport type ModelUpdateArgs = z.infer<z.ZodObject<typeof inputSchema>>;\n\nfunction validateUpdateSourceReferenceOptions(args: ModelUpdateArgs): void {\n const hasSourceOptions =\n args.sourceFile || args.sourceSymbol || args.sourceProvenance || args.sourceRepoRemote || args.sourceRepoCommit;\n\n if (args.clearSourceReference && hasSourceOptions) {\n throw new CLIError(\"Cannot use clearSourceReference with other source reference options\", ErrorCategory.USER, [\n \"Specify either clearSourceReference or other source options, not both\",\n ]);\n }\n\n if (hasSourceOptions) {\n validateSourceReferenceOptions(args, \"mcp\");\n }\n}\n\nexport async function modelUpdateHandler(args: ModelUpdateArgs): Promise<CallToolResult> {\n return runTool(async () => {\n validateUpdateSourceReferenceOptions(args);\n // Refuse attributes + attributePatch before touching the model.\n resolveAttributeUpdate(undefined, { attributes: args.attributes, attributePatch: args.attributePatch });\n // An empty patch names no key: it is not an update.\n const attributePatch = effectiveAttributePatch(args.attributePatch);\n\n const model = await loadModel(args.rootPath);\n\n const layerName = await findElementLayer(model, args.id);\n if (!layerName) {\n throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [\n 'Use \"model_search\" to find similar elements',\n ]);\n }\n\n const layer = await model.getLayer(layerName);\n const element = layer?.getElement(args.id);\n if (!layer || !element) {\n throw new CLIError(`Element ${args.id} not found`, ErrorCategory.NOT_FOUND, [\n 'Use \"model_search\" to find similar elements',\n ]);\n }\n\n const hasUpdates =\n args.name ||\n args.description !== undefined ||\n args.type ||\n args.attributes ||\n attributePatch ||\n args.sourceFile ||\n args.sourceSymbol ||\n args.sourceProvenance ||\n args.sourceRepoRemote ||\n args.sourceRepoCommit ||\n args.clearSourceReference;\n\n if (!hasUpdates) {\n return jsonResult({ status: \"no-op\", id: args.id, message: \"No fields specified for update\" });\n }\n\n const mutationHandler = new MutationHandler(model, args.id, layerName);\n const warnings: string[] = [];\n\n await mutationHandler.executeUpdate(element, async (elem, after) => {\n let resolvedNewType: string | undefined;\n if (args.type) {\n if (!isValidNodeType(layerName, args.type)) {\n const validNodeTypes = getNodeTypesForLayer(layerName);\n const typeNames = validNodeTypes.map((t) => t.type).sort();\n const similar = findSimilar(args.type!, typeNames, 3);\n const suggestions: string[] = [`Valid types for ${layerName}: ${formatValidOptions(typeNames)}`];\n if (similar.length > 0) suggestions.unshift(`Did you mean: ${similar.join(\" or \")}?`);\n throw new CLIError(`Invalid element type \"${args.type}\" for layer \"${layerName}\"`, ErrorCategory.USER, suggestions);\n }\n resolvedNewType = normalizeNodeType(layerName, args.type);\n }\n\n // The element is the projected one, so a patch merges over attributes the changeset already staged.\n const newAttributes = resolveAttributeUpdate(elem.attributes, {\n attributes: args.attributes,\n attributePatch,\n });\n\n if (resolvedNewType || newAttributes) {\n const candidateType = resolvedNewType ?? elem.type;\n const tempElem = new Element({\n ...elem.toJSON(),\n type: candidateType,\n spec_node_id: `${layerName}.${candidateType}`,\n attributes: newAttributes ?? elem.attributes,\n });\n const schemaValidator = new SchemaValidator();\n const tempLayer = new Layer(layerName);\n tempLayer.addElement(tempElem);\n const attrValidation = await schemaValidator.validateLayer(tempLayer);\n // An element already of a type the spec no longer defines (no type change requested):\n // its attributes have no type schema to fail, so let the update through and say how to\n // retype it, rather than refusing with a spec_type hint that cannot work.\n const undefinedType = !isValidNodeType(layerName, candidateType);\n const blocking = undefinedType\n ? attrValidation.errors.filter((e) => e.location !== \"/type\")\n : attrValidation.errors;\n if (undefinedType) {\n const { message, hint } = undefinedTypeWarning(\n \"mcp\",\n layerName,\n args.id,\n candidateType,\n suggestNodeTypes(layerName, candidateType)\n );\n warnings.push(`${message}. ${hint}`);\n }\n if (blocking.length > 0) {\n const errorMessages = blocking.map((e) => ` ${e.message}`).join(\"\\n\");\n throw new CLIError(`Updated attributes fail schema validation:\\n${errorMessages}`, ErrorCategory.USER, [\n ...(undefinedType ? warnings : [schemaLookupHint(\"mcp\", layerName, candidateType)]),\n ]);\n }\n }\n\n if (resolvedNewType) {\n elem.type = resolvedNewType;\n after.type = resolvedNewType;\n elem.spec_node_id = `${layerName}.${resolvedNewType}`;\n after.spec_node_id = `${layerName}.${resolvedNewType}`;\n }\n\n if (args.name) {\n elem.name = args.name;\n after.name = args.name;\n }\n\n if (args.description !== undefined) {\n elem.description = args.description || undefined;\n after.description = args.description || undefined;\n }\n\n if (newAttributes) {\n elem.attributes = newAttributes;\n after.attributes = newAttributes;\n }\n\n const hasSourceReferenceUpdate =\n args.sourceFile || args.sourceSymbol || args.sourceProvenance || args.sourceRepoRemote || args.sourceRepoCommit;\n\n if (args.clearSourceReference) {\n elem.setSourceReference(undefined);\n setStagedSourceReference(after, undefined);\n } else if (hasSourceReferenceUpdate) {\n const newRef = buildSourceReference(args);\n if (newRef) {\n elem.setSourceReference(newRef);\n setStagedSourceReference(after, newRef);\n }\n }\n\n layer.updateElement(elem);\n });\n\n // The handler mutated the cached (projected) element in place; reread so the next call projects\n // from what is on disk, staged or written.\n await reloadModel(args.rootPath);\n\n const stagingManager = mutationHandler.getStagingManager();\n const activeChangeset = await stagingManager.getActive();\n const staged = !!(activeChangeset && activeChangeset.status === \"staged\");\n\n return jsonResult({\n status: staged ? \"staged\" : \"updated\",\n changeset: staged ? activeChangeset!.name : undefined,\n id: args.id,\n layer: layerName,\n changedFields: mutationHandler.getChangedFields(),\n // The changeset adds this element: the update was folded into that add (no separate change).\n ...(mutationHandler.wasFoldedIntoAdd() ? { foldedIntoAdd: true } : {}),\n ...(warnings.length > 0 ? { warnings } : {}),\n });\n });\n}\n\nexport const modelUpdateTool: McpToolDefinition<typeof inputSchema> = {\n name: \"model_update\",\n description:\n \"Update an existing element's name, description, type, attributes, or source reference. attributes replaces \" +\n \"all attributes; attributePatch changes only the keys it names (null removes one).\",\n inputSchema,\n handler: modelUpdateHandler,\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|