@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
package/dist/utils/errors.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/errors.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Error handling utilities with formatted output\n * Matches Python CLI error message formatting\n *\n * Error Categories:\n * - User Error (exit code 1): Invalid input, wrong format, element exists\n * - Not Found (exit code 2): File/element/model not found\n * - System Error (exit code 3): Permission denied, I/O failure\n * - Validation Error (exit code 4): Schema/reference/semantic validation failed\n * - Breaking Change (exit code 5): Version migration required\n */\n\nimport ansis from \"ansis\";\n\nconst MAX_SUGGESTIONS = 5;\n\nexport enum ErrorCategory {\n USER = 1,\n NOT_FOUND = 2,\n SYSTEM = 3,\n VALIDATION = 4,\n BREAKING_CHANGE = 5,\n}\n\nexport interface ErrorContext {\n operation?: string;\n context?: string;\n relatedElements?: string[];\n partialProgress?: { completed: number; total: number };\n}\n\nexport class CLIError extends Error {\n constructor(\n message: string,\n public exitCode: number = ErrorCategory.USER,\n public suggestions?: string[],\n public context?: ErrorContext\n ) {\n super(message);\n this.name = \"CLIError\";\n }\n\n format(): string {\n const lines: string[] = [];\n\n lines.push(ansis.red(`Error: ${this.message}`));\n\n if (this.context?.operation) {\n lines.push(ansis.dim(`During: ${this.context.operation}`));\n }\n\n if (this.context?.partialProgress) {\n const { completed, total } = this.context.partialProgress;\n lines.push(\n ansis.yellow(`\u26A0 Partial progress: ${completed}/${total} completed (operation rolled back)`)\n );\n }\n\n if (this.context?.relatedElements && this.context.relatedElements.length > 0) {\n lines.push(ansis.dim(\"Related elements:\"));\n for (const elem of this.context.relatedElements.slice(0, MAX_SUGGESTIONS)) {\n lines.push(ansis.dim(` \u2022 ${elem}`));\n }\n if (this.context.relatedElements.length > MAX_SUGGESTIONS) {\n lines.push(\n ansis.dim(` ... and ${this.context.relatedElements.length - MAX_SUGGESTIONS} more`)\n );\n }\n }\n\n if (this.suggestions && this.suggestions.length > 0) {\n lines.push(\"\");\n lines.push(ansis.dim(\"Suggestions:\"));\n for (const suggestion of this.suggestions) {\n lines.push(ansis.dim(` \u2022 ${suggestion}`));\n }\n }\n\n return lines.join(\"\\n\");\n }\n}\n\nexport class ValidationError extends CLIError {\n constructor(\n message: string,\n public errors: Array<{ layer?: string; elementId?: string; message: string }> = [],\n suggestions?: string[],\n context?: ErrorContext\n ) {\n super(message, ErrorCategory.VALIDATION, suggestions, context);\n this.name = \"ValidationError\";\n }\n\n format(): string {\n const lines: string[] = [];\n\n lines.push(ansis.red(`Error: ${this.message}`));\n\n if (this.errors.length > 0) {\n // Show error count and grouping\n const errorsByLayer: Record<string, Array<{ elementId?: string; message: string }>> = {};\n for (const error of this.errors) {\n const layer = error.layer || \"general\";\n if (!errorsByLayer[layer]) {\n errorsByLayer[layer] = [];\n }\n errorsByLayer[layer].push({ elementId: error.elementId, message: error.message });\n }\n\n lines.push(\"\");\n lines.push(ansis.dim(`Validation errors: ${this.errors.length} found`));\n lines.push(\"\");\n\n const layerNames = Object.keys(errorsByLayer).sort();\n\n for (const layer of layerNames) {\n const layerErrors = errorsByLayer[layer];\n lines.push(ansis.dim(` [${layer}] ${layerErrors.length} error(s):`));\n\n for (let i = 0; i < Math.min(layerErrors.length, MAX_SUGGESTIONS); i++) {\n const error = layerErrors[i];\n let detail = \"\";\n if (error.elementId) {\n detail = ` ${error.elementId}: ${error.message}`;\n } else {\n detail = ` ${error.message}`;\n }\n lines.push(ansis.dim(detail));\n }\n\n if (layerErrors.length > MAX_SUGGESTIONS) {\n lines.push(\n ansis.dim(` ... and ${layerErrors.length - MAX_SUGGESTIONS} more in this layer`)\n );\n }\n }\n }\n\n if (this.suggestions && this.suggestions.length > 0) {\n lines.push(\"\");\n lines.push(ansis.dim(\"Suggestions:\"));\n for (const suggestion of this.suggestions) {\n lines.push(ansis.dim(` \u2022 ${suggestion}`));\n }\n }\n\n return lines.join(\"\\n\");\n }\n}\n\nexport class FileNotFoundError extends CLIError {\n constructor(path: string, context?: string) {\n const message = context ? `${context}: ${path}` : `File not found: ${path}`;\n super(message, ErrorCategory.NOT_FOUND, [\n `Check that the path is correct: ${path}`,\n 'Use \"dr init\" to create a new model',\n 'Use \"dr list <layer>\" to see available elements',\n ]);\n this.name = \"FileNotFoundError\";\n }\n}\n\nexport class ElementNotFoundError extends CLIError {\n constructor(elementId: string) {\n super(`Element not found: ${elementId}`, ErrorCategory.NOT_FOUND, [\n `Use \"dr search ${elementId}\" to find similar elements`,\n 'Use \"dr list <layer>\" to list all elements in a layer',\n \"Check the element ID format: {layer}.{type}.{kebab-case-name}\",\n ]);\n this.name = \"ElementNotFoundError\";\n }\n}\n\nexport class ModelNotFoundError extends CLIError {\n constructor(rootPath: string = \".\") {\n super(`No model found at ${rootPath}`, ErrorCategory.NOT_FOUND, [\n `Run \"dr init\" to initialize a new model`,\n \"Check that you are in the correct directory\",\n ]);\n this.name = \"ModelNotFoundError\";\n }\n}\n\nexport class InvalidJSONError extends CLIError {\n constructor(input: string, context?: string) {\n const message = context ? `Invalid JSON in ${context}` : \"Invalid JSON format\";\n super(message, ErrorCategory.USER, [\n `Check your JSON syntax: ${input}`,\n 'Use single quotes to wrap JSON: --attributes \\'{\"key\":\"value\"}\\'',\n \"Escape special characters properly\",\n ]);\n this.name = \"InvalidJSONError\";\n }\n}\n\nexport function handleError(error: unknown): never {\n if (error instanceof CLIError) {\n console.error(error.format());\n // Don't call process.exit() - rethrow so CLI wrapper can handle telemetry shutdown\n throw error;\n } else if (error instanceof Error) {\n console.error(ansis.red(`Error: ${error.message}`));\n if (process.env.DEBUG) {\n console.error(ansis.dim(error.stack));\n }\n // Rethrow to allow CLI wrapper to handle shutdown\n throw error;\n } else {\n console.error(ansis.red(\"An unexpected error occurred\"));\n if (process.env.DEBUG) {\n console.error(ansis.dim(String(error)));\n }\n // Create proper error to throw\n throw new Error(String(error));\n }\n}\n\nexport function handleWarning(message: string, suggestions?: string[]): void {\n const lines: string[] = [];\n lines.push(ansis.yellow(`Warning: ${message}`));\n if (suggestions && suggestions.length > 0) {\n lines.push(\"\");\n lines.push(ansis.dim(\"Suggestions:\"));\n for (const suggestion of suggestions) {\n lines.push(ansis.dim(` \u2022 ${suggestion}`));\n }\n }\n console.warn(lines.join(\"\\n\"));\n}\n\nexport function handleSuccess(message: string, details?: Record<string, string>): void {\n console.log(ansis.green(`\u2713 ${message}`));\n if (details) {\n for (const [key, value] of Object.entries(details)) {\n console.log(ansis.dim(` ${key}: ${value}`));\n }\n }\n}\n\n/**\n * Utility to extract common valid options for better error suggestions\n */\nexport function formatValidOptions(options: string[], heading: string = \"Valid options\"): string {\n if (options.length === 0) return \"\";\n if (options.length === 1) return `${heading}: ${options[0]}`;\n return `${heading}: ${options.slice(0, -1).join(\", \")} or ${options[options.length - 1]}`;\n}\n\n/**\n * Categorize errors based on type, using stable error.code for system errors\n * Returns the appropriate ErrorCategory for a given error\n */\nexport function categorizeError(error: unknown): ErrorCategory {\n if (error instanceof CLIError) {\n return error.exitCode;\n }\n\n // Check for system errors using stable error.code\n if (error instanceof Error && \"code\" in error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (typeof code === \"string\") {\n // File not found errors should map to NOT_FOUND category\n if (code === \"ENOENT\") {\n return ErrorCategory.NOT_FOUND;\n }\n // Other system-level errors\n const SYSTEM_ERROR_CODES = new Set([\"EACCES\", \"ENOSPC\", \"EIO\", \"EROFS\", \"EPERM\"]);\n if (SYSTEM_ERROR_CODES.has(code)) {\n return ErrorCategory.SYSTEM;\n }\n }\n }\n\n // Default to USER for other errors\n return ErrorCategory.USER;\n}\n\n/**\n * Extract error message from any error type\n * Handles Error instances, strings, and unknown types consistently\n */\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\n/**\n * Simple fuzzy match to find similar strings (for typo suggestions)\n */\nexport function findSimilar(input: string, options: string[], maxDistance: number = 2): string[] {\n return options\n .map((option) => ({\n option,\n distance: levenshteinDistance(input.toLowerCase(), option.toLowerCase()),\n }))\n .filter(({ distance }) => distance <= maxDistance)\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 3)\n .map(({ option }) => option);\n}\n\n/**\n * Calculate Levenshtein distance for typo detection\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j] + 1\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n"],
|
|
5
|
-
"mappings": "AAYA,OAAO,WAAW;
|
|
4
|
+
"sourcesContent": ["/**\n * Error handling utilities with formatted output\n * Matches Python CLI error message formatting\n *\n * Error Categories:\n * - User Error (exit code 1): Invalid input, wrong format, element exists\n * - Not Found (exit code 2): File/element/model not found\n * - System Error (exit code 3): Permission denied, I/O failure\n * - Validation Error (exit code 4): Schema/reference/semantic validation failed\n * - Breaking Change (exit code 5): Version migration required\n */\n\nimport ansis from \"ansis\";\nimport { isJson } from \"./globals.js\";\n\nconst MAX_SUGGESTIONS = 5;\n\nexport enum ErrorCategory {\n USER = 1,\n NOT_FOUND = 2,\n SYSTEM = 3,\n VALIDATION = 4,\n BREAKING_CHANGE = 5,\n}\n\nexport interface ErrorContext {\n operation?: string;\n context?: string;\n relatedElements?: string[];\n partialProgress?: { completed: number; total: number };\n}\n\nexport class CLIError extends Error {\n constructor(\n message: string,\n public exitCode: number = ErrorCategory.USER,\n public suggestions?: string[],\n public context?: ErrorContext\n ) {\n super(message);\n this.name = \"CLIError\";\n }\n\n format(): string {\n const lines: string[] = [];\n\n lines.push(ansis.red(`Error: ${this.message}`));\n\n if (this.context?.operation) {\n lines.push(ansis.dim(`During: ${this.context.operation}`));\n }\n\n if (this.context?.partialProgress) {\n const { completed, total } = this.context.partialProgress;\n lines.push(\n ansis.yellow(`\u26A0 Partial progress: ${completed}/${total} completed (operation rolled back)`)\n );\n }\n\n if (this.context?.relatedElements && this.context.relatedElements.length > 0) {\n lines.push(ansis.dim(\"Related elements:\"));\n for (const elem of this.context.relatedElements.slice(0, MAX_SUGGESTIONS)) {\n lines.push(ansis.dim(` \u2022 ${elem}`));\n }\n if (this.context.relatedElements.length > MAX_SUGGESTIONS) {\n lines.push(\n ansis.dim(` ... and ${this.context.relatedElements.length - MAX_SUGGESTIONS} more`)\n );\n }\n }\n\n if (this.suggestions && this.suggestions.length > 0) {\n lines.push(\"\");\n lines.push(ansis.dim(\"Suggestions:\"));\n for (const suggestion of this.suggestions) {\n lines.push(ansis.dim(` \u2022 ${suggestion}`));\n }\n }\n\n return lines.join(\"\\n\");\n }\n}\n\nexport class ValidationError extends CLIError {\n constructor(\n message: string,\n public errors: Array<{ layer?: string; elementId?: string; message: string }> = [],\n suggestions?: string[],\n context?: ErrorContext\n ) {\n super(message, ErrorCategory.VALIDATION, suggestions, context);\n this.name = \"ValidationError\";\n }\n\n format(): string {\n const lines: string[] = [];\n\n lines.push(ansis.red(`Error: ${this.message}`));\n\n if (this.errors.length > 0) {\n // Show error count and grouping\n const errorsByLayer: Record<string, Array<{ elementId?: string; message: string }>> = {};\n for (const error of this.errors) {\n const layer = error.layer || \"general\";\n if (!errorsByLayer[layer]) {\n errorsByLayer[layer] = [];\n }\n errorsByLayer[layer].push({ elementId: error.elementId, message: error.message });\n }\n\n lines.push(\"\");\n lines.push(ansis.dim(`Validation errors: ${this.errors.length} found`));\n lines.push(\"\");\n\n const layerNames = Object.keys(errorsByLayer).sort();\n\n for (const layer of layerNames) {\n const layerErrors = errorsByLayer[layer];\n lines.push(ansis.dim(` [${layer}] ${layerErrors.length} error(s):`));\n\n for (let i = 0; i < Math.min(layerErrors.length, MAX_SUGGESTIONS); i++) {\n const error = layerErrors[i];\n let detail = \"\";\n if (error.elementId) {\n detail = ` ${error.elementId}: ${error.message}`;\n } else {\n detail = ` ${error.message}`;\n }\n lines.push(ansis.dim(detail));\n }\n\n if (layerErrors.length > MAX_SUGGESTIONS) {\n lines.push(\n ansis.dim(` ... and ${layerErrors.length - MAX_SUGGESTIONS} more in this layer`)\n );\n }\n }\n }\n\n if (this.suggestions && this.suggestions.length > 0) {\n lines.push(\"\");\n lines.push(ansis.dim(\"Suggestions:\"));\n for (const suggestion of this.suggestions) {\n lines.push(ansis.dim(` \u2022 ${suggestion}`));\n }\n }\n\n return lines.join(\"\\n\");\n }\n}\n\nexport class FileNotFoundError extends CLIError {\n constructor(path: string, context?: string) {\n const message = context ? `${context}: ${path}` : `File not found: ${path}`;\n super(message, ErrorCategory.NOT_FOUND, [\n `Check that the path is correct: ${path}`,\n 'Use \"dr init\" to create a new model',\n 'Use \"dr list <layer>\" to see available elements',\n ]);\n this.name = \"FileNotFoundError\";\n }\n}\n\nexport class ElementNotFoundError extends CLIError {\n constructor(elementId: string) {\n super(`Element not found: ${elementId}`, ErrorCategory.NOT_FOUND, [\n `Use \"dr search ${elementId}\" to find similar elements`,\n 'Use \"dr list <layer>\" to list all elements in a layer',\n \"Check the element ID format: {layer}.{type}.{kebab-case-name}\",\n ]);\n this.name = \"ElementNotFoundError\";\n }\n}\n\nexport class ModelNotFoundError extends CLIError {\n constructor(rootPath: string = \".\") {\n super(`No model found at ${rootPath}`, ErrorCategory.NOT_FOUND, [\n `Run \"dr init\" to initialize a new model`,\n \"Check that you are in the correct directory\",\n ]);\n this.name = \"ModelNotFoundError\";\n }\n}\n\nexport class InvalidJSONError extends CLIError {\n constructor(input: string, context?: string) {\n const message = context ? `Invalid JSON in ${context}` : \"Invalid JSON format\";\n super(message, ErrorCategory.USER, [\n `Check your JSON syntax: ${input}`,\n 'Use single quotes to wrap JSON: --attributes \\'{\"key\":\"value\"}\\'',\n \"Escape special characters properly\",\n ]);\n this.name = \"InvalidJSONError\";\n }\n}\n\nexport function handleError(error: unknown): never {\n if (error instanceof CLIError) {\n if (isJson()) {\n const output: Record<string, unknown> = {\n status: \"error\",\n code: error.exitCode,\n message: error.message,\n };\n if (error.suggestions && error.suggestions.length > 0) {\n output.suggestions = error.suggestions;\n }\n if (error.context) {\n if (error.context.operation) output.operation = error.context.operation;\n if (error.context.relatedElements && error.context.relatedElements.length > 0) {\n output.relatedElements = error.context.relatedElements;\n }\n if (error.context.partialProgress) {\n output.partialProgress = error.context.partialProgress;\n }\n }\n console.log(JSON.stringify(output));\n } else {\n console.error(error.format());\n }\n // Don't call process.exit() - rethrow so CLI wrapper can handle telemetry shutdown\n throw error;\n } else if (error instanceof Error) {\n if (isJson()) {\n const output: Record<string, unknown> = {\n status: \"error\",\n code: ErrorCategory.USER,\n message: error.message,\n };\n if (process.env.DEBUG && error.stack) {\n output.stack = error.stack;\n }\n console.log(JSON.stringify(output));\n } else {\n console.error(ansis.red(`Error: ${error.message}`));\n if (process.env.DEBUG) {\n console.error(ansis.dim(error.stack));\n }\n }\n // Rethrow to allow CLI wrapper to handle shutdown\n throw error;\n } else {\n if (isJson()) {\n const output: Record<string, unknown> = {\n status: \"error\",\n code: ErrorCategory.USER,\n message: \"An unexpected error occurred\",\n };\n if (process.env.DEBUG) {\n output.details = String(error);\n }\n console.log(JSON.stringify(output));\n } else {\n console.error(ansis.red(\"An unexpected error occurred\"));\n if (process.env.DEBUG) {\n console.error(ansis.dim(String(error)));\n }\n }\n // Create proper error to throw\n throw new Error(String(error));\n }\n}\n\nexport function handleWarning(message: string, suggestions?: string[]): void {\n if (isJson()) {\n // In JSON mode, emit warnings to stderr as structured JSON\n const output: Record<string, unknown> = {\n level: \"warning\",\n message,\n };\n if (suggestions && suggestions.length > 0) {\n output.suggestions = suggestions;\n }\n console.error(JSON.stringify(output));\n } else {\n const lines: string[] = [];\n lines.push(ansis.yellow(`Warning: ${message}`));\n if (suggestions && suggestions.length > 0) {\n lines.push(\"\");\n lines.push(ansis.dim(\"Suggestions:\"));\n for (const suggestion of suggestions) {\n lines.push(ansis.dim(` \u2022 ${suggestion}`));\n }\n }\n console.warn(lines.join(\"\\n\"));\n }\n}\n\nexport function handleSuccess(message: string, details?: Record<string, unknown>, options?: { verbose?: boolean }): void {\n if (isJson()) {\n const output: Record<string, unknown> = {\n ...details,\n status: \"ok\",\n };\n console.log(JSON.stringify(output));\n } else {\n console.log(ansis.green(`\u2713 ${message}`));\n // Only show details in non-JSON mode if verbose flag is set or details include changesetStatus/status\n if (details && (options?.verbose || details.changesetStatus)) {\n // Convert camelCase keys to PascalCase for display\n for (const [key, value] of Object.entries(details)) {\n const displayKey = keyToDisplayName(key);\n console.log(ansis.dim(` ${displayKey}: ${value}`));\n }\n }\n }\n}\n\nfunction keyToDisplayName(key: string): string {\n // Convert camelCase and snake_case to PascalCase/Title Case\n return key\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase to words\n .replace(/_/g, ' ') // snake_case to words\n .split(' ')\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\nexport function handleInfo(message: string, details?: Record<string, string>): void {\n if (isJson()) {\n const output: Record<string, unknown> = {\n level: \"info\",\n message,\n };\n if (details) {\n output.details = details;\n }\n console.error(JSON.stringify(output));\n } else {\n console.log(message);\n if (details) {\n for (const [key, value] of Object.entries(details)) {\n console.log(ansis.dim(` ${key}: ${value}`));\n }\n }\n }\n}\n\n/**\n * Utility to extract common valid options for better error suggestions\n */\nexport function formatValidOptions(options: string[], heading: string = \"Valid options\"): string {\n if (options.length === 0) return \"\";\n if (options.length === 1) return `${heading}: ${options[0]}`;\n return `${heading}: ${options.slice(0, -1).join(\", \")} or ${options[options.length - 1]}`;\n}\n\n/**\n * Categorize errors based on type, using stable error.code for system errors\n * Returns the appropriate ErrorCategory for a given error\n */\nexport function categorizeError(error: unknown): ErrorCategory {\n if (error instanceof CLIError) {\n return error.exitCode;\n }\n\n // Check for system errors using stable error.code\n if (error instanceof Error && \"code\" in error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (typeof code === \"string\") {\n // File not found errors should map to NOT_FOUND category\n if (code === \"ENOENT\") {\n return ErrorCategory.NOT_FOUND;\n }\n // Other system-level errors\n const SYSTEM_ERROR_CODES = new Set([\"EACCES\", \"ENOSPC\", \"EIO\", \"EROFS\", \"EPERM\"]);\n if (SYSTEM_ERROR_CODES.has(code)) {\n return ErrorCategory.SYSTEM;\n }\n }\n }\n\n // Default to USER for other errors\n return ErrorCategory.USER;\n}\n\n/**\n * Extract error message from any error type\n * Handles Error instances, strings, and unknown types consistently\n */\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\n/**\n * Simple fuzzy match to find similar strings (for typo suggestions)\n */\nexport function findSimilar(input: string, options: string[], maxDistance: number = 2): string[] {\n return options\n .map((option) => ({\n option,\n distance: levenshteinDistance(input.toLowerCase(), option.toLowerCase()),\n }))\n .filter(({ distance }) => distance <= maxDistance)\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 3)\n .map(({ option }) => option);\n}\n\n/**\n * Calculate Levenshtein distance for typo detection\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j] + 1\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["ErrorCategory"]
|
|
7
7
|
}
|
package/dist/utils/globals.js
CHANGED
|
@@ -2,6 +2,9 @@ let globalOptions = {};
|
|
|
2
2
|
function setGlobalOptions(options) {
|
|
3
3
|
globalOptions = { ...globalOptions, ...options };
|
|
4
4
|
}
|
|
5
|
+
function resetGlobalOptions() {
|
|
6
|
+
globalOptions = {};
|
|
7
|
+
}
|
|
5
8
|
function getGlobalOptions() {
|
|
6
9
|
return { ...globalOptions };
|
|
7
10
|
}
|
|
@@ -11,18 +14,31 @@ function isVerbose() {
|
|
|
11
14
|
function isDebug() {
|
|
12
15
|
return globalOptions.debug === !0;
|
|
13
16
|
}
|
|
17
|
+
function isJson() {
|
|
18
|
+
return globalOptions.json === !0;
|
|
19
|
+
}
|
|
14
20
|
function logVerbose(message) {
|
|
15
21
|
isVerbose() && console.log(message);
|
|
16
22
|
}
|
|
17
23
|
function logDebug(message) {
|
|
18
24
|
isDebug() && console.debug(`[DEBUG] ${message}`);
|
|
19
25
|
}
|
|
26
|
+
function setJsonMode(json) {
|
|
27
|
+
globalOptions.json = json;
|
|
28
|
+
}
|
|
29
|
+
function getCodebasePath() {
|
|
30
|
+
return globalOptions.codebasePath;
|
|
31
|
+
}
|
|
20
32
|
export {
|
|
33
|
+
getCodebasePath,
|
|
21
34
|
getGlobalOptions,
|
|
22
35
|
isDebug,
|
|
36
|
+
isJson,
|
|
23
37
|
isVerbose,
|
|
24
38
|
logDebug,
|
|
25
39
|
logVerbose,
|
|
26
|
-
|
|
40
|
+
resetGlobalOptions,
|
|
41
|
+
setGlobalOptions,
|
|
42
|
+
setJsonMode
|
|
27
43
|
};
|
|
28
44
|
//# sourceMappingURL=globals.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/globals.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Global CLI option management\n */\n\nexport interface GlobalOptions {\n verbose?: boolean;\n debug?: boolean;\n}\n\n// Global state for CLI options\nlet globalOptions: GlobalOptions = {};\n\n/**\n * Set global CLI options\n */\nexport function setGlobalOptions(options: GlobalOptions): void {\n globalOptions = { ...globalOptions, ...options };\n}\n\n/**\n * Get current global options\n */\nexport function getGlobalOptions(): GlobalOptions {\n return { ...globalOptions };\n}\n\n/**\n * Check if verbose mode is enabled\n */\nexport function isVerbose(): boolean {\n return globalOptions.verbose === true;\n}\n\n/**\n * Check if debug mode is enabled\n */\nexport function isDebug(): boolean {\n return globalOptions.debug === true;\n}\n\n/**\n * Log verbose messages\n */\nexport function logVerbose(message: string): void {\n if (isVerbose()) {\n console.log(message);\n }\n}\n\n/**\n * Log debug messages\n */\nexport function logDebug(message: string): void {\n if (isDebug()) {\n console.debug(`[DEBUG] ${message}`);\n }\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * Global CLI option management\n */\n\nexport interface GlobalOptions {\n verbose?: boolean;\n debug?: boolean;\n json?: boolean;\n codebasePath?: string;\n}\n\n// Global state for CLI options\nlet globalOptions: GlobalOptions = {};\n\n/**\n * Set global CLI options\n */\nexport function setGlobalOptions(options: GlobalOptions): void {\n globalOptions = { ...globalOptions, ...options };\n}\n\n/**\n * Reset global CLI options to empty state\n */\nexport function resetGlobalOptions(): void {\n globalOptions = {};\n}\n\n/**\n * Get current global options\n */\nexport function getGlobalOptions(): GlobalOptions {\n return { ...globalOptions };\n}\n\n/**\n * Check if verbose mode is enabled\n */\nexport function isVerbose(): boolean {\n return globalOptions.verbose === true;\n}\n\n/**\n * Check if debug mode is enabled\n */\nexport function isDebug(): boolean {\n return globalOptions.debug === true;\n}\n\n/**\n * Check if JSON mode is enabled\n */\nexport function isJson(): boolean {\n return globalOptions.json === true;\n}\n\n/**\n * Log verbose messages\n */\nexport function logVerbose(message: string): void {\n if (isVerbose()) {\n console.log(message);\n }\n}\n\n/**\n * Log debug messages\n */\nexport function logDebug(message: string): void {\n if (isDebug()) {\n console.debug(`[DEBUG] ${message}`);\n }\n}\n\n/**\n * Set JSON mode\n */\nexport function setJsonMode(json: boolean): void {\n globalOptions.json = json;\n}\n\n/**\n * Get codebase path from global options\n */\nexport function getCodebasePath(): string | undefined {\n return globalOptions.codebasePath;\n}\n"],
|
|
5
|
+
"mappings": "AAYA,IAAI,gBAA+B,CAAA;AAK7B,SAAU,iBAAiB,SAAsB;AACrD,kBAAgB,EAAE,GAAG,eAAe,GAAG,QAAO;AAChD;AAKM,SAAU,qBAAkB;AAChC,kBAAgB,CAAA;AAClB;AAKM,SAAU,mBAAgB;AAC9B,SAAO,EAAE,GAAG,cAAa;AAC3B;AAKM,SAAU,YAAS;AACvB,SAAO,cAAc,YAAY;AACnC;AAKM,SAAU,UAAO;AACrB,SAAO,cAAc,UAAU;AACjC;AAKM,SAAU,SAAM;AACpB,SAAO,cAAc,SAAS;AAChC;AAKM,SAAU,WAAW,SAAe;AACxC,EAAI,UAAS,KACX,QAAQ,IAAI,OAAO;AAEvB;AAKM,SAAU,SAAS,SAAe;AACtC,EAAI,QAAO,KACT,QAAQ,MAAM,WAAW,OAAO,EAAE;AAEtC;AAKM,SAAU,YAAY,MAAa;AACvC,gBAAc,OAAO;AACvB;AAKM,SAAU,kBAAe;AAC7B,SAAO,cAAc;AACvB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fileExists } from "./file-io.js";
|
|
2
|
-
import { join, dirname } from "path";
|
|
3
|
-
|
|
2
|
+
import { join, dirname, resolve } from "path";
|
|
3
|
+
import { stat } from "fs/promises";
|
|
4
|
+
const MAX_SEARCH_DEPTH = 5, PROJECT_FOLDER_NAME = "documentation-robotics", SPEC_FOLDER_NAME = ".dr", FARM_FILE_NAME = "farm.yaml";
|
|
4
5
|
async function findProjectRoot(startPath) {
|
|
5
6
|
let currentPath = startPath || process.cwd();
|
|
6
7
|
for (let depth = 0; depth < MAX_SEARCH_DEPTH; depth++) {
|
|
@@ -35,11 +36,57 @@ async function getSpecReferencePath(startPath) {
|
|
|
35
36
|
async function isInDRProject(startPath) {
|
|
36
37
|
return await findProjectRoot(startPath) !== null;
|
|
37
38
|
}
|
|
39
|
+
async function findFarmRoot(startPath) {
|
|
40
|
+
if (process.env.DR_FARM_PATH) {
|
|
41
|
+
const envPath = resolve(process.env.DR_FARM_PATH), farmFilePath = join(envPath, FARM_FILE_NAME);
|
|
42
|
+
if (await fileExists(farmFilePath))
|
|
43
|
+
return envPath;
|
|
44
|
+
throw new Error(`Invalid DR_FARM_PATH: ${process.env.DR_FARM_PATH}
|
|
45
|
+
Expected farm.yaml at: ${farmFilePath}
|
|
46
|
+
Ensure DR_FARM_PATH points to a directory containing a farm.yaml file.`);
|
|
47
|
+
}
|
|
48
|
+
let currentPath = startPath || process.cwd();
|
|
49
|
+
for (let depth = 0; depth < MAX_SEARCH_DEPTH; depth++) {
|
|
50
|
+
const farmFilePath = join(currentPath, FARM_FILE_NAME);
|
|
51
|
+
if (await fileExists(farmFilePath))
|
|
52
|
+
return currentPath;
|
|
53
|
+
const parentPath = dirname(currentPath);
|
|
54
|
+
if (parentPath === currentPath)
|
|
55
|
+
break;
|
|
56
|
+
currentPath = parentPath;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const MODEL_LAYOUT_CANDIDATES = [
|
|
61
|
+
{ layout: "standard", segments: [PROJECT_FOLDER_NAME, "model"] },
|
|
62
|
+
{ layout: "legacy", segments: ["model"] },
|
|
63
|
+
{ layout: "detached", segments: [] }
|
|
64
|
+
];
|
|
65
|
+
async function resolveModelDirectory(basePath, options = {}) {
|
|
66
|
+
const resolvedBase = resolve(basePath);
|
|
67
|
+
for (const candidate of MODEL_LAYOUT_CANDIDATES) {
|
|
68
|
+
const modelDir = join(resolvedBase, ...candidate.segments), stats = await statIfExists(modelDir);
|
|
69
|
+
if (stats && !(options.requireManifest && (!stats.isDirectory() || !await statIfExists(join(modelDir, "manifest.yaml")))))
|
|
70
|
+
return { modelDir, layout: candidate.layout, isDirectory: stats.isDirectory() };
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
async function statIfExists(target) {
|
|
75
|
+
try {
|
|
76
|
+
return await stat(target);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (err?.code === "ENOENT")
|
|
79
|
+
return;
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
38
83
|
export {
|
|
84
|
+
findFarmRoot,
|
|
39
85
|
findProjectRoot,
|
|
40
86
|
getDocumentationRobotsPath,
|
|
41
87
|
getModelPath,
|
|
42
88
|
getSpecReferencePath,
|
|
43
|
-
isInDRProject
|
|
89
|
+
isInDRProject,
|
|
90
|
+
resolveModelDirectory
|
|
44
91
|
};
|
|
45
92
|
//# sourceMappingURL=project-paths.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/project-paths.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Project Path Utilities\n *\n * Handles finding project root and related paths in the DR project structure:\n *\n * my-project/\n * \u251C\u2500\u2500 .dr/ # Reference spec (ephemeral, git-ignored)\n * \u2502 \u251C\u2500\u2500 manifest.json # Installed spec version\n * \u2502 \u2514\u2500\u2500 schemas/\n * \u2514\u2500\u2500 documentation-robotics/ # Project model (permanent, git-committed)\n * \u251C\u2500\u2500 model/\n * \u2502 \u251C\u2500\u2500 manifest.yaml # Model spec version\n * \u2502 \u2514\u2500\u2500 layers/\n * \u251C\u2500\u2500 annotations/\n * \u2514\u2500\u2500 changesets/\n */\n\nimport { fileExists } from \"./file-io.js\";\nimport { join, dirname } from \"path\";\n\nconst MAX_SEARCH_DEPTH = 5;\nconst PROJECT_FOLDER_NAME = \"documentation-robotics\";\nconst SPEC_FOLDER_NAME = \".dr\";\n\n/**\n * Find the project root by searching for the documentation-robotics/ folder\n *\n * Searches upward from the current working directory up to MAX_SEARCH_DEPTH levels.\n * Returns the directory containing the documentation_robotics/ folder.\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to project root, or null if not found\n *\n * @example\n * // From /home/user/my-project/documentation-robotics/model/01_motivation\n * // Returns: /home/user/my-project\n * const root = await findProjectRoot();\n */\nexport async function findProjectRoot(startPath?: string): Promise<string | null> {\n let currentPath = startPath || process.cwd();\n\n for (let depth = 0; depth < MAX_SEARCH_DEPTH; depth++) {\n const projectPath = join(currentPath, PROJECT_FOLDER_NAME);\n\n if (await fileExists(projectPath)) {\n return currentPath;\n }\n\n // Move up one directory\n const parentPath = dirname(currentPath);\n\n // Check if we've reached the root\n if (parentPath === currentPath) {\n break;\n }\n\n currentPath = parentPath;\n }\n\n return null;\n}\n\n/**\n * Get the path to the documentation-robotics/ folder\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to documentation-robotics/ folder, or null if not found\n */\nexport async function getDocumentationRobotsPath(startPath?: string): Promise<string | null> {\n const root = await findProjectRoot(startPath);\n return root ? join(root, PROJECT_FOLDER_NAME) : null;\n}\n\n/**\n * Get the path to the model directory (documentation-robotics/model/)\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to model directory, or null if not found\n */\nexport async function getModelPath(startPath?: string): Promise<string | null> {\n const drPath = await getDocumentationRobotsPath(startPath);\n if (!drPath) return null;\n\n const modelPath = join(drPath, \"model\");\n return (await fileExists(modelPath)) ? modelPath : null;\n}\n\n/**\n * Get the path to the .dr/ folder (spec reference directory)\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to .dr/ folder, or null if not found\n */\nexport async function getSpecReferencePath(startPath?: string): Promise<string | null> {\n const root = await findProjectRoot(startPath);\n if (!root) return null;\n\n const specPath = join(root, SPEC_FOLDER_NAME);\n return (await fileExists(specPath)) ? specPath : null;\n}\n\n/**\n * Check if currently in a DR project\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns True if documentation-robotics/ folder exists in hierarchy\n */\nexport async function isInDRProject(startPath?: string): Promise<boolean> {\n return (await findProjectRoot(startPath)) !== null;\n}\n"],
|
|
5
|
-
"mappings": "AAiBA,SAAS,kBAAkB;AAC3B,SAAS,MAAM,eAAe;
|
|
4
|
+
"sourcesContent": ["/**\n * Project Path Utilities\n *\n * Handles finding project root and related paths in the DR project structure:\n *\n * my-project/\n * \u251C\u2500\u2500 .dr/ # Reference spec (ephemeral, git-ignored)\n * \u2502 \u251C\u2500\u2500 manifest.json # Installed spec version\n * \u2502 \u2514\u2500\u2500 schemas/\n * \u2514\u2500\u2500 documentation-robotics/ # Project model (permanent, git-committed)\n * \u251C\u2500\u2500 model/\n * \u2502 \u251C\u2500\u2500 manifest.yaml # Model spec version\n * \u2502 \u2514\u2500\u2500 layers/\n * \u251C\u2500\u2500 annotations/\n * \u2514\u2500\u2500 changesets/\n */\n\nimport { fileExists } from \"./file-io.js\";\nimport { join, dirname, resolve } from \"path\";\nimport { stat } from \"fs/promises\";\nimport type { Stats } from \"fs\";\n\nconst MAX_SEARCH_DEPTH = 5;\nconst PROJECT_FOLDER_NAME = \"documentation-robotics\";\nconst SPEC_FOLDER_NAME = \".dr\";\nconst FARM_FILE_NAME = \"farm.yaml\";\n\n/**\n * Find the project root by searching for the documentation-robotics/ folder\n *\n * Searches upward from the current working directory up to MAX_SEARCH_DEPTH levels.\n * Returns the directory containing the documentation_robotics/ folder.\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to project root, or null if not found\n *\n * @example\n * // From /home/user/my-project/documentation-robotics/model/01_motivation\n * // Returns: /home/user/my-project\n * const root = await findProjectRoot();\n */\nexport async function findProjectRoot(startPath?: string): Promise<string | null> {\n let currentPath = startPath || process.cwd();\n\n for (let depth = 0; depth < MAX_SEARCH_DEPTH; depth++) {\n const projectPath = join(currentPath, PROJECT_FOLDER_NAME);\n\n if (await fileExists(projectPath)) {\n return currentPath;\n }\n\n // Move up one directory\n const parentPath = dirname(currentPath);\n\n // Check if we've reached the root\n if (parentPath === currentPath) {\n break;\n }\n\n currentPath = parentPath;\n }\n\n return null;\n}\n\n/**\n * Get the path to the documentation-robotics/ folder\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to documentation-robotics/ folder, or null if not found\n */\nexport async function getDocumentationRobotsPath(startPath?: string): Promise<string | null> {\n const root = await findProjectRoot(startPath);\n return root ? join(root, PROJECT_FOLDER_NAME) : null;\n}\n\n/**\n * Get the path to the model directory (documentation-robotics/model/)\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to model directory, or null if not found\n */\nexport async function getModelPath(startPath?: string): Promise<string | null> {\n const drPath = await getDocumentationRobotsPath(startPath);\n if (!drPath) return null;\n\n const modelPath = join(drPath, \"model\");\n return (await fileExists(modelPath)) ? modelPath : null;\n}\n\n/**\n * Get the path to the .dr/ folder (spec reference directory)\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to .dr/ folder, or null if not found\n */\nexport async function getSpecReferencePath(startPath?: string): Promise<string | null> {\n const root = await findProjectRoot(startPath);\n if (!root) return null;\n\n const specPath = join(root, SPEC_FOLDER_NAME);\n return (await fileExists(specPath)) ? specPath : null;\n}\n\n/**\n * Check if currently in a DR project\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns True if documentation-robotics/ folder exists in hierarchy\n */\nexport async function isInDRProject(startPath?: string): Promise<boolean> {\n return (await findProjectRoot(startPath)) !== null;\n}\n\n/**\n * Find the farm root by searching for the farm.yaml file\n *\n * Resolution order:\n * 1. DR_FARM_PATH environment variable (if set, must point to a directory with farm.yaml)\n * 2. Directory walk upward from startPath (default: process.cwd()) up to MAX_SEARCH_DEPTH levels\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to farm root, or null if not found\n *\n * @example\n * // From /home/user/architecture-farm/service-a-model/documentation-robotics/model\n * // Returns: /home/user/architecture-farm (if farm.yaml exists there)\n * const farmRoot = await findFarmRoot();\n *\n * @example\n * // With DR_FARM_PATH environment variable set\n * // Returns: /path/specified/in/env (if farm.yaml exists there)\n * const farmRoot = await findFarmRoot();\n */\nexport async function findFarmRoot(startPath?: string): Promise<string | null> {\n // Check DR_FARM_PATH environment variable first\n if (process.env.DR_FARM_PATH) {\n const envPath = resolve(process.env.DR_FARM_PATH);\n const farmFilePath = join(envPath, FARM_FILE_NAME);\n if (await fileExists(farmFilePath)) {\n return envPath;\n }\n // If DR_FARM_PATH is set but invalid, throw a clear error rather than silently returning null\n throw new Error(\n `Invalid DR_FARM_PATH: ${process.env.DR_FARM_PATH}\\n` +\n `Expected farm.yaml at: ${farmFilePath}\\n` +\n `Ensure DR_FARM_PATH points to a directory containing a farm.yaml file.`\n );\n }\n\n let currentPath = startPath || process.cwd();\n\n for (let depth = 0; depth < MAX_SEARCH_DEPTH; depth++) {\n const farmFilePath = join(currentPath, FARM_FILE_NAME);\n\n if (await fileExists(farmFilePath)) {\n return currentPath;\n }\n\n // Move up one directory\n const parentPath = dirname(currentPath);\n\n // Check if we've reached the root\n if (parentPath === currentPath) {\n break;\n }\n\n currentPath = parentPath;\n }\n\n return null;\n}\n\n/**\n * Layouts under which a model directory can be found below a base path.\n * - `standard`: `{base}/documentation-robotics/model/` (a project that self-hosts its model)\n * - `legacy`: `{base}/model/`\n * - `detached`: `{base}` itself holds `manifest.yaml` and the layer folders\n */\nexport type ModelLayout = \"standard\" | \"legacy\" | \"detached\";\n\nexport interface ResolvedModelDirectory {\n /** Absolute path of the directory that holds the model (manifest.yaml and layer folders) */\n modelDir: string;\n layout: ModelLayout;\n /** False when something that is not a directory sits at the resolved path */\n isDirectory: boolean;\n}\n\nconst MODEL_LAYOUT_CANDIDATES: ReadonlyArray<{ layout: ModelLayout; segments: string[] }> = [\n { layout: \"standard\", segments: [PROJECT_FOLDER_NAME, \"model\"] },\n { layout: \"legacy\", segments: [\"model\"] },\n { layout: \"detached\", segments: [] },\n];\n\n/**\n * Locate a model directory under `basePath`, trying the standard, legacy and detached\n * layouts in that order. Shared by the composed reference validator (resolving external\n * models) and `dr farm add` (adopting a codebase's self-hosted model).\n *\n * Only a missing path moves the search on to the next candidate; permission and I/O\n * errors propagate so callers never mistake them for \"no model here\".\n *\n * @param basePath - Project root, model folder or any path to probe\n * @param options.requireManifest - When true, a candidate counts only if it is a directory\n * that contains `manifest.yaml`; otherwise the first existing path wins (even a file, so\n * the caller can report it)\n * @returns The resolved directory, or null when none of the candidates exists\n */\nexport async function resolveModelDirectory(\n basePath: string,\n options: { requireManifest?: boolean } = {}\n): Promise<ResolvedModelDirectory | null> {\n const resolvedBase = resolve(basePath);\n\n for (const candidate of MODEL_LAYOUT_CANDIDATES) {\n const modelDir = join(resolvedBase, ...candidate.segments);\n const stats = await statIfExists(modelDir);\n if (!stats) {\n continue;\n }\n\n if (options.requireManifest) {\n if (!stats.isDirectory() || !(await statIfExists(join(modelDir, \"manifest.yaml\")))) {\n continue;\n }\n }\n\n return { modelDir, layout: candidate.layout, isDirectory: stats.isDirectory() };\n }\n\n return null;\n}\n\nasync function statIfExists(target: string): Promise<Stats | undefined> {\n try {\n return await stat(target);\n } catch (err) {\n if ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAiBA,SAAS,kBAAkB;AAC3B,SAAS,MAAM,SAAS,eAAe;AACvC,SAAS,YAAY;AAGrB,MAAM,mBAAmB,GACnB,sBAAsB,0BACtB,mBAAmB,OACnB,iBAAiB;AAgBvB,eAAsB,gBAAgB,WAAkB;AACtD,MAAI,cAAc,aAAa,QAAQ,IAAG;AAE1C,WAAS,QAAQ,GAAG,QAAQ,kBAAkB,SAAS;AACrD,UAAM,cAAc,KAAK,aAAa,mBAAmB;AAEzD,QAAI,MAAM,WAAW,WAAW;AAC9B,aAAO;AAIT,UAAM,aAAa,QAAQ,WAAW;AAGtC,QAAI,eAAe;AACjB;AAGF,kBAAc;EAChB;AAEA,SAAO;AACT;AAQA,eAAsB,2BAA2B,WAAkB;AACjE,QAAM,OAAO,MAAM,gBAAgB,SAAS;AAC5C,SAAO,OAAO,KAAK,MAAM,mBAAmB,IAAI;AAClD;AAQA,eAAsB,aAAa,WAAkB;AACnD,QAAM,SAAS,MAAM,2BAA2B,SAAS;AACzD,MAAI,CAAC;AAAQ,WAAO;AAEpB,QAAM,YAAY,KAAK,QAAQ,OAAO;AACtC,SAAQ,MAAM,WAAW,SAAS,IAAK,YAAY;AACrD;AAQA,eAAsB,qBAAqB,WAAkB;AAC3D,QAAM,OAAO,MAAM,gBAAgB,SAAS;AAC5C,MAAI,CAAC;AAAM,WAAO;AAElB,QAAM,WAAW,KAAK,MAAM,gBAAgB;AAC5C,SAAQ,MAAM,WAAW,QAAQ,IAAK,WAAW;AACnD;AAQA,eAAsB,cAAc,WAAkB;AACpD,SAAQ,MAAM,gBAAgB,SAAS,MAAO;AAChD;AAsBA,eAAsB,aAAa,WAAkB;AAEnD,MAAI,QAAQ,IAAI,cAAc;AAC5B,UAAM,UAAU,QAAQ,QAAQ,IAAI,YAAY,GAC1C,eAAe,KAAK,SAAS,cAAc;AACjD,QAAI,MAAM,WAAW,YAAY;AAC/B,aAAO;AAGT,UAAM,IAAI,MACR,yBAAyB,QAAQ,IAAI,YAAY;yBACvB,YAAY;uEACkC;EAE5E;AAEA,MAAI,cAAc,aAAa,QAAQ,IAAG;AAE1C,WAAS,QAAQ,GAAG,QAAQ,kBAAkB,SAAS;AACrD,UAAM,eAAe,KAAK,aAAa,cAAc;AAErD,QAAI,MAAM,WAAW,YAAY;AAC/B,aAAO;AAIT,UAAM,aAAa,QAAQ,WAAW;AAGtC,QAAI,eAAe;AACjB;AAGF,kBAAc;EAChB;AAEA,SAAO;AACT;AAkBA,MAAM,0BAAsF;EAC1F,EAAE,QAAQ,YAAY,UAAU,CAAC,qBAAqB,OAAO,EAAC;EAC9D,EAAE,QAAQ,UAAU,UAAU,CAAC,OAAO,EAAC;EACvC,EAAE,QAAQ,YAAY,UAAU,CAAA,EAAE;;AAiBpC,eAAsB,sBACpB,UACA,UAAyC,CAAA,GAAE;AAE3C,QAAM,eAAe,QAAQ,QAAQ;AAErC,aAAW,aAAa,yBAAyB;AAC/C,UAAM,WAAW,KAAK,cAAc,GAAG,UAAU,QAAQ,GACnD,QAAQ,MAAM,aAAa,QAAQ;AACzC,QAAK,SAID,UAAQ,oBACN,CAAC,MAAM,YAAW,KAAM,CAAE,MAAM,aAAa,KAAK,UAAU,eAAe,CAAC;AAKlF,aAAO,EAAE,UAAU,QAAQ,UAAU,QAAQ,aAAa,MAAM,YAAW,EAAE;EAC/E;AAEA,SAAO;AACT;AAEA,eAAe,aAAa,QAAc;AACxC,MAAI;AACF,WAAO,MAAM,KAAK,MAAM;EAC1B,SAAS,KAAK;AACZ,QAAK,KAA+B,SAAS;AAC3C;AAEF,UAAM;EACR;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class ReferencePathParseError extends Error {
|
|
2
|
+
kind = "malformed-qualified-path";
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message), this.name = "ReferencePathParseError", Object.setPrototypeOf(this, ReferencePathParseError.prototype);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, QUALIFIED_PREFIX_PATTERN = /^@([a-z0-9][a-z0-9_-]*)\//i, ELEMENT_PATH_PATTERN = /^[a-z_][a-z0-9_-]*(\.[a-z_][a-z0-9_-]*)+$/;
|
|
8
|
+
function parseReferencePath(path) {
|
|
9
|
+
if (!path || typeof path != "string" || path.trim() === "")
|
|
10
|
+
throw createParseError("Empty reference path");
|
|
11
|
+
const trimmedPath = path.trim(), qualifiedMatch = trimmedPath.match(QUALIFIED_PREFIX_PATTERN);
|
|
12
|
+
if (qualifiedMatch) {
|
|
13
|
+
const modelName = qualifiedMatch[1].toLowerCase(), segment = trimmedPath.substring(qualifiedMatch[0].length);
|
|
14
|
+
if (!segment || segment.trim() === "")
|
|
15
|
+
throw createParseError("Qualified reference missing segment after '@{model-name}/'");
|
|
16
|
+
if (UUID_PATTERN.test(segment))
|
|
17
|
+
throw createParseError("Qualified references cannot reference UUIDs; use '@{model-name}/{layer}.{type}.{name}' format");
|
|
18
|
+
if (!ELEMENT_PATH_PATTERN.test(segment))
|
|
19
|
+
throw createParseError(`Qualified reference has invalid element path: '${segment}'. Use format '@{model-name}/{layer}.{type}.{name}'`);
|
|
20
|
+
return {
|
|
21
|
+
modelName,
|
|
22
|
+
segment,
|
|
23
|
+
isQualified: !0
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (UUID_PATTERN.test(trimmedPath))
|
|
27
|
+
return {
|
|
28
|
+
modelName: void 0,
|
|
29
|
+
segment: trimmedPath,
|
|
30
|
+
isQualified: !1
|
|
31
|
+
};
|
|
32
|
+
if (ELEMENT_PATH_PATTERN.test(trimmedPath))
|
|
33
|
+
return {
|
|
34
|
+
modelName: void 0,
|
|
35
|
+
segment: trimmedPath,
|
|
36
|
+
isQualified: !1
|
|
37
|
+
};
|
|
38
|
+
throw createParseError(`Invalid reference path format: '${trimmedPath}'. Use one of: '{layer}.{type}.{name}', UUID, or '@{model-name}/{layer}.{type}.{name}'`);
|
|
39
|
+
}
|
|
40
|
+
function createParseError(message) {
|
|
41
|
+
return new ReferencePathParseError(message);
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
QUALIFIED_PREFIX_PATTERN,
|
|
45
|
+
ReferencePathParseError,
|
|
46
|
+
UUID_PATTERN,
|
|
47
|
+
parseReferencePath
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=reference-path-parser.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/reference-path-parser.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Reference path parser for qualified and unqualified element references.\n *\n * Supports parsing reference targets in the following formats:\n * - Unqualified: {layer}.{type}.{name} (e.g., motivation.goal.increase-revenue)\n * - Unqualified UUID: UUIDv4 format\n * - Qualified: @{model-name}/{layer}.{type}.{name} (e.g., @auth-service/api.operation.authenticate)\n *\n * The parser distinguishes between parse-level errors (malformed qualified paths)\n * and reference resolution errors (unknown model or broken reference).\n */\n\nexport interface ParsedReferencePath {\n /** Model qualifier if present (without @ or /) */\n modelName?: string;\n /** The underlying {layer}.{type}.{name} segment or UUID */\n segment: string;\n /** Whether this is a qualified reference */\n isQualified: boolean;\n}\n\nexport class ReferencePathParseError extends Error {\n readonly kind = \"malformed-qualified-path\" as const;\n\n constructor(message: string) {\n super(message);\n this.name = \"ReferencePathParseError\";\n Object.setPrototypeOf(this, ReferencePathParseError.prototype);\n }\n}\n\nexport const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nexport const QUALIFIED_PREFIX_PATTERN = /^@([a-z0-9][a-z0-9_-]*)\\//i;\nconst ELEMENT_PATH_PATTERN = /^[a-z_][a-z0-9_-]*(\\.[a-z_][a-z0-9_-]*)+$/;\n\n/**\n * Parse a reference path, extracting model qualifier and underlying segment.\n *\n * @param path The reference path string to parse\n * @returns Parsed reference information on success\n * @throws ReferencePathParseError if the path is malformed\n *\n * @example\n * // Unqualified path\n * parseReferencePath(\"motivation.goal.increase-revenue\")\n * // => { modelName: undefined, segment: \"motivation.goal.increase-revenue\", isQualified: false }\n *\n * // Qualified path\n * parseReferencePath(\"@auth-service/api.operation.authenticate\")\n * // => { modelName: \"auth-service\", segment: \"api.operation.authenticate\", isQualified: true }\n *\n * // UUID\n * parseReferencePath(\"550e8400-e29b-41d4-a716-446655440000\")\n * // => { modelName: undefined, segment: \"550e8400-e29b-41d4-a716-446655440000\", isQualified: false }\n *\n * // Qualified UUID (not yet supported, will throw)\n * parseReferencePath(\"@model/550e8400-e29b-41d4-a716-446655440000\")\n * // => throws ReferencePathParseError\n */\nexport function parseReferencePath(path: string): ParsedReferencePath {\n if (!path || typeof path !== \"string\" || path.trim() === \"\") {\n throw createParseError(\"Empty reference path\");\n }\n\n const trimmedPath = path.trim();\n\n // Check for qualified prefix (@model-name/)\n const qualifiedMatch = trimmedPath.match(QUALIFIED_PREFIX_PATTERN);\n\n if (qualifiedMatch) {\n const modelName = qualifiedMatch[1].toLowerCase();\n const segment = trimmedPath.substring(qualifiedMatch[0].length);\n\n // Validate segment format\n if (!segment || segment.trim() === \"\") {\n throw createParseError(\n \"Qualified reference missing segment after '@{model-name}/'\"\n );\n }\n\n // Qualified paths must use dot-separated format, not UUIDs\n if (UUID_PATTERN.test(segment)) {\n throw createParseError(\n \"Qualified references cannot reference UUIDs; use '@{model-name}/{layer}.{type}.{name}' format\"\n );\n }\n\n // Validate segment is a valid element path\n if (!ELEMENT_PATH_PATTERN.test(segment)) {\n throw createParseError(\n `Qualified reference has invalid element path: '${segment}'. Use format '@{model-name}/{layer}.{type}.{name}'`\n );\n }\n\n return {\n modelName,\n segment,\n isQualified: true,\n };\n }\n\n // Unqualified reference\n // Accept either UUID or dot-separated element path\n if (UUID_PATTERN.test(trimmedPath)) {\n return {\n modelName: undefined,\n segment: trimmedPath,\n isQualified: false,\n };\n }\n\n if (ELEMENT_PATH_PATTERN.test(trimmedPath)) {\n return {\n modelName: undefined,\n segment: trimmedPath,\n isQualified: false,\n };\n }\n\n // Invalid format\n throw createParseError(\n `Invalid reference path format: '${trimmedPath}'. ` +\n \"Use one of: '{layer}.{type}.{name}', UUID, or '@{model-name}/{layer}.{type}.{name}'\"\n );\n}\n\n\nfunction createParseError(message: string): ReferencePathParseError {\n return new ReferencePathParseError(message);\n}\n"],
|
|
5
|
+
"mappings": "AAqBM,MAAO,gCAAgC,MAAK;EACvC,OAAO;EAEhB,YAAY,SAAe;AACzB,UAAM,OAAO,GACb,KAAK,OAAO,2BACZ,OAAO,eAAe,MAAM,wBAAwB,SAAS;EAC/D;;AAGK,MAAM,eAAe,mEACf,2BAA2B,8BAClC,uBAAuB;AA0BvB,SAAU,mBAAmB,MAAY;AAC7C,MAAI,CAAC,QAAQ,OAAO,QAAS,YAAY,KAAK,KAAI,MAAO;AACvD,UAAM,iBAAiB,sBAAsB;AAG/C,QAAM,cAAc,KAAK,KAAI,GAGvB,iBAAiB,YAAY,MAAM,wBAAwB;AAEjE,MAAI,gBAAgB;AAClB,UAAM,YAAY,eAAe,CAAC,EAAE,YAAW,GACzC,UAAU,YAAY,UAAU,eAAe,CAAC,EAAE,MAAM;AAG9D,QAAI,CAAC,WAAW,QAAQ,KAAI,MAAO;AACjC,YAAM,iBACJ,4DAA4D;AAKhE,QAAI,aAAa,KAAK,OAAO;AAC3B,YAAM,iBACJ,+FAA+F;AAKnG,QAAI,CAAC,qBAAqB,KAAK,OAAO;AACpC,YAAM,iBACJ,kDAAkD,OAAO,qDAAqD;AAIlH,WAAO;MACL;MACA;MACA,aAAa;;EAEjB;AAIA,MAAI,aAAa,KAAK,WAAW;AAC/B,WAAO;MACL,WAAW;MACX,SAAS;MACT,aAAa;;AAIjB,MAAI,qBAAqB,KAAK,WAAW;AACvC,WAAO;MACL,WAAW;MACX,SAAS;MACT,aAAa;;AAKjB,QAAM,iBACJ,mCAAmC,WAAW,wFACyC;AAE3F;AAGA,SAAS,iBAAiB,SAAe;AACvC,SAAO,IAAI,wBAAwB,OAAO;AAC5C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,33 +1,40 @@
|
|
|
1
1
|
import { CLIError } from "./errors.js";
|
|
2
|
-
|
|
2
|
+
import { optionExample, optionName } from "./error-hints.js";
|
|
3
|
+
function validateSourceReferenceOptions(options, surface = "cli") {
|
|
4
|
+
const name = (option) => optionName(surface, option), example = (option, value) => optionExample(surface, option, value);
|
|
3
5
|
if (!(options.sourceFile || options.sourceSymbol || options.sourceProvenance || options.sourceRepoRemote || options.sourceRepoCommit)) {
|
|
4
|
-
process.stderr.isTTY && console.warn(
|
|
6
|
+
process.stderr.isTTY && console.warn(`Warning: no source provenance specified. Add ${name("sourceFile")} and ${name("sourceProvenance")} to maintain traceability for drift detection and /dr-sync workflows.`);
|
|
5
7
|
return;
|
|
6
8
|
}
|
|
9
|
+
if (options.sourceSymbol && !options.sourceFile)
|
|
10
|
+
throw new CLIError(`${name("sourceSymbol")} requires ${name("sourceFile")}`, 1, [
|
|
11
|
+
"Provide the source file that contains the symbol",
|
|
12
|
+
`Example: ${example("sourceFile", "src/auth.ts")} ${example("sourceSymbol", "validateToken")}`
|
|
13
|
+
]);
|
|
7
14
|
if (!options.sourceProvenance)
|
|
8
|
-
throw new CLIError("
|
|
15
|
+
throw new CLIError(`${name("sourceProvenance")} is required when specifying source reference`, 1, [
|
|
9
16
|
"Specify provenance type: extracted, manual, inferred, or generated"
|
|
10
17
|
]);
|
|
11
18
|
const fileOptionalProvenances = ["inferred", "generated"];
|
|
12
19
|
if (!options.sourceFile && !fileOptionalProvenances.includes(options.sourceProvenance))
|
|
13
|
-
throw new CLIError("
|
|
20
|
+
throw new CLIError(`${name("sourceFile")} is required when specifying source reference`, 1, [
|
|
14
21
|
"Provide source file path relative to repository root",
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
`Example: ${example("sourceFile", "src/services/auth.ts")}`,
|
|
23
|
+
`Note: ${name("sourceFile")} is optional when ${name("sourceProvenance")} is 'inferred' or 'generated'`
|
|
17
24
|
]);
|
|
18
25
|
const validProvenance = ["extracted", "manual", "inferred", "generated"];
|
|
19
26
|
if (!validProvenance.includes(options.sourceProvenance))
|
|
20
|
-
throw new CLIError(`Invalid
|
|
27
|
+
throw new CLIError(`Invalid ${name("sourceProvenance")} value: "${options.sourceProvenance}"`, 1, [
|
|
21
28
|
`Must be one of: ${validProvenance.join(", ")}`
|
|
22
29
|
]);
|
|
23
30
|
if (options.sourceRepoCommit && !/^[0-9a-f]{40}$/.test(options.sourceRepoCommit))
|
|
24
|
-
throw new CLIError(`Invalid
|
|
31
|
+
throw new CLIError(`Invalid ${name("sourceRepoCommit")} value: "${options.sourceRepoCommit}"`, 1, [
|
|
25
32
|
"Must be exactly 40 hexadecimal characters (full Git commit SHA)"
|
|
26
33
|
]);
|
|
27
34
|
if (options.sourceRepoRemote && !options.sourceRepoCommit)
|
|
28
|
-
throw new CLIError("
|
|
35
|
+
throw new CLIError(`${name("sourceRepoCommit")} is required when ${name("sourceRepoRemote")} is provided`, 1, ["Provide both remote URL and commit SHA for repository context"]);
|
|
29
36
|
if (options.sourceRepoCommit && !options.sourceRepoRemote)
|
|
30
|
-
throw new CLIError("
|
|
37
|
+
throw new CLIError(`${name("sourceRepoRemote")} is required when ${name("sourceRepoCommit")} is provided`, 1, ["Provide both remote URL and commit SHA for repository context"]);
|
|
31
38
|
}
|
|
32
39
|
function buildSourceReference(options) {
|
|
33
40
|
if (!options.sourceProvenance)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/source-reference.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Source Reference Utilities\n * Shared validation and builder functions for source reference options\n */\n\nimport type { SourceReference, ProvenanceType } from \"../types/index.js\";\nimport { CLIError } from \"./errors.js\";\n\nexport interface SourceReferenceOptions {\n sourceFile?: string;\n sourceSymbol?: string;\n sourceProvenance?: string;\n sourceRepoRemote?: string;\n sourceRepoCommit?: string;\n}\n\n/**\n * Validate source reference options\n */\nexport function validateSourceReferenceOptions(options: SourceReferenceOptions): void {\n const hasSourceOptions =\n options.sourceFile ||\n options.sourceSymbol ||\n options.sourceProvenance ||\n options.sourceRepoRemote ||\n options.sourceRepoCommit;\n\n if (!hasSourceOptions) {\n if (process.stderr.isTTY) {\n console.warn(\n
|
|
5
|
-
"mappings": "AAMA,SAAS,gBAAgB;
|
|
4
|
+
"sourcesContent": ["/**\n * Source Reference Utilities\n * Shared validation and builder functions for source reference options\n */\n\nimport type { SourceReference, ProvenanceType } from \"../types/index.js\";\nimport { CLIError } from \"./errors.js\";\nimport { optionExample, optionName, type HintOption, type HintSurface } from \"./error-hints.js\";\n\nexport interface SourceReferenceOptions {\n sourceFile?: string;\n sourceSymbol?: string;\n sourceProvenance?: string;\n sourceRepoRemote?: string;\n sourceRepoCommit?: string;\n}\n\n/**\n * Validate source reference options\n */\nexport function validateSourceReferenceOptions(\n options: SourceReferenceOptions,\n surface: HintSurface = \"cli\"\n): void {\n const name = (option: HintOption) => optionName(surface, option);\n const example = (option: HintOption, value: string) => optionExample(surface, option, value);\n const hasSourceOptions =\n options.sourceFile ||\n options.sourceSymbol ||\n options.sourceProvenance ||\n options.sourceRepoRemote ||\n options.sourceRepoCommit;\n\n if (!hasSourceOptions) {\n if (process.stderr.isTTY) {\n console.warn(\n `Warning: no source provenance specified. Add ${name(\"sourceFile\")} and ${name(\"sourceProvenance\")} to maintain traceability for drift detection and /dr-sync workflows.`\n );\n }\n return;\n }\n\n // Require source-symbol to have source-file (both or neither is not acceptable here)\n if (options.sourceSymbol && !options.sourceFile) {\n throw new CLIError(`${name(\"sourceSymbol\")} requires ${name(\"sourceFile\")}`, 1, [\n \"Provide the source file that contains the symbol\",\n `Example: ${example(\"sourceFile\", \"src/auth.ts\")} ${example(\"sourceSymbol\", \"validateToken\")}`,\n ]);\n }\n\n // Require provenance first so we can use it to determine file requirements\n if (!options.sourceProvenance) {\n throw new CLIError(`${name(\"sourceProvenance\")} is required when specifying source reference`, 1, [\n \"Specify provenance type: extracted, manual, inferred, or generated\",\n ]);\n }\n\n // For inferred and generated provenance, source file is optional (no code exists yet)\n const fileOptionalProvenances = [\"inferred\", \"generated\"];\n if (!options.sourceFile && !fileOptionalProvenances.includes(options.sourceProvenance)) {\n throw new CLIError(`${name(\"sourceFile\")} is required when specifying source reference`, 1, [\n \"Provide source file path relative to repository root\",\n `Example: ${example(\"sourceFile\", \"src/services/auth.ts\")}`,\n `Note: ${name(\"sourceFile\")} is optional when ${name(\"sourceProvenance\")} is 'inferred' or 'generated'`,\n ]);\n }\n\n // Validate provenance enum\n const validProvenance = [\"extracted\", \"manual\", \"inferred\", \"generated\"];\n if (!validProvenance.includes(options.sourceProvenance)) {\n throw new CLIError(`Invalid ${name(\"sourceProvenance\")} value: \"${options.sourceProvenance}\"`, 1, [\n `Must be one of: ${validProvenance.join(\", \")}`,\n ]);\n }\n\n // Validate commit SHA format if provided\n if (options.sourceRepoCommit && !/^[0-9a-f]{40}$/.test(options.sourceRepoCommit)) {\n throw new CLIError(`Invalid ${name(\"sourceRepoCommit\")} value: \"${options.sourceRepoCommit}\"`, 1, [\n \"Must be exactly 40 hexadecimal characters (full Git commit SHA)\",\n ]);\n }\n\n // Require both or neither repo context fields\n if (options.sourceRepoRemote && !options.sourceRepoCommit) {\n throw new CLIError(\n `${name(\"sourceRepoCommit\")} is required when ${name(\"sourceRepoRemote\")} is provided`,\n 1,\n [\"Provide both remote URL and commit SHA for repository context\"]\n );\n }\n\n if (options.sourceRepoCommit && !options.sourceRepoRemote) {\n throw new CLIError(\n `${name(\"sourceRepoRemote\")} is required when ${name(\"sourceRepoCommit\")} is provided`,\n 1,\n [\"Provide both remote URL and commit SHA for repository context\"]\n );\n }\n}\n\n/**\n * Build SourceReference object from options\n */\nexport function buildSourceReference(options: SourceReferenceOptions): SourceReference | undefined {\n if (!options.sourceProvenance) {\n return undefined;\n }\n\n const reference: SourceReference = {\n provenance: options.sourceProvenance as ProvenanceType,\n };\n\n if (options.sourceFile) {\n reference.locations = [\n {\n file: options.sourceFile,\n ...(options.sourceSymbol && { symbol: options.sourceSymbol }),\n },\n ];\n }\n\n if (options.sourceRepoRemote && options.sourceRepoCommit) {\n reference.repository = {\n url: options.sourceRepoRemote,\n commit: options.sourceRepoCommit,\n };\n }\n\n return reference;\n}\n"],
|
|
5
|
+
"mappings": "AAMA,SAAS,gBAAgB;AACzB,SAAS,eAAe,kBAAqD;AAavE,SAAU,+BACd,SACA,UAAuB,OAAK;AAE5B,QAAM,OAAO,CAAC,WAAuB,WAAW,SAAS,MAAM,GACzD,UAAU,CAAC,QAAoB,UAAkB,cAAc,SAAS,QAAQ,KAAK;AAQ3F,MAAI,EANF,QAAQ,cACR,QAAQ,gBACR,QAAQ,oBACR,QAAQ,oBACR,QAAQ,mBAEa;AACrB,IAAI,QAAQ,OAAO,SACjB,QAAQ,KACN,gDAAgD,KAAK,YAAY,CAAC,QAAQ,KAAK,kBAAkB,CAAC,uEAAuE;AAG7K;EACF;AAGA,MAAI,QAAQ,gBAAgB,CAAC,QAAQ;AACnC,UAAM,IAAI,SAAS,GAAG,KAAK,cAAc,CAAC,aAAa,KAAK,YAAY,CAAC,IAAI,GAAG;MAC9E;MACA,YAAY,QAAQ,cAAc,aAAa,CAAC,IAAI,QAAQ,gBAAgB,eAAe,CAAC;KAC7F;AAIH,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,SAAS,GAAG,KAAK,kBAAkB,CAAC,iDAAiD,GAAG;MAChG;KACD;AAIH,QAAM,0BAA0B,CAAC,YAAY,WAAW;AACxD,MAAI,CAAC,QAAQ,cAAc,CAAC,wBAAwB,SAAS,QAAQ,gBAAgB;AACnF,UAAM,IAAI,SAAS,GAAG,KAAK,YAAY,CAAC,iDAAiD,GAAG;MAC1F;MACA,YAAY,QAAQ,cAAc,sBAAsB,CAAC;MACzD,SAAS,KAAK,YAAY,CAAC,qBAAqB,KAAK,kBAAkB,CAAC;KACzE;AAIH,QAAM,kBAAkB,CAAC,aAAa,UAAU,YAAY,WAAW;AACvE,MAAI,CAAC,gBAAgB,SAAS,QAAQ,gBAAgB;AACpD,UAAM,IAAI,SAAS,WAAW,KAAK,kBAAkB,CAAC,YAAY,QAAQ,gBAAgB,KAAK,GAAG;MAChG,mBAAmB,gBAAgB,KAAK,IAAI,CAAC;KAC9C;AAIH,MAAI,QAAQ,oBAAoB,CAAC,iBAAiB,KAAK,QAAQ,gBAAgB;AAC7E,UAAM,IAAI,SAAS,WAAW,KAAK,kBAAkB,CAAC,YAAY,QAAQ,gBAAgB,KAAK,GAAG;MAChG;KACD;AAIH,MAAI,QAAQ,oBAAoB,CAAC,QAAQ;AACvC,UAAM,IAAI,SACR,GAAG,KAAK,kBAAkB,CAAC,qBAAqB,KAAK,kBAAkB,CAAC,gBACxE,GACA,CAAC,+DAA+D,CAAC;AAIrE,MAAI,QAAQ,oBAAoB,CAAC,QAAQ;AACvC,UAAM,IAAI,SACR,GAAG,KAAK,kBAAkB,CAAC,qBAAqB,KAAK,kBAAkB,CAAC,gBACxE,GACA,CAAC,+DAA+D,CAAC;AAGvE;AAKM,SAAU,qBAAqB,SAA+B;AAClE,MAAI,CAAC,QAAQ;AACX;AAGF,QAAM,YAA6B;IACjC,YAAY,QAAQ;;AAGtB,SAAI,QAAQ,eACV,UAAU,YAAY;IACpB;MACE,MAAM,QAAQ;MACd,GAAI,QAAQ,gBAAgB,EAAE,QAAQ,QAAQ,aAAY;;MAK5D,QAAQ,oBAAoB,QAAQ,qBACtC,UAAU,aAAa;IACrB,KAAK,QAAQ;IACb,QAAQ,QAAQ;MAIb;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { readJSON, fileExists } from "./file-io.js";
|
|
2
2
|
import { join } from "path";
|
|
3
|
-
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
const BUNDLED_SPEC_VERSION = "0.11.0";
|
|
5
|
+
function resolveDevVersion() {
|
|
6
|
+
try {
|
|
7
|
+
return createRequire(import.meta.url)("../../package.json").version ?? "0.0.0";
|
|
8
|
+
} catch (error) {
|
|
9
|
+
return console.error(`Failed to resolve dev-mode CLI version from package.json, falling back to "0.0.0": ${error instanceof Error ? error.message : String(error)}`), "0.0.0";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
4
12
|
function getCliVersion() {
|
|
5
|
-
return
|
|
13
|
+
return "0.3.0";
|
|
6
14
|
}
|
|
7
15
|
function getCliBundledSpecVersion() {
|
|
8
16
|
return BUNDLED_SPEC_VERSION;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/spec-version.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Spec Version Utilities\n *\n * Manages spec version information for the CLI\n */\n\nimport { readJSON, fileExists } from \"./file-io.js\";\nimport { join } from \"path\";\n\n/**\n *
|
|
5
|
-
"mappings": "AAMA,SAAS,UAAU,kBAAkB;AACrC,SAAS,YAAY;
|
|
4
|
+
"sourcesContent": ["/**\n * Spec Version Utilities\n *\n * Manages spec version information for the CLI\n */\n\nimport { readJSON, fileExists } from \"./file-io.js\";\nimport { join } from \"path\";\nimport { createRequire } from \"node:module\";\n\n// Declare build-time constant (substituted by esbuild)\ndeclare const CLI_VERSION: string;\n\n/**\n * Bundled spec version\n * This should match the version in ../../../spec/VERSION\n * Updated during build process\n */\nconst BUNDLED_SPEC_VERSION = \"0.11.0\";\n\n// Dev-mode fallback (e.g. `tsx src/cli.ts`) that reads package.json directly, so it can't drift.\nfunction resolveDevVersion(): string {\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\") as { version?: string };\n return pkg.version ?? \"0.0.0\";\n } catch (error) {\n console.error(\n `Failed to resolve dev-mode CLI version from package.json, falling back to \"0.0.0\": ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n return \"0.0.0\";\n }\n}\n\n/**\n * Get the CLI version\n *\n * @returns CLI version string\n */\nexport function getCliVersion(): string {\n return typeof CLI_VERSION !== \"undefined\" ? CLI_VERSION : resolveDevVersion();\n}\n\n/**\n * Get the CLI's bundled spec version\n *\n * This is the version of the DR specification that is bundled with this CLI.\n * It represents what version of the spec this CLI supports.\n *\n * @returns Bundled spec version string\n */\nexport function getCliBundledSpecVersion(): string {\n return BUNDLED_SPEC_VERSION;\n}\n\n/**\n * Read spec version from .dr/manifest.json (installed spec reference)\n *\n * @param drPath - Path to .dr/ directory\n * @returns Spec version from .dr/manifest.json, or null if not found\n */\nexport async function getInstalledSpecVersion(drPath: string): Promise<string | null> {\n const manifestPath = join(drPath, \"manifest.json\");\n\n if (!(await fileExists(manifestPath))) {\n return null;\n }\n\n try {\n const manifest = await readJSON<{ specVersion?: string }>(manifestPath);\n return manifest.specVersion || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read spec version from documentation_robotics/model/manifest.yaml\n *\n * @param modelPath - Path to model directory\n * @returns Spec version from model manifest, or null if not found\n */\nexport async function getModelSpecVersion(modelPath: string): Promise<string | null> {\n const manifestPath = join(modelPath, \"manifest.yaml\");\n\n if (!(await fileExists(manifestPath))) {\n return null;\n }\n\n try {\n // Use YAML parser\n const yaml = await import(\"yaml\");\n const fs = await import(\"fs/promises\");\n const content = await fs.readFile(manifestPath, \"utf-8\");\n const manifest = yaml.parse(content) as { spec_version?: string; specVersion?: string };\n // Check snake_case first (Python CLI format), then camelCase (legacy)\n return manifest.spec_version || manifest.specVersion || null;\n } catch {\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAMA,SAAS,UAAU,kBAAkB;AACrC,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAU9B,MAAM,uBAAuB;AAG7B,SAAS,oBAAiB;AACxB,MAAI;AAGF,WAFgB,cAAc,YAAY,GAAG,EACzB,oBAAoB,EAC7B,WAAW;EACxB,SAAS,OAAO;AACd,mBAAQ,MACN,sFACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CACvD,EAAE,GAEG;EACT;AACF;AAOM,SAAU,gBAAa;AAC3B,SAA4C;AAC9C;AAUM,SAAU,2BAAwB;AACtC,SAAO;AACT;AAQA,eAAsB,wBAAwB,QAAc;AAC1D,QAAM,eAAe,KAAK,QAAQ,eAAe;AAEjD,MAAI,CAAE,MAAM,WAAW,YAAY;AACjC,WAAO;AAGT,MAAI;AAEF,YADiB,MAAM,SAAmC,YAAY,GACtD,eAAe;EACjC,QAAQ;AACN,WAAO;EACT;AACF;AAQA,eAAsB,oBAAoB,WAAiB;AACzD,QAAM,eAAe,KAAK,WAAW,eAAe;AAEpD,MAAI,CAAE,MAAM,WAAW,YAAY;AACjC,WAAO;AAGT,MAAI;AAEF,UAAM,OAAO,MAAM,OAAO,MAAM,GAE1B,UAAU,OADL,MAAM,OAAO,aAAa,GACZ,SAAS,cAAc,OAAO,GACjD,WAAW,KAAK,MAAM,OAAO;AAEnC,WAAO,SAAS,gBAAgB,SAAS,eAAe;EAC1D,QAAQ;AACN,WAAO;EACT;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|