@documentation-robotics/cli 0.1.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +39 -1
  2. package/dist/analyzers/verify-engine.js +22 -19
  3. package/dist/analyzers/verify-engine.js.map +2 -2
  4. package/dist/cli.js +101 -27
  5. package/dist/cli.js.map +3 -3
  6. package/dist/coding-agents/agents/claude-code.js +3 -1
  7. package/dist/coding-agents/agents/claude-code.js.map +2 -2
  8. package/dist/coding-agents/claude-code-client.js +2 -1
  9. package/dist/coding-agents/claude-code-client.js.map +2 -2
  10. package/dist/coding-agents/copilot-client.js +17 -12
  11. package/dist/coding-agents/copilot-client.js.map +2 -2
  12. package/dist/coding-agents/default-permissions.js +122 -0
  13. package/dist/coding-agents/default-permissions.js.map +7 -0
  14. package/dist/commands/add.js +10 -6
  15. package/dist/commands/add.js.map +2 -2
  16. package/dist/commands/analyzer.js +18 -10
  17. package/dist/commands/analyzer.js.map +2 -2
  18. package/dist/commands/audit.js +6 -6
  19. package/dist/commands/audit.js.map +2 -2
  20. package/dist/commands/changeset.js +238 -145
  21. package/dist/commands/changeset.js.map +2 -2
  22. package/dist/commands/delete.js +45 -18
  23. package/dist/commands/delete.js.map +3 -3
  24. package/dist/commands/element.js +3 -2
  25. package/dist/commands/element.js.map +2 -2
  26. package/dist/commands/farm.js +657 -0
  27. package/dist/commands/farm.js.map +7 -0
  28. package/dist/commands/init.js +11 -1
  29. package/dist/commands/init.js.map +2 -2
  30. package/dist/commands/mcp.js +107 -0
  31. package/dist/commands/mcp.js.map +7 -0
  32. package/dist/commands/relationship.js +53 -30
  33. package/dist/commands/relationship.js.map +2 -2
  34. package/dist/commands/reports.js +1 -1
  35. package/dist/commands/reports.js.map +2 -2
  36. package/dist/commands/update.js +19 -6
  37. package/dist/commands/update.js.map +2 -2
  38. package/dist/commands/validate.js +41 -3
  39. package/dist/commands/validate.js.map +2 -2
  40. package/dist/commands/version.js +2 -2
  41. package/dist/commands/version.js.map +2 -2
  42. package/dist/config.js +83 -0
  43. package/dist/config.js.map +7 -0
  44. package/dist/core/annotation-store.js +138 -0
  45. package/dist/core/annotation-store.js.map +7 -0
  46. package/dist/core/farm-manifest.js +134 -0
  47. package/dist/core/farm-manifest.js.map +7 -0
  48. package/dist/core/farm-sync-engine.js +346 -0
  49. package/dist/core/farm-sync-engine.js.map +7 -0
  50. package/dist/core/farm-sync-state.js +159 -0
  51. package/dist/core/farm-sync-state.js.map +7 -0
  52. package/dist/core/manifest.js +6 -3
  53. package/dist/core/manifest.js.map +2 -2
  54. package/dist/core/migration-registry.js +55 -1
  55. package/dist/core/migration-registry.js.map +2 -2
  56. package/dist/core/model.js +177 -16
  57. package/dist/core/model.js.map +3 -3
  58. package/dist/core/virtual-projection.js +1 -0
  59. package/dist/core/virtual-projection.js.map +2 -2
  60. package/dist/generated/node-types.js +1 -1
  61. package/dist/generated/node-types.js.map +2 -2
  62. package/dist/generated/relationship-index.js +45 -9
  63. package/dist/generated/relationship-index.js.map +2 -2
  64. package/dist/integrations/claude_code/docs/MCP_SERVER.md +55 -0
  65. package/dist/integrations/claude_code/docs/README.md +4 -0
  66. package/dist/mcp/api-key-manager.js +98 -0
  67. package/dist/mcp/api-key-manager.js.map +7 -0
  68. package/dist/mcp/http-transport.js +176 -0
  69. package/dist/mcp/http-transport.js.map +7 -0
  70. package/dist/mcp/resource-registry.js +97 -0
  71. package/dist/mcp/resource-registry.js.map +7 -0
  72. package/dist/mcp/tool-registry.js +44 -0
  73. package/dist/mcp/tool-registry.js.map +7 -0
  74. package/dist/mcp/tools/annotation-create.js +39 -0
  75. package/dist/mcp/tools/annotation-create.js.map +7 -0
  76. package/dist/mcp/tools/annotation-delete.js +29 -0
  77. package/dist/mcp/tools/annotation-delete.js.map +7 -0
  78. package/dist/mcp/tools/annotation-get.js +29 -0
  79. package/dist/mcp/tools/annotation-get.js.map +7 -0
  80. package/dist/mcp/tools/annotation-list.js +24 -0
  81. package/dist/mcp/tools/annotation-list.js.map +7 -0
  82. package/dist/mcp/tools/annotation-reply.js +34 -0
  83. package/dist/mcp/tools/annotation-reply.js.map +7 -0
  84. package/dist/mcp/tools/annotation-shared.js +10 -0
  85. package/dist/mcp/tools/annotation-shared.js.map +7 -0
  86. package/dist/mcp/tools/annotation-update.js +39 -0
  87. package/dist/mcp/tools/annotation-update.js.map +7 -0
  88. package/dist/mcp/tools/changeset-list.js +33 -0
  89. package/dist/mcp/tools/changeset-list.js.map +7 -0
  90. package/dist/mcp/tools/changeset-show.js +39 -0
  91. package/dist/mcp/tools/changeset-show.js.map +7 -0
  92. package/dist/mcp/tools/chat-launch.js +50 -0
  93. package/dist/mcp/tools/chat-launch.js.map +7 -0
  94. package/dist/mcp/tools/chat-status.js +26 -0
  95. package/dist/mcp/tools/chat-status.js.map +7 -0
  96. package/dist/mcp/tools/index.js +47 -0
  97. package/dist/mcp/tools/index.js.map +7 -0
  98. package/dist/mcp/tools/model-add.js +106 -0
  99. package/dist/mcp/tools/model-add.js.map +7 -0
  100. package/dist/mcp/tools/model-delete.js +99 -0
  101. package/dist/mcp/tools/model-delete.js.map +7 -0
  102. package/dist/mcp/tools/model-export.js +72 -0
  103. package/dist/mcp/tools/model-export.js.map +7 -0
  104. package/dist/mcp/tools/model-info.js +51 -0
  105. package/dist/mcp/tools/model-info.js.map +7 -0
  106. package/dist/mcp/tools/model-list.js +53 -0
  107. package/dist/mcp/tools/model-list.js.map +7 -0
  108. package/dist/mcp/tools/model-reload.js +34 -0
  109. package/dist/mcp/tools/model-reload.js.map +7 -0
  110. package/dist/mcp/tools/model-search.js +88 -0
  111. package/dist/mcp/tools/model-search.js.map +7 -0
  112. package/dist/mcp/tools/model-show.js +41 -0
  113. package/dist/mcp/tools/model-show.js.map +7 -0
  114. package/dist/mcp/tools/model-stats.js +22 -0
  115. package/dist/mcp/tools/model-stats.js.map +7 -0
  116. package/dist/mcp/tools/model-trace.js +55 -0
  117. package/dist/mcp/tools/model-trace.js.map +7 -0
  118. package/dist/mcp/tools/model-update.js +105 -0
  119. package/dist/mcp/tools/model-update.js.map +7 -0
  120. package/dist/mcp/tools/model-validate.js +78 -0
  121. package/dist/mcp/tools/model-validate.js.map +7 -0
  122. package/dist/mcp/tools/shared.js +103 -0
  123. package/dist/mcp/tools/shared.js.map +7 -0
  124. package/dist/reports/model-layer-report-generator.js +5 -3
  125. package/dist/reports/model-layer-report-generator.js.map +2 -2
  126. package/dist/reports/model-readme-generator.js +116 -0
  127. package/dist/reports/model-readme-generator.js.map +7 -0
  128. package/dist/reports/model-report-data.js +37 -1
  129. package/dist/reports/model-report-data.js.map +2 -2
  130. package/dist/reports/model-report-orchestrator.js +29 -6
  131. package/dist/reports/model-report-orchestrator.js.map +2 -2
  132. package/dist/reports/model-report-utils.js +9 -0
  133. package/dist/reports/model-report-utils.js.map +7 -0
  134. package/dist/schemas/bundled/analyzers/cbm.json +2 -2
  135. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  136. package/dist/schemas/bundled/api.json +1 -1
  137. package/dist/schemas/bundled/apm.json +1 -1
  138. package/dist/schemas/bundled/application.json +12 -1
  139. package/dist/schemas/bundled/base.json +1 -1
  140. package/dist/schemas/bundled/business.json +1 -1
  141. package/dist/schemas/bundled/data-model.json +38 -2
  142. package/dist/schemas/bundled/data-store.json +1 -1
  143. package/dist/schemas/bundled/manifest.json +4 -4
  144. package/dist/schemas/bundled/motivation.json +1 -1
  145. package/dist/schemas/bundled/navigation.json +1 -1
  146. package/dist/schemas/bundled/product.json +1 -1
  147. package/dist/schemas/bundled/security.json +1 -1
  148. package/dist/schemas/bundled/technology.json +1 -1
  149. package/dist/schemas/bundled/testing.json +1 -1
  150. package/dist/schemas/bundled/ux.json +1 -1
  151. package/dist/server/server.js +45 -7
  152. package/dist/server/server.js.map +2 -2
  153. package/dist/telemetry/config.js +2 -40
  154. package/dist/telemetry/config.js.map +2 -2
  155. package/dist/telemetry/index.js +1 -1
  156. package/dist/types/index.js.map +2 -2
  157. package/dist/utils/ansi-suppressor.js +25 -0
  158. package/dist/utils/ansi-suppressor.js.map +7 -0
  159. package/dist/utils/color-utils.js +34 -0
  160. package/dist/utils/color-utils.js.map +7 -0
  161. package/dist/utils/element-reference-shape.js +15 -1
  162. package/dist/utils/element-reference-shape.js.map +3 -3
  163. package/dist/utils/errors.js +69 -10
  164. package/dist/utils/errors.js.map +2 -2
  165. package/dist/utils/globals.js +17 -1
  166. package/dist/utils/globals.js.map +2 -2
  167. package/dist/utils/project-paths.js +24 -2
  168. package/dist/utils/project-paths.js.map +2 -2
  169. package/dist/utils/reference-path-parser.js +49 -0
  170. package/dist/utils/reference-path-parser.js.map +7 -0
  171. package/dist/utils/source-reference.js +5 -0
  172. package/dist/utils/source-reference.js.map +2 -2
  173. package/dist/utils/spec-version.js +10 -2
  174. package/dist/utils/spec-version.js.map +2 -2
  175. package/dist/validators/composed-reference-validator.js +262 -0
  176. package/dist/validators/composed-reference-validator.js.map +7 -0
  177. package/dist/validators/reference-validator.js +59 -8
  178. package/dist/validators/reference-validator.js.map +3 -3
  179. package/dist/validators/relationship-schema-validator.js +30 -2
  180. package/dist/validators/relationship-schema-validator.js.map +3 -3
  181. package/dist/viewer/README.md +6 -6
  182. package/dist/viewer/assets/main-B7AOQXXX.js +81 -0
  183. package/dist/viewer/assets/main-vdrFrG7q.css +1 -0
  184. package/dist/viewer/index.html +2 -2
  185. package/dist/viewer/manifest.json +10 -10
  186. package/dist/viewer/version.json +1 -1
  187. package/package.json +6 -3
  188. package/dist/viewer/assets/main-BRPZyc36.css +0 -1
  189. package/dist/viewer/assets/main-BrpLargo.js +0 -81
@@ -1,49 +1,11 @@
1
- import { homedir } from "node:os";
2
- import { existsSync } from "node:fs";
3
- import { readFile } from "node:fs/promises";
4
- import { parse } from "yaml";
5
- import { join } from "node:path";
6
- const CONFIG_FILENAME = ".dr-config.yaml";
1
+ import { loadDRConfig } from "../config.js";
7
2
  async function loadOTLPConfig() {
8
3
  const defaults = {
9
4
  endpoint: "http://localhost:4318/v1/traces",
10
5
  logsEndpoint: "http://localhost:4318/v1/logs",
11
6
  serviceName: "dr-cli",
12
7
  isExplicitlyConfigured: !1
13
- };
14
- let fileConfig = {};
15
- const configPath = process.env.DR_CONFIG_PATH ?? join(homedir(), CONFIG_FILENAME);
16
- if (existsSync(configPath))
17
- try {
18
- const content = await readFile(configPath, "utf-8");
19
- fileConfig = parse(content);
20
- } catch (error) {
21
- if (error instanceof Error) {
22
- const errorMsg = error.message;
23
- errorMsg.includes("EACCES") || errorMsg.includes("permission denied") ? (process.stderr.write(`Error: Cannot read config file ${configPath} - permission denied
24
- `), process.stderr.write(`Suggestions:
25
- `), process.stderr.write(` \u2022 Check file permissions with: ls -l ${configPath}
26
- `), process.stderr.write(` \u2022 Ensure you have read access to the file
27
- `), process.stderr.write(` \u2022 Try: chmod 644 ${configPath}
28
- `)) : errorMsg.includes("YAMLException") || error.name === "YAMLException" || errorMsg.includes("bad indentation") || errorMsg.includes("unexpected") ? (process.stderr.write(`Error: Invalid YAML syntax in ${configPath}
29
- `), process.stderr.write(`Details: ${errorMsg}
30
- `), process.stderr.write(`Suggestions:
31
- `), process.stderr.write(` \u2022 Validate your YAML syntax at https://www.yamllint.com/
32
- `), process.stderr.write(` \u2022 Check for proper indentation (use spaces, not tabs)
33
- `), process.stderr.write(` \u2022 Verify colons have spaces after them
34
- `)) : (process.stderr.write(`Error: Failed to load config file ${configPath}
35
- `), process.stderr.write(`Details: ${errorMsg}
36
- `), process.stderr.write(`Suggestions:
37
- `), process.stderr.write(` \u2022 Verify the file is valid UTF-8 encoded text
38
- `), process.stderr.write(` \u2022 Check if the file system is accessible
39
- `), process.stderr.write(` \u2022 Try recreating the file if it may be corrupted
40
- `)), process.stderr.write(`Using default OTLP configuration due to config file error
41
- `);
42
- } else
43
- process.stderr.write(`Warning: Failed to parse ${configPath}, using defaults
44
- `);
45
- }
46
- const otlp = fileConfig?.telemetry?.otlp, getEnvVar = (value) => value !== void 0 && value.trim().length > 0 ? value : void 0, getValidUrl = (url, fieldName = "URL") => {
8
+ }, otlp = (await loadDRConfig())?.telemetry?.otlp, getEnvVar = (value) => value !== void 0 && value.trim().length > 0 ? value : void 0, getValidUrl = (url, fieldName = "URL") => {
47
9
  if (!url)
48
10
  return;
49
11
  const trimmed = url.trim();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/telemetry/config.ts"],
4
- "sourcesContent": ["/**\n * OTLP Configuration Loader\n *\n * Loads and merges OTLP configuration from multiple sources with the following precedence:\n * 1. DR-specific env vars: DR_OTLP_ENDPOINT, DR_OTLP_LOGS_ENDPOINT, DR_OTLP_SERVICE_NAME (highest)\n * 2. ~/.dr-config.yaml file configuration\n * 3. Standard OTEL env vars: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,\n * OTEL_SERVICE_NAME \u2014 treated as a low-priority fallback so that ambient OTEL env vars set\n * by a parent process (e.g. Claude Code) do not silently override the DR config file.\n * 4. Hard-coded defaults (lowest priority)\n *\n * Why DR-specific env vars are highest: the standard OTEL_* vars are commonly set in developer\n * environments by other OTEL-instrumented tools (IDEs, shell profiles, CI). When dr runs as a\n * subprocess it inherits those vars and would silently misdirect spans. DR_OTLP_* vars are\n * unambiguously for dr and always win.\n *\n * Configuration file format (~/.dr-config.yaml):\n * ```yaml\n * telemetry:\n * otlp:\n * endpoint: 'http://localhost:4318/v1/traces'\n * logs_endpoint: 'http://localhost:4318/v1/logs'\n * service_name: 'dr-cli'\n * ```\n */\n\nimport { homedir } from \"node:os\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { parse } from \"yaml\";\nimport { join } from \"node:path\";\n\n/**\n * Configuration structure parsed from ~/.dr-config.yaml\n */\ninterface DRConfig {\n telemetry?: {\n otlp?: {\n endpoint?: string;\n logs_endpoint?: string;\n service_name?: string;\n };\n };\n}\n\n/**\n * Resolved OTLP configuration with all required fields\n */\nexport interface OTLPConfig {\n endpoint: string;\n logsEndpoint: string;\n serviceName: string;\n /** True only when an endpoint was explicitly set via env var or config file. */\n isExplicitlyConfigured: boolean;\n}\n\nconst CONFIG_FILENAME = \".dr-config.yaml\";\n\n/**\n * Load OTLP configuration from multiple sources.\n *\n * Configuration precedence (each field resolved independently):\n * 1. Environment variables (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_SERVICE_NAME)\n * 2. File configuration (~/.dr-config.yaml)\n * 3. Hard-coded defaults\n *\n * All endpoint URLs are validated. Invalid URLs are treated as not set and fall back to next priority.\n *\n * @returns Resolved OTLP configuration with all required fields\n *\n * @example\n * ```typescript\n * const config = await loadOTLPConfig();\n * console.log(config.endpoint); // 'http://localhost:4318/v1/traces' (or from env/file)\n * ```\n */\nexport async function loadOTLPConfig(): Promise<OTLPConfig> {\n // Hard-coded defaults (Priority 3)\n const defaults: OTLPConfig = {\n endpoint: \"http://localhost:4318/v1/traces\",\n logsEndpoint: \"http://localhost:4318/v1/logs\",\n serviceName: \"dr-cli\",\n isExplicitlyConfigured: false,\n };\n\n // Load file configuration (Priority 2)\n let fileConfig: DRConfig = {};\n // Support DR_CONFIG_PATH override for testing; default to ~/.dr-config.yaml\n const configPath = process.env.DR_CONFIG_PATH ?? join(homedir(), CONFIG_FILENAME);\n\n if (existsSync(configPath)) {\n try {\n const content = await readFile(configPath, \"utf-8\");\n fileConfig = parse(content) as DRConfig;\n } catch (error) {\n // Provide specific error messages based on error type\n if (error instanceof Error) {\n const errorMsg = error.message;\n\n // File permission errors\n if (errorMsg.includes(\"EACCES\") || errorMsg.includes(\"permission denied\")) {\n process.stderr.write(\n `Error: Cannot read config file ${configPath} - permission denied\\n`\n );\n process.stderr.write(\"Suggestions:\\n\");\n process.stderr.write(` \u2022 Check file permissions with: ls -l ${configPath}\\n`);\n process.stderr.write(\" \u2022 Ensure you have read access to the file\\n\");\n process.stderr.write(` \u2022 Try: chmod 644 ${configPath}\\n`);\n }\n // YAML parse errors\n else if (\n errorMsg.includes(\"YAMLException\") ||\n error.name === \"YAMLException\" ||\n errorMsg.includes(\"bad indentation\") ||\n errorMsg.includes(\"unexpected\")\n ) {\n process.stderr.write(`Error: Invalid YAML syntax in ${configPath}\\n`);\n process.stderr.write(`Details: ${errorMsg}\\n`);\n process.stderr.write(\"Suggestions:\\n\");\n process.stderr.write(\" \u2022 Validate your YAML syntax at https://www.yamllint.com/\\n\");\n process.stderr.write(\" \u2022 Check for proper indentation (use spaces, not tabs)\\n\");\n process.stderr.write(\" \u2022 Verify colons have spaces after them\\n\");\n }\n // File encoding or other I/O errors\n else {\n process.stderr.write(`Error: Failed to load config file ${configPath}\\n`);\n process.stderr.write(`Details: ${errorMsg}\\n`);\n process.stderr.write(\"Suggestions:\\n\");\n process.stderr.write(\" \u2022 Verify the file is valid UTF-8 encoded text\\n\");\n process.stderr.write(\" \u2022 Check if the file system is accessible\\n\");\n process.stderr.write(\" \u2022 Try recreating the file if it may be corrupted\\n\");\n }\n process.stderr.write(`Using default OTLP configuration due to config file error\\n`);\n } else {\n process.stderr.write(`Warning: Failed to parse ${configPath}, using defaults\\n`);\n }\n }\n }\n\n const otlp = fileConfig?.telemetry?.otlp;\n\n // Helper to check if env var is effectively set (not empty after trim)\n const getEnvVar = (value: string | undefined) => {\n return value !== undefined && value.trim().length > 0 ? value : undefined;\n };\n\n // Helper to validate URL and return if valid, undefined if invalid\n const getValidUrl = (url: string | undefined, fieldName: string = \"URL\") => {\n if (!url) return undefined;\n const trimmed = url.trim();\n if (trimmed.length === 0) return undefined;\n\n try {\n const parsedUrl = new URL(trimmed);\n\n // Only accept http and https protocols\n if (parsedUrl.protocol !== \"http:\" && parsedUrl.protocol !== \"https:\") {\n const protocolMsg =\n parsedUrl.protocol === \"\" || parsedUrl.protocol === \":\"\n ? \"URL must include http:// or https:// scheme\"\n : `Invalid protocol \"${parsedUrl.protocol}\" (expected http or https)`;\n process.stderr.write(`Error: ${protocolMsg} in ${fieldName}\\n`);\n process.stderr.write(` Provided: ${trimmed}\\n`);\n process.stderr.write(` Expected: URL must start with http:// or https://\\n`);\n process.stderr.write(` Using fallback configuration for this field\\n`);\n return undefined;\n }\n\n return trimmed;\n } catch (error) {\n process.stderr.write(`Error: Invalid URL in ${fieldName}: \"${trimmed}\"\\n`);\n if (error instanceof Error) {\n process.stderr.write(` Reason: ${error.message}\\n`);\n }\n process.stderr.write(\"Suggestions:\\n\");\n process.stderr.write(\" \u2022 Ensure URL starts with http:// or https://\\n\");\n process.stderr.write(\" \u2022 Check for typos in hostname\\n\");\n process.stderr.write(\" \u2022 Verify port number if specified (e.g., :4318)\\n\");\n process.stderr.write(\" \u2022 Example: http://localhost:4318/v1/traces\\n\");\n process.stderr.write(` Using fallback configuration for this field\\n`);\n return undefined;\n }\n };\n\n // Resolve each field using the 4-level precedence chain:\n // 1. DR-specific env vars (unambiguously for dr, never set by other tools)\n // 2. ~/.dr-config.yaml (explicit user config for dr)\n // 3. Standard OTEL env vars (low-priority fallback; may be set by parent processes)\n // 4. Hard-coded defaults\n //\n // Standard OTEL env vars are intentionally below the config file. When dr runs as a\n // subprocess of another OTEL-instrumented tool (e.g. Claude Code), it inherits those\n // env vars and would otherwise silently redirect spans to the wrong collector.\n const explicitEndpoint =\n getValidUrl(getEnvVar(process.env.DR_OTLP_ENDPOINT), \"DR_OTLP_ENDPOINT\") ??\n getValidUrl(otlp?.endpoint, \"telemetry.otlp.endpoint\") ??\n getValidUrl(getEnvVar(process.env.OTEL_EXPORTER_OTLP_ENDPOINT), \"OTEL_EXPORTER_OTLP_ENDPOINT\");\n\n const explicitLogsEndpoint =\n getValidUrl(getEnvVar(process.env.DR_OTLP_LOGS_ENDPOINT), \"DR_OTLP_LOGS_ENDPOINT\") ??\n getValidUrl(otlp?.logs_endpoint, \"telemetry.otlp.logs_endpoint\") ??\n getValidUrl(getEnvVar(process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT), \"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT\");\n\n return {\n endpoint: explicitEndpoint ?? defaults.endpoint,\n logsEndpoint: explicitLogsEndpoint ?? defaults.logsEndpoint,\n serviceName:\n getEnvVar(process.env.DR_OTLP_SERVICE_NAME) ??\n otlp?.service_name ??\n getEnvVar(process.env.OTEL_SERVICE_NAME) ??\n defaults.serviceName,\n // True only when at least one endpoint was explicitly provided. Without this,\n // TELEMETRY_ENABLED=true (build-time) would try to connect to localhost:4318\n // even when no collector is running, causing a hang at shutdown.\n isExplicitlyConfigured: !!(explicitEndpoint || explicitLogsEndpoint),\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * OTLP Configuration Loader\n *\n * Loads and merges OTLP configuration from multiple sources with the following precedence:\n * 1. DR-specific env vars: DR_OTLP_ENDPOINT, DR_OTLP_LOGS_ENDPOINT, DR_OTLP_SERVICE_NAME (highest)\n * 2. ~/.dr-config.yaml file configuration\n * 3. Standard OTEL env vars: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,\n * OTEL_SERVICE_NAME \u2014 treated as a low-priority fallback so that ambient OTEL env vars set\n * by a parent process (e.g. Claude Code) do not silently override the DR config file.\n * 4. Hard-coded defaults (lowest priority)\n *\n * Why DR-specific env vars are highest: the standard OTEL_* vars are commonly set in developer\n * environments by other OTEL-instrumented tools (IDEs, shell profiles, CI). When dr runs as a\n * subprocess it inherits those vars and would silently misdirect spans. DR_OTLP_* vars are\n * unambiguously for dr and always win.\n *\n * Configuration file format (~/.dr-config.yaml):\n * ```yaml\n * telemetry:\n * otlp:\n * endpoint: 'http://localhost:4318/v1/traces'\n * logs_endpoint: 'http://localhost:4318/v1/logs'\n * service_name: 'dr-cli'\n * ```\n */\n\nimport { loadDRConfig } from \"../config.js\";\n\n/**\n * Resolved OTLP configuration with all required fields\n */\nexport interface OTLPConfig {\n endpoint: string;\n logsEndpoint: string;\n serviceName: string;\n /** True only when an endpoint was explicitly set via env var or config file. */\n isExplicitlyConfigured: boolean;\n}\n\n/**\n * Load OTLP configuration from multiple sources.\n *\n * Configuration precedence (each field resolved independently):\n * 1. Environment variables (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_SERVICE_NAME)\n * 2. File configuration (~/.dr-config.yaml)\n * 3. Hard-coded defaults\n *\n * All endpoint URLs are validated. Invalid URLs are treated as not set and fall back to next priority.\n *\n * @returns Resolved OTLP configuration with all required fields\n *\n * @example\n * ```typescript\n * const config = await loadOTLPConfig();\n * console.log(config.endpoint); // 'http://localhost:4318/v1/traces' (or from env/file)\n * ```\n */\nexport async function loadOTLPConfig(): Promise<OTLPConfig> {\n // Hard-coded defaults (Priority 3)\n const defaults: OTLPConfig = {\n endpoint: \"http://localhost:4318/v1/traces\",\n logsEndpoint: \"http://localhost:4318/v1/logs\",\n serviceName: \"dr-cli\",\n isExplicitlyConfigured: false,\n };\n\n // Load file configuration (Priority 2) via the shared DR config loader.\n // Malformed/unreadable config files are reported to stderr there and\n // resolve to an empty object here, matching prior loadOTLPConfig() behavior.\n const fileConfig = await loadDRConfig();\n const otlp = fileConfig?.telemetry?.otlp;\n\n // Helper to check if env var is effectively set (not empty after trim)\n const getEnvVar = (value: string | undefined) => {\n return value !== undefined && value.trim().length > 0 ? value : undefined;\n };\n\n // Helper to validate URL and return if valid, undefined if invalid\n const getValidUrl = (url: string | undefined, fieldName: string = \"URL\") => {\n if (!url) return undefined;\n const trimmed = url.trim();\n if (trimmed.length === 0) return undefined;\n\n try {\n const parsedUrl = new URL(trimmed);\n\n // Only accept http and https protocols\n if (parsedUrl.protocol !== \"http:\" && parsedUrl.protocol !== \"https:\") {\n const protocolMsg =\n parsedUrl.protocol === \"\" || parsedUrl.protocol === \":\"\n ? \"URL must include http:// or https:// scheme\"\n : `Invalid protocol \"${parsedUrl.protocol}\" (expected http or https)`;\n process.stderr.write(`Error: ${protocolMsg} in ${fieldName}\\n`);\n process.stderr.write(` Provided: ${trimmed}\\n`);\n process.stderr.write(` Expected: URL must start with http:// or https://\\n`);\n process.stderr.write(` Using fallback configuration for this field\\n`);\n return undefined;\n }\n\n return trimmed;\n } catch (error) {\n process.stderr.write(`Error: Invalid URL in ${fieldName}: \"${trimmed}\"\\n`);\n if (error instanceof Error) {\n process.stderr.write(` Reason: ${error.message}\\n`);\n }\n process.stderr.write(\"Suggestions:\\n\");\n process.stderr.write(\" \u2022 Ensure URL starts with http:// or https://\\n\");\n process.stderr.write(\" \u2022 Check for typos in hostname\\n\");\n process.stderr.write(\" \u2022 Verify port number if specified (e.g., :4318)\\n\");\n process.stderr.write(\" \u2022 Example: http://localhost:4318/v1/traces\\n\");\n process.stderr.write(` Using fallback configuration for this field\\n`);\n return undefined;\n }\n };\n\n // Resolve each field using the 4-level precedence chain:\n // 1. DR-specific env vars (unambiguously for dr, never set by other tools)\n // 2. ~/.dr-config.yaml (explicit user config for dr)\n // 3. Standard OTEL env vars (low-priority fallback; may be set by parent processes)\n // 4. Hard-coded defaults\n //\n // Standard OTEL env vars are intentionally below the config file. When dr runs as a\n // subprocess of another OTEL-instrumented tool (e.g. Claude Code), it inherits those\n // env vars and would otherwise silently redirect spans to the wrong collector.\n const explicitEndpoint =\n getValidUrl(getEnvVar(process.env.DR_OTLP_ENDPOINT), \"DR_OTLP_ENDPOINT\") ??\n getValidUrl(otlp?.endpoint, \"telemetry.otlp.endpoint\") ??\n getValidUrl(getEnvVar(process.env.OTEL_EXPORTER_OTLP_ENDPOINT), \"OTEL_EXPORTER_OTLP_ENDPOINT\");\n\n const explicitLogsEndpoint =\n getValidUrl(getEnvVar(process.env.DR_OTLP_LOGS_ENDPOINT), \"DR_OTLP_LOGS_ENDPOINT\") ??\n getValidUrl(otlp?.logs_endpoint, \"telemetry.otlp.logs_endpoint\") ??\n getValidUrl(getEnvVar(process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT), \"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT\");\n\n return {\n endpoint: explicitEndpoint ?? defaults.endpoint,\n logsEndpoint: explicitLogsEndpoint ?? defaults.logsEndpoint,\n serviceName:\n getEnvVar(process.env.DR_OTLP_SERVICE_NAME) ??\n otlp?.service_name ??\n getEnvVar(process.env.OTEL_SERVICE_NAME) ??\n defaults.serviceName,\n // True only when at least one endpoint was explicitly provided. Without this,\n // TELEMETRY_ENABLED=true (build-time) would try to connect to localhost:4318\n // even when no collector is running, causing a hang at shutdown.\n isExplicitlyConfigured: !!(explicitEndpoint || explicitLogsEndpoint),\n };\n}\n"],
5
+ "mappings": "AA0BA,SAAS,oBAAoB;AA+B7B,eAAsB,iBAAc;AAElC,QAAM,WAAuB;IAC3B,UAAU;IACV,cAAc;IACd,aAAa;IACb,wBAAwB;KAOpB,QADa,MAAM,aAAY,IACZ,WAAW,MAG9B,YAAY,CAAC,UACV,UAAU,UAAa,MAAM,KAAI,EAAG,SAAS,IAAI,QAAQ,QAI5D,cAAc,CAAC,KAAyB,YAAoB,UAAS;AACzE,QAAI,CAAC;AAAK;AACV,UAAM,UAAU,IAAI,KAAI;AACxB,QAAI,QAAQ,WAAW;AAEvB,UAAI;AACF,cAAM,YAAY,IAAI,IAAI,OAAO;AAGjC,YAAI,UAAU,aAAa,WAAW,UAAU,aAAa,UAAU;AACrE,gBAAM,cACJ,UAAU,aAAa,MAAM,UAAU,aAAa,MAChD,gDACA,qBAAqB,UAAU,QAAQ;AAC7C,kBAAQ,OAAO,MAAM,UAAU,WAAW,OAAO,SAAS;CAAI,GAC9D,QAAQ,OAAO,MAAM,eAAe,OAAO;CAAI,GAC/C,QAAQ,OAAO,MAAM;CAAuD,GAC5E,QAAQ,OAAO,MAAM;CAAiD;AACtE;QACF;AAEA,eAAO;MACT,SAAS,OAAO;AACd,gBAAQ,OAAO,MAAM,yBAAyB,SAAS,MAAM,OAAO;CAAK,GACrE,iBAAiB,SACnB,QAAQ,OAAO,MAAM,aAAa,MAAM,OAAO;CAAI,GAErD,QAAQ,OAAO,MAAM;CAAgB,GACrC,QAAQ,OAAO,MAAM;CAAkD,GACvE,QAAQ,OAAO,MAAM;CAAmC,GACxD,QAAQ,OAAO,MAAM;CAAqD,GAC1E,QAAQ,OAAO,MAAM;CAAgD,GACrE,QAAQ,OAAO,MAAM;CAAiD;AACtE;MACF;EACF,GAWM,mBACJ,YAAY,UAAU,QAAQ,IAAI,gBAAgB,GAAG,kBAAkB,KACvE,YAAY,MAAM,UAAU,yBAAyB,KACrD,YAAY,UAAU,QAAQ,IAAI,2BAA2B,GAAG,6BAA6B,GAEzF,uBACJ,YAAY,UAAU,QAAQ,IAAI,qBAAqB,GAAG,uBAAuB,KACjF,YAAY,MAAM,eAAe,8BAA8B,KAC/D,YAAY,UAAU,QAAQ,IAAI,gCAAgC,GAAG,kCAAkC;AAEzG,SAAO;IACL,UAAU,oBAAoB,SAAS;IACvC,cAAc,wBAAwB,SAAS;IAC/C,aACE,UAAU,QAAQ,IAAI,oBAAoB,KAC1C,MAAM,gBACN,UAAU,QAAQ,IAAI,iBAAiB,KACvC,SAAS;;;;IAIX,wBAAwB,CAAC,EAAE,oBAAoB;;AAEnD;",
6
6
  "names": []
7
7
  }
@@ -21,7 +21,7 @@ async function initTelemetry() {
21
21
  `), process.stderr.write(` - Logs endpoint: ${otlpConfig.logsEndpoint}
22
22
  `), process.stderr.write(` - Service name: ${otlpConfig.serviceName}
23
23
  `));
24
- const cliVersion = "0.1.12";
24
+ const cliVersion = "0.2.0";
25
25
  let projectName = "unknown";
26
26
  try {
27
27
  const [{ readFileSync, existsSync }, { parse }] = await Promise.all([
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/types/index.ts"],
4
- "sourcesContent": ["/**\n * Core type definitions for Documentation Robotics CLI\n */\n\n// Import source reference types for use in Element interface\nimport type { SourceReference } from \"./source-reference.js\";\n\n// Export source reference types\nexport type {\n ProvenanceType,\n SourceLocation,\n RepositoryContext,\n SourceReference,\n} from \"./source-reference.js\";\n\n/**\n * Reference across layers\n */\nexport interface Reference {\n source: string; // Element ID\n target: string; // Element ID\n type: string; // Reference type\n description?: string;\n}\n\n/**\n * Intra-layer or cross-layer relationship\n */\nexport interface Relationship {\n source: string;\n target: string;\n predicate: string; // e.g., \"depends-on\", \"implements\"\n layer?: string; // Source element's layer ID\n targetLayer?: string; // Destination element's layer ID; present only for cross-layer relationships\n properties?: Record<string, unknown>;\n category?: \"structural\" | \"behavioral\"; // Relationship classification\n}\n\n/**\n * Element metadata for lifecycle and version tracking\n *\n * Note: created_at and updated_at should be treated as immutable after element initialization.\n * For safe external access with immutability guarantees, use Element.getMetadata() which\n * returns a defensive copy protecting against external mutations.\n */\nexport interface ElementMetadata {\n created_at?: string; // ISO 8601 timestamp (immutable after initialization)\n updated_at?: string; // ISO 8601 timestamp (immutable after initialization)\n created_by?: string;\n version?: number;\n}\n\n/**\n * Element representation\n *\n * Aligned with spec-node.schema.json structure for direct validation\n */\nexport interface Element {\n // Spec-node aligned fields (required in new format)\n id?: string; // UUIDv4 (e.g., \"550e8400-e29b-41d4-a716-446655440000\")\n path?: string; // Human-readable path: {layer}.{type}.{kebab-name} (e.g., \"motivation.goal.increase-revenue\")\n spec_node_id: string; // Reference to spec node type (e.g., \"motivation.goal\")\n type: string; // Denormalized node type (e.g., \"goal\", \"endpoint\")\n layer_id: string; // Denormalized layer ID (e.g., \"motivation\", \"api\")\n name: string;\n\n // Spec-node aligned optional fields\n description?: string;\n attributes?: Record<string, unknown>; // Type-specific properties\n source_reference?: SourceReference; // Provenance tracking\n metadata?: ElementMetadata; // Lifecycle tracking\n\n // Relationship tracking\n references?: Reference[];\n relationships?: Relationship[];\n\n // Internal tracking (unchanged)\n layer?: string;\n filePath?: string;\n rawData?: any;\n}\n\n/**\n * Layer container\n */\nexport interface LayerData {\n elements: Element[];\n metadata?: {\n layer: string;\n version: string;\n };\n}\n\n/**\n * Changeset application history entry\n * Records when changesets were committed or discarded in the model\n */\nexport interface ChangesetHistoryEntry {\n /** Name of the changeset that was committed or discarded */\n name: string;\n /** ISO 8601 timestamp when the action occurred */\n committed_at: string;\n /** Type of action performed */\n action: \"committed\" | \"discarded\";\n}\n\n/**\n * Manifest metadata\n */\nexport interface ManifestData {\n name: string;\n version: string;\n description?: string;\n author?: string;\n created?: string;\n modified?: string;\n specVersion?: string;\n /** Changeset history for the model */\n changeset_history?: ChangesetHistoryEntry[];\n}\n\n/**\n * Model options for customization\n */\nexport interface ModelOptions {\n enableCache?: boolean;\n lazyLoad?: boolean;\n referenceRegistry?: unknown; // Will be properly typed when implemented\n layers?: string[]; // If set, only these layers are loaded (selective loading)\n}\n\n/**\n * JSON Schema property definition\n * Represents a single property in a JSON Schema object\n */\nexport interface JsonSchemaProperty {\n type?: string | string[];\n description?: string;\n enum?: unknown[];\n default?: unknown;\n items?: JsonSchemaProperty;\n properties?: Record<string, JsonSchemaProperty>;\n required?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Tool parameter definition for Claude AI tools\n * Describes a single input parameter for a tool\n */\nexport interface ToolParameter {\n type: string;\n description: string;\n enum?: string[];\n items?: { type: string };\n [key: string]: unknown;\n}\n\n/**\n * Tool input schema definition\n * Defines the structure of inputs for a Claude AI tool\n */\nexport interface ToolInputSchema {\n type: \"object\";\n properties: Record<string, ToolParameter>;\n required: string[];\n}\n\n/**\n * Tool definition for Claude AI integration\n * Represents a single tool that Claude can call\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n input_schema: ToolInputSchema;\n}\n\n/**\n * Result of executing a tool\n * All tool results are returned as JSON-serializable objects\n */\nexport interface ToolExecutionResult {\n [key: string]: unknown;\n error?: string;\n}\n\n/**\n * SHA256 hash branded type for compile-time safety\n * Prevents accidental misuse of arbitrary strings as cryptographic checksums\n */\nexport type Sha256Hash = string & { readonly __brand: \"Sha256Hash\" };\n\n/**\n * Create a Sha256Hash from a hex string\n * This is the only valid way to construct a Sha256Hash value\n *\n * @param hash - The hex string representation of a SHA256 hash\n * @returns A branded Sha256Hash type\n * @throws Error if the hash is not a valid 64-character hex string\n */\nexport function createSha256Hash(hash: string): Sha256Hash {\n if (!/^[a-f0-9]{64}$/.test(hash)) {\n throw new Error(`Invalid SHA256 hash format. Expected 64-character hex string, got: ${hash}`);\n }\n return hash as Sha256Hash;\n}\n\n/**\n * Backup manifest entry for a single file\n * All properties are readonly to enforce immutability of backup records\n */\nexport interface BackupManifestFile {\n readonly path: string; // Relative path to the file in the backup directory\n readonly checksum: Sha256Hash; // SHA256 hash of the file contents (branded type)\n readonly size: number; // File size in bytes\n}\n\n/**\n * Backup manifest tracking files and creation timestamp\n * Used for backup integrity validation and recovery\n * All properties are readonly to enforce immutability of backup records\n */\nexport interface BackupManifest {\n readonly files: ReadonlyArray<BackupManifestFile>; // Array of backed up files with metadata\n readonly timestamp: string; // ISO 8601 timestamp when backup was created\n}\n\n/**\n * Create and validate a BackupManifest from raw data\n * Validates structure and transforms string checksums to Sha256Hash branded type\n *\n * @param data - Unknown data to validate (typically from JSON parsing)\n * @returns Validated and typed BackupManifest object\n * @throws Error if data doesn't match expected structure or contains invalid checksums\n */\nexport function createBackupManifest(data: unknown): BackupManifest {\n if (!data || typeof data !== \"object\") {\n throw new Error(\"Backup manifest must be an object\");\n }\n\n const obj = data as Record<string, unknown>;\n\n if (!obj.files || !Array.isArray(obj.files)) {\n throw new Error(\"Backup manifest must have a files array\");\n }\n\n if (obj.timestamp && typeof obj.timestamp !== \"string\") {\n throw new Error(\"Backup manifest timestamp must be a string if provided\");\n }\n\n // Validate ISO 8601 timestamp if provided\n let timestamp: string;\n if (obj.timestamp) {\n const timestampDate = new Date(obj.timestamp as string);\n if (isNaN(timestampDate.getTime())) {\n throw new Error(`Invalid timestamp format: ${obj.timestamp}. Expected ISO 8601 string.`);\n }\n timestamp = obj.timestamp as string;\n } else {\n timestamp = new Date().toISOString();\n }\n\n // Validate and transform each file entry\n const files: readonly BackupManifestFile[] = obj.files.map((file: unknown, index: number) => {\n if (!file || typeof file !== \"object\") {\n throw new Error(`Backup manifest file entry ${index} must be an object`);\n }\n\n const fileObj = file as Record<string, unknown>;\n\n if (typeof fileObj.path !== \"string\" || !fileObj.path) {\n throw new Error(`Backup manifest file entry ${index} must have a non-empty path string`);\n }\n\n if (typeof fileObj.checksum !== \"string\") {\n throw new Error(`Backup manifest file entry ${index} must have a checksum string`);\n }\n\n if (typeof fileObj.size !== \"number\" || fileObj.size < 0) {\n throw new Error(`Backup manifest file entry ${index} must have a non-negative size number`);\n }\n\n return {\n path: fileObj.path,\n checksum: createSha256Hash(fileObj.checksum),\n size: fileObj.size,\n };\n });\n\n return {\n files,\n timestamp,\n };\n}\n\n/**\n * Verify backup manifest against actual file checksums\n * Checks that all files in the manifest exist and have matching checksums\n *\n * @param manifest - The backup manifest to verify\n * @param readFile - Function to read file contents by path\n * @param fileExists - Function to check if a file exists by path\n * @param backupDir - Base directory path for resolving relative file paths\n * @returns Promise resolving to verification result with detailed error information\n */\nexport async function verifyBackupIntegrity(\n manifest: BackupManifest,\n readFile: (path: string) => Promise<string>,\n fileExists: (path: string) => Promise<boolean>,\n backupDir: string\n): Promise<{\n isValid: boolean;\n filesChecked: number;\n errors: string[];\n}> {\n const { createHash } = await import(\"crypto\");\n const path = await import(\"path\");\n\n const errors: string[] = [];\n let filesChecked = 0;\n\n try {\n // Verify each file in the manifest\n for (const file of manifest.files) {\n filesChecked++;\n const backupFilePath = path.join(backupDir, file.path);\n\n // Check file exists\n if (!(await fileExists(backupFilePath))) {\n errors.push(`Missing: ${file.path}`);\n continue;\n }\n\n try {\n // Read file and verify checksum\n const content = await readFile(backupFilePath);\n const checksum = createHash(\"sha256\").update(content).digest(\"hex\") as Sha256Hash;\n\n if (checksum !== file.checksum) {\n errors.push(\n `Checksum mismatch: ${file.path} (expected ${file.checksum}, got ${checksum})`\n );\n continue;\n }\n\n // Verify size matches\n if (content.length !== file.size) {\n errors.push(`Size mismatch: ${file.path} (expected ${file.size}, got ${content.length})`);\n }\n } catch (err) {\n errors.push(\n `Failed to verify: ${file.path} (${err instanceof Error ? err.message : String(err)})`\n );\n }\n }\n\n return {\n isValid: errors.length === 0,\n filesChecked,\n errors,\n };\n } catch (err) {\n return {\n isValid: false,\n filesChecked,\n errors: [\n ...errors,\n `Integrity verification failed: ${err instanceof Error ? err.message : String(err)}`,\n ],\n };\n }\n}\n"],
5
- "mappings": "AAyMM,SAAU,iBAAiB,MAAY;AAC3C,MAAI,CAAC,iBAAiB,KAAK,IAAI;AAC7B,UAAM,IAAI,MAAM,sEAAsE,IAAI,EAAE;AAE9F,SAAO;AACT;AA8BM,SAAU,qBAAqB,MAAa;AAChD,MAAI,CAAC,QAAQ,OAAO,QAAS;AAC3B,UAAM,IAAI,MAAM,mCAAmC;AAGrD,QAAM,MAAM;AAEZ,MAAI,CAAC,IAAI,SAAS,CAAC,MAAM,QAAQ,IAAI,KAAK;AACxC,UAAM,IAAI,MAAM,yCAAyC;AAG3D,MAAI,IAAI,aAAa,OAAO,IAAI,aAAc;AAC5C,UAAM,IAAI,MAAM,wDAAwD;AAI1E,MAAI;AACJ,MAAI,IAAI,WAAW;AACjB,UAAM,gBAAgB,IAAI,KAAK,IAAI,SAAmB;AACtD,QAAI,MAAM,cAAc,QAAO,CAAE;AAC/B,YAAM,IAAI,MAAM,6BAA6B,IAAI,SAAS,6BAA6B;AAEzF,gBAAY,IAAI;EAClB;AACE,iBAAY,oBAAI,KAAI,GAAG,YAAW;AA8BpC,SAAO;IACL,OA3B2C,IAAI,MAAM,IAAI,CAAC,MAAe,UAAiB;AAC1F,UAAI,CAAC,QAAQ,OAAO,QAAS;AAC3B,cAAM,IAAI,MAAM,8BAA8B,KAAK,oBAAoB;AAGzE,YAAM,UAAU;AAEhB,UAAI,OAAO,QAAQ,QAAS,YAAY,CAAC,QAAQ;AAC/C,cAAM,IAAI,MAAM,8BAA8B,KAAK,oCAAoC;AAGzF,UAAI,OAAO,QAAQ,YAAa;AAC9B,cAAM,IAAI,MAAM,8BAA8B,KAAK,8BAA8B;AAGnF,UAAI,OAAO,QAAQ,QAAS,YAAY,QAAQ,OAAO;AACrD,cAAM,IAAI,MAAM,8BAA8B,KAAK,uCAAuC;AAG5F,aAAO;QACL,MAAM,QAAQ;QACd,UAAU,iBAAiB,QAAQ,QAAQ;QAC3C,MAAM,QAAQ;;IAElB,CAAC;IAIC;;AAEJ;AAYA,eAAsB,sBACpB,UACA,UACA,YACA,WAAiB;AAMjB,QAAM,EAAE,WAAU,IAAK,MAAM,OAAO,QAAQ,GACtC,OAAO,MAAM,OAAO,MAAM,GAE1B,SAAmB,CAAA;AACzB,MAAI,eAAe;AAEnB,MAAI;AAEF,eAAW,QAAQ,SAAS,OAAO;AACjC;AACA,YAAM,iBAAiB,KAAK,KAAK,WAAW,KAAK,IAAI;AAGrD,UAAI,CAAE,MAAM,WAAW,cAAc,GAAI;AACvC,eAAO,KAAK,YAAY,KAAK,IAAI,EAAE;AACnC;MACF;AAEA,UAAI;AAEF,cAAM,UAAU,MAAM,SAAS,cAAc,GACvC,WAAW,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAElE,YAAI,aAAa,KAAK,UAAU;AAC9B,iBAAO,KACL,sBAAsB,KAAK,IAAI,cAAc,KAAK,QAAQ,SAAS,QAAQ,GAAG;AAEhF;QACF;AAGA,QAAI,QAAQ,WAAW,KAAK,QAC1B,OAAO,KAAK,kBAAkB,KAAK,IAAI,cAAc,KAAK,IAAI,SAAS,QAAQ,MAAM,GAAG;MAE5F,SAAS,KAAK;AACZ,eAAO,KACL,qBAAqB,KAAK,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG;MAE1F;IACF;AAEA,WAAO;MACL,SAAS,OAAO,WAAW;MAC3B;MACA;;EAEJ,SAAS,KAAK;AACZ,WAAO;MACL,SAAS;MACT;MACA,QAAQ;QACN,GAAG;QACH,kCAAkC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;;;EAGxF;AACF;",
4
+ "sourcesContent": ["/**\n * Core type definitions for Documentation Robotics CLI\n */\n\n// Import source reference types for use in Element interface\nimport type { SourceReference } from \"./source-reference.js\";\n\n// Export source reference types\nexport type {\n ProvenanceType,\n SourceLocation,\n RepositoryContext,\n SourceReference,\n} from \"./source-reference.js\";\n\n// Export farm-related types\nexport type {\n FarmProject,\n FarmManifestData,\n} from \"../core/farm-manifest.js\";\n\n/**\n * Reference across layers\n */\nexport interface Reference {\n source: string; // Element ID\n target: string; // Element ID\n type: string; // Reference type\n description?: string;\n}\n\n/**\n * Intra-layer or cross-layer relationship\n */\nexport interface Relationship {\n source: string;\n target: string;\n predicate: string; // e.g., \"depends-on\", \"implements\"\n layer?: string; // Source element's layer ID\n targetLayer?: string; // Destination element's layer ID; present only for cross-layer relationships\n properties?: Record<string, unknown>;\n category?: \"structural\" | \"behavioral\"; // Relationship classification\n}\n\n/**\n * Element metadata for lifecycle and version tracking\n *\n * Note: created_at and updated_at should be treated as immutable after element initialization.\n * For safe external access with immutability guarantees, use Element.getMetadata() which\n * returns a defensive copy protecting against external mutations.\n */\nexport interface ElementMetadata {\n created_at?: string; // ISO 8601 timestamp (immutable after initialization)\n updated_at?: string; // ISO 8601 timestamp (immutable after initialization)\n created_by?: string;\n version?: number;\n}\n\n/**\n * Element representation\n *\n * Aligned with spec-node.schema.json structure for direct validation\n */\nexport interface Element {\n // Spec-node aligned fields (required in new format)\n id?: string; // UUIDv4 (e.g., \"550e8400-e29b-41d4-a716-446655440000\")\n path?: string; // Human-readable path: {layer}.{type}.{kebab-name} (e.g., \"motivation.goal.increase-revenue\")\n spec_node_id: string; // Reference to spec node type (e.g., \"motivation.goal\")\n type: string; // Denormalized node type (e.g., \"goal\", \"endpoint\")\n layer_id: string; // Denormalized layer ID (e.g., \"motivation\", \"api\")\n name: string;\n\n // Spec-node aligned optional fields\n description?: string;\n attributes?: Record<string, unknown>; // Type-specific properties\n source_reference?: SourceReference; // Provenance tracking\n metadata?: ElementMetadata; // Lifecycle tracking\n\n // Relationship tracking\n references?: Reference[];\n relationships?: Relationship[];\n\n // Internal tracking (unchanged)\n layer?: string;\n filePath?: string;\n rawData?: any;\n}\n\n/**\n * Layer container\n */\nexport interface LayerData {\n elements: Element[];\n metadata?: {\n layer: string;\n version: string;\n };\n}\n\n/**\n * Changeset application history entry\n * Records when changesets were committed or discarded in the model\n */\nexport interface ChangesetHistoryEntry {\n /** Name of the changeset that was committed or discarded */\n name: string;\n /** ISO 8601 timestamp when the action occurred */\n committed_at: string;\n /** Type of action performed */\n action: \"committed\" | \"discarded\";\n}\n\n/**\n * External model declaration in manifest\n * Declares a model that this model may reference via qualified paths (e.g., @model-name/...)\n */\nexport interface ExternalModelDeclaration {\n /** Optional git repository URL for documentation */\n url?: string;\n /** Optional informational role (e.g., \"shared\") - not interpreted by CLI */\n role?: string;\n}\n\n/**\n * Manifest metadata\n */\nexport interface ManifestData {\n name: string;\n version: string;\n description?: string;\n author?: string;\n created?: string;\n modified?: string;\n cliVersion?: string;\n specVersion?: string;\n /** Changeset history for the model */\n changeset_history?: ChangesetHistoryEntry[];\n /** External model declarations: model name to declaration metadata */\n models?: Record<string, ExternalModelDeclaration>;\n /** Relative path to the codebase root (for detached models). Resolved relative to model root. */\n codebase_path?: string;\n}\n\n/**\n * Model options for customization\n */\nexport interface ModelOptions {\n enableCache?: boolean;\n lazyLoad?: boolean;\n referenceRegistry?: unknown; // Will be properly typed when implemented\n layers?: string[]; // If set, only these layers are loaded (selective loading)\n codebaseRoot?: string; // If set, points to the codebase directory (for detached models)\n}\n\n/**\n * JSON Schema property definition\n * Represents a single property in a JSON Schema object\n */\nexport interface JsonSchemaProperty {\n type?: string | string[];\n description?: string;\n enum?: unknown[];\n default?: unknown;\n items?: JsonSchemaProperty;\n properties?: Record<string, JsonSchemaProperty>;\n required?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Tool parameter definition for Claude AI tools\n * Describes a single input parameter for a tool\n */\nexport interface ToolParameter {\n type: string;\n description: string;\n enum?: string[];\n items?: { type: string };\n [key: string]: unknown;\n}\n\n/**\n * Tool input schema definition\n * Defines the structure of inputs for a Claude AI tool\n */\nexport interface ToolInputSchema {\n type: \"object\";\n properties: Record<string, ToolParameter>;\n required: string[];\n}\n\n/**\n * Tool definition for Claude AI integration\n * Represents a single tool that Claude can call\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n input_schema: ToolInputSchema;\n}\n\n/**\n * Result of executing a tool\n * All tool results are returned as JSON-serializable objects\n */\nexport interface ToolExecutionResult {\n [key: string]: unknown;\n error?: string;\n}\n\n/**\n * SHA256 hash branded type for compile-time safety\n * Prevents accidental misuse of arbitrary strings as cryptographic checksums\n */\nexport type Sha256Hash = string & { readonly __brand: \"Sha256Hash\" };\n\n/**\n * Create a Sha256Hash from a hex string\n * This is the only valid way to construct a Sha256Hash value\n *\n * @param hash - The hex string representation of a SHA256 hash\n * @returns A branded Sha256Hash type\n * @throws Error if the hash is not a valid 64-character hex string\n */\nexport function createSha256Hash(hash: string): Sha256Hash {\n if (!/^[a-f0-9]{64}$/.test(hash)) {\n throw new Error(`Invalid SHA256 hash format. Expected 64-character hex string, got: ${hash}`);\n }\n return hash as Sha256Hash;\n}\n\n/**\n * Backup manifest entry for a single file\n * All properties are readonly to enforce immutability of backup records\n */\nexport interface BackupManifestFile {\n readonly path: string; // Relative path to the file in the backup directory\n readonly checksum: Sha256Hash; // SHA256 hash of the file contents (branded type)\n readonly size: number; // File size in bytes\n}\n\n/**\n * Backup manifest tracking files and creation timestamp\n * Used for backup integrity validation and recovery\n * All properties are readonly to enforce immutability of backup records\n */\nexport interface BackupManifest {\n readonly files: ReadonlyArray<BackupManifestFile>; // Array of backed up files with metadata\n readonly timestamp: string; // ISO 8601 timestamp when backup was created\n}\n\n/**\n * Create and validate a BackupManifest from raw data\n * Validates structure and transforms string checksums to Sha256Hash branded type\n *\n * @param data - Unknown data to validate (typically from JSON parsing)\n * @returns Validated and typed BackupManifest object\n * @throws Error if data doesn't match expected structure or contains invalid checksums\n */\nexport function createBackupManifest(data: unknown): BackupManifest {\n if (!data || typeof data !== \"object\") {\n throw new Error(\"Backup manifest must be an object\");\n }\n\n const obj = data as Record<string, unknown>;\n\n if (!obj.files || !Array.isArray(obj.files)) {\n throw new Error(\"Backup manifest must have a files array\");\n }\n\n if (obj.timestamp && typeof obj.timestamp !== \"string\") {\n throw new Error(\"Backup manifest timestamp must be a string if provided\");\n }\n\n // Validate ISO 8601 timestamp if provided\n let timestamp: string;\n if (obj.timestamp) {\n const timestampDate = new Date(obj.timestamp as string);\n if (isNaN(timestampDate.getTime())) {\n throw new Error(`Invalid timestamp format: ${obj.timestamp}. Expected ISO 8601 string.`);\n }\n timestamp = obj.timestamp as string;\n } else {\n timestamp = new Date().toISOString();\n }\n\n // Validate and transform each file entry\n const files: readonly BackupManifestFile[] = obj.files.map((file: unknown, index: number) => {\n if (!file || typeof file !== \"object\") {\n throw new Error(`Backup manifest file entry ${index} must be an object`);\n }\n\n const fileObj = file as Record<string, unknown>;\n\n if (typeof fileObj.path !== \"string\" || !fileObj.path) {\n throw new Error(`Backup manifest file entry ${index} must have a non-empty path string`);\n }\n\n if (typeof fileObj.checksum !== \"string\") {\n throw new Error(`Backup manifest file entry ${index} must have a checksum string`);\n }\n\n if (typeof fileObj.size !== \"number\" || fileObj.size < 0) {\n throw new Error(`Backup manifest file entry ${index} must have a non-negative size number`);\n }\n\n return {\n path: fileObj.path,\n checksum: createSha256Hash(fileObj.checksum),\n size: fileObj.size,\n };\n });\n\n return {\n files,\n timestamp,\n };\n}\n\n/**\n * Verify backup manifest against actual file checksums\n * Checks that all files in the manifest exist and have matching checksums\n *\n * @param manifest - The backup manifest to verify\n * @param readFile - Function to read file contents by path\n * @param fileExists - Function to check if a file exists by path\n * @param backupDir - Base directory path for resolving relative file paths\n * @returns Promise resolving to verification result with detailed error information\n */\nexport async function verifyBackupIntegrity(\n manifest: BackupManifest,\n readFile: (path: string) => Promise<string>,\n fileExists: (path: string) => Promise<boolean>,\n backupDir: string\n): Promise<{\n isValid: boolean;\n filesChecked: number;\n errors: string[];\n}> {\n const { createHash } = await import(\"crypto\");\n const path = await import(\"path\");\n\n const errors: string[] = [];\n let filesChecked = 0;\n\n try {\n // Verify each file in the manifest\n for (const file of manifest.files) {\n filesChecked++;\n const backupFilePath = path.join(backupDir, file.path);\n\n // Check file exists\n if (!(await fileExists(backupFilePath))) {\n errors.push(`Missing: ${file.path}`);\n continue;\n }\n\n try {\n // Read file and verify checksum\n const content = await readFile(backupFilePath);\n const checksum = createHash(\"sha256\").update(content).digest(\"hex\") as Sha256Hash;\n\n if (checksum !== file.checksum) {\n errors.push(\n `Checksum mismatch: ${file.path} (expected ${file.checksum}, got ${checksum})`\n );\n continue;\n }\n\n // Verify size matches\n if (content.length !== file.size) {\n errors.push(`Size mismatch: ${file.path} (expected ${file.size}, got ${content.length})`);\n }\n } catch (err) {\n errors.push(\n `Failed to verify: ${file.path} (${err instanceof Error ? err.message : String(err)})`\n );\n }\n }\n\n return {\n isValid: errors.length === 0,\n filesChecked,\n errors,\n };\n } catch (err) {\n return {\n isValid: false,\n filesChecked,\n errors: [\n ...errors,\n `Integrity verification failed: ${err instanceof Error ? err.message : String(err)}`,\n ],\n };\n }\n}\n"],
5
+ "mappings": "AAgOM,SAAU,iBAAiB,MAAY;AAC3C,MAAI,CAAC,iBAAiB,KAAK,IAAI;AAC7B,UAAM,IAAI,MAAM,sEAAsE,IAAI,EAAE;AAE9F,SAAO;AACT;AA8BM,SAAU,qBAAqB,MAAa;AAChD,MAAI,CAAC,QAAQ,OAAO,QAAS;AAC3B,UAAM,IAAI,MAAM,mCAAmC;AAGrD,QAAM,MAAM;AAEZ,MAAI,CAAC,IAAI,SAAS,CAAC,MAAM,QAAQ,IAAI,KAAK;AACxC,UAAM,IAAI,MAAM,yCAAyC;AAG3D,MAAI,IAAI,aAAa,OAAO,IAAI,aAAc;AAC5C,UAAM,IAAI,MAAM,wDAAwD;AAI1E,MAAI;AACJ,MAAI,IAAI,WAAW;AACjB,UAAM,gBAAgB,IAAI,KAAK,IAAI,SAAmB;AACtD,QAAI,MAAM,cAAc,QAAO,CAAE;AAC/B,YAAM,IAAI,MAAM,6BAA6B,IAAI,SAAS,6BAA6B;AAEzF,gBAAY,IAAI;EAClB;AACE,iBAAY,oBAAI,KAAI,GAAG,YAAW;AA8BpC,SAAO;IACL,OA3B2C,IAAI,MAAM,IAAI,CAAC,MAAe,UAAiB;AAC1F,UAAI,CAAC,QAAQ,OAAO,QAAS;AAC3B,cAAM,IAAI,MAAM,8BAA8B,KAAK,oBAAoB;AAGzE,YAAM,UAAU;AAEhB,UAAI,OAAO,QAAQ,QAAS,YAAY,CAAC,QAAQ;AAC/C,cAAM,IAAI,MAAM,8BAA8B,KAAK,oCAAoC;AAGzF,UAAI,OAAO,QAAQ,YAAa;AAC9B,cAAM,IAAI,MAAM,8BAA8B,KAAK,8BAA8B;AAGnF,UAAI,OAAO,QAAQ,QAAS,YAAY,QAAQ,OAAO;AACrD,cAAM,IAAI,MAAM,8BAA8B,KAAK,uCAAuC;AAG5F,aAAO;QACL,MAAM,QAAQ;QACd,UAAU,iBAAiB,QAAQ,QAAQ;QAC3C,MAAM,QAAQ;;IAElB,CAAC;IAIC;;AAEJ;AAYA,eAAsB,sBACpB,UACA,UACA,YACA,WAAiB;AAMjB,QAAM,EAAE,WAAU,IAAK,MAAM,OAAO,QAAQ,GACtC,OAAO,MAAM,OAAO,MAAM,GAE1B,SAAmB,CAAA;AACzB,MAAI,eAAe;AAEnB,MAAI;AAEF,eAAW,QAAQ,SAAS,OAAO;AACjC;AACA,YAAM,iBAAiB,KAAK,KAAK,WAAW,KAAK,IAAI;AAGrD,UAAI,CAAE,MAAM,WAAW,cAAc,GAAI;AACvC,eAAO,KAAK,YAAY,KAAK,IAAI,EAAE;AACnC;MACF;AAEA,UAAI;AAEF,cAAM,UAAU,MAAM,SAAS,cAAc,GACvC,WAAW,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAElE,YAAI,aAAa,KAAK,UAAU;AAC9B,iBAAO,KACL,sBAAsB,KAAK,IAAI,cAAc,KAAK,QAAQ,SAAS,QAAQ,GAAG;AAEhF;QACF;AAGA,QAAI,QAAQ,WAAW,KAAK,QAC1B,OAAO,KAAK,kBAAkB,KAAK,IAAI,cAAc,KAAK,IAAI,SAAS,QAAQ,MAAM,GAAG;MAE5F,SAAS,KAAK;AACZ,eAAO,KACL,qBAAqB,KAAK,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG;MAE1F;IACF;AAEA,WAAO;MACL,SAAS,OAAO,WAAW;MAC3B;MACA;;EAEJ,SAAS,KAAK;AACZ,WAAO;MACL,SAAS;MACT;MACA,QAAQ;QACN,GAAG;QACH,kCAAkC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;;;EAGxF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,25 @@
1
+ function stripAnsi(text) {
2
+ return text.replace(/\x1b\[[0-9;]*m/g, "");
3
+ }
4
+ function installAnsiSuppressor() {
5
+ if (process.stdout.isTTY === !0)
6
+ return;
7
+ const originalLog = console.log, originalError = console.error, originalWarn = console.warn, originalInfo = console.info;
8
+ console.log = function(...args) {
9
+ const stripped = args.map((arg) => typeof arg == "string" ? stripAnsi(arg) : arg);
10
+ return originalLog.apply(console, stripped);
11
+ }, console.error = function(...args) {
12
+ const stripped = args.map((arg) => typeof arg == "string" ? stripAnsi(arg) : arg);
13
+ return originalError.apply(console, stripped);
14
+ }, console.warn = function(...args) {
15
+ const stripped = args.map((arg) => typeof arg == "string" ? stripAnsi(arg) : arg);
16
+ return originalWarn.apply(console, stripped);
17
+ }, console.info = function(...args) {
18
+ const stripped = args.map((arg) => typeof arg == "string" ? stripAnsi(arg) : arg);
19
+ return originalInfo.apply(console, stripped);
20
+ };
21
+ }
22
+ export {
23
+ installAnsiSuppressor
24
+ };
25
+ //# sourceMappingURL=ansi-suppressor.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/ansi-suppressor.ts"],
4
+ "sourcesContent": ["/**\n * Global ANSI code suppression for non-TTY output\n * Intercepts console output and strips ANSI escape sequences when stdout is not a TTY\n */\n\nfunction stripAnsi(text: string): string {\n // eslint-disable-next-line no-control-regex\n return text.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\nexport function installAnsiSuppressor(): void {\n // Only suppress if stdout is not a TTY\n if (process.stdout.isTTY === true) {\n return;\n }\n\n const originalLog = console.log;\n const originalError = console.error;\n const originalWarn = console.warn;\n const originalInfo = console.info;\n\n console.log = function (...args: any[]) {\n const stripped = args.map((arg) => {\n if (typeof arg === \"string\") {\n return stripAnsi(arg);\n }\n return arg;\n });\n return originalLog.apply(console, stripped);\n };\n\n console.error = function (...args: any[]) {\n const stripped = args.map((arg) => {\n if (typeof arg === \"string\") {\n return stripAnsi(arg);\n }\n return arg;\n });\n return originalError.apply(console, stripped);\n };\n\n console.warn = function (...args: any[]) {\n const stripped = args.map((arg) => {\n if (typeof arg === \"string\") {\n return stripAnsi(arg);\n }\n return arg;\n });\n return originalWarn.apply(console, stripped);\n };\n\n console.info = function (...args: any[]) {\n const stripped = args.map((arg) => {\n if (typeof arg === \"string\") {\n return stripAnsi(arg);\n }\n return arg;\n });\n return originalInfo.apply(console, stripped);\n };\n}\n"],
5
+ "mappings": "AAKA,SAAS,UAAU,MAAY;AAE7B,SAAO,KAAK,QAAQ,mBAAmB,EAAE;AAC3C;AAEM,SAAU,wBAAqB;AAEnC,MAAI,QAAQ,OAAO,UAAU;AAC3B;AAGF,QAAM,cAAc,QAAQ,KACtB,gBAAgB,QAAQ,OACxB,eAAe,QAAQ,MACvB,eAAe,QAAQ;AAE7B,UAAQ,MAAM,YAAa,MAAW;AACpC,UAAM,WAAW,KAAK,IAAI,CAAC,QACrB,OAAO,OAAQ,WACV,UAAU,GAAG,IAEf,GACR;AACD,WAAO,YAAY,MAAM,SAAS,QAAQ;EAC5C,GAEA,QAAQ,QAAQ,YAAa,MAAW;AACtC,UAAM,WAAW,KAAK,IAAI,CAAC,QACrB,OAAO,OAAQ,WACV,UAAU,GAAG,IAEf,GACR;AACD,WAAO,cAAc,MAAM,SAAS,QAAQ;EAC9C,GAEA,QAAQ,OAAO,YAAa,MAAW;AACrC,UAAM,WAAW,KAAK,IAAI,CAAC,QACrB,OAAO,OAAQ,WACV,UAAU,GAAG,IAEf,GACR;AACD,WAAO,aAAa,MAAM,SAAS,QAAQ;EAC7C,GAEA,QAAQ,OAAO,YAAa,MAAW;AACrC,UAAM,WAAW,KAAK,IAAI,CAAC,QACrB,OAAO,OAAQ,WACV,UAAU,GAAG,IAEf,GACR;AACD,WAAO,aAAa,MAAM,SAAS,QAAQ;EAC7C;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,34 @@
1
+ import ansis from "ansis";
2
+ function shouldUseColors() {
3
+ return process.stdout.isTTY === !0;
4
+ }
5
+ const colors = {
6
+ red: (str) => shouldUseColors() ? ansis.red(str) : str,
7
+ green: (str) => shouldUseColors() ? ansis.green(str) : str,
8
+ yellow: (str) => shouldUseColors() ? ansis.yellow(str) : str,
9
+ blue: (str) => shouldUseColors() ? ansis.blue(str) : str,
10
+ gray: (str) => shouldUseColors() ? ansis.gray(str) : str,
11
+ dim: (str) => shouldUseColors() ? ansis.dim(str) : str,
12
+ bold: (str) => shouldUseColors() ? ansis.bold(str) : str,
13
+ cyan: (str) => shouldUseColors() ? ansis.cyan(str) : str,
14
+ get red_bold() {
15
+ return (str) => shouldUseColors() ? ansis.red.bold(str) : str;
16
+ },
17
+ get bold_yellow() {
18
+ return (str) => shouldUseColors() ? ansis.bold.yellow(str) : str;
19
+ },
20
+ get bold_blue() {
21
+ return (str) => shouldUseColors() ? ansis.bold.blue(str) : str;
22
+ },
23
+ get bold_green() {
24
+ return (str) => shouldUseColors() ? ansis.bold.green(str) : str;
25
+ }
26
+ };
27
+ function isColorSupported() {
28
+ return shouldUseColors();
29
+ }
30
+ export {
31
+ colors,
32
+ isColorSupported
33
+ };
34
+ //# sourceMappingURL=color-utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/color-utils.ts"],
4
+ "sourcesContent": ["import ansis from \"ansis\";\n\nfunction shouldUseColors(): boolean {\n return process.stdout.isTTY === true;\n}\n\nexport const colors = {\n red: (str: string): string => (shouldUseColors() ? ansis.red(str) : str),\n green: (str: string): string => (shouldUseColors() ? ansis.green(str) : str),\n yellow: (str: string): string => (shouldUseColors() ? ansis.yellow(str) : str),\n blue: (str: string): string => (shouldUseColors() ? ansis.blue(str) : str),\n gray: (str: string): string => (shouldUseColors() ? ansis.gray(str) : str),\n dim: (str: string): string => (shouldUseColors() ? ansis.dim(str) : str),\n bold: (str: string): string => (shouldUseColors() ? ansis.bold(str) : str),\n cyan: (str: string): string => (shouldUseColors() ? ansis.cyan(str) : str),\n\n get red_bold() {\n return (str: string): string => (shouldUseColors() ? ansis.red.bold(str) : str);\n },\n get bold_yellow() {\n return (str: string): string => (shouldUseColors() ? ansis.bold.yellow(str) : str);\n },\n get bold_blue() {\n return (str: string): string => (shouldUseColors() ? ansis.bold.blue(str) : str);\n },\n get bold_green() {\n return (str: string): string => (shouldUseColors() ? ansis.bold.green(str) : str);\n },\n};\n\nexport function isColorSupported(): boolean {\n return shouldUseColors();\n}\n"],
5
+ "mappings": "AAAA,OAAO,WAAW;AAElB,SAAS,kBAAe;AACtB,SAAO,QAAQ,OAAO,UAAU;AAClC;AAEO,MAAM,SAAS;EACpB,KAAK,CAAC,QAAyB,gBAAe,IAAK,MAAM,IAAI,GAAG,IAAI;EACpE,OAAO,CAAC,QAAyB,gBAAe,IAAK,MAAM,MAAM,GAAG,IAAI;EACxE,QAAQ,CAAC,QAAyB,gBAAe,IAAK,MAAM,OAAO,GAAG,IAAI;EAC1E,MAAM,CAAC,QAAyB,gBAAe,IAAK,MAAM,KAAK,GAAG,IAAI;EACtE,MAAM,CAAC,QAAyB,gBAAe,IAAK,MAAM,KAAK,GAAG,IAAI;EACtE,KAAK,CAAC,QAAyB,gBAAe,IAAK,MAAM,IAAI,GAAG,IAAI;EACpE,MAAM,CAAC,QAAyB,gBAAe,IAAK,MAAM,KAAK,GAAG,IAAI;EACtE,MAAM,CAAC,QAAyB,gBAAe,IAAK,MAAM,KAAK,GAAG,IAAI;EAEtE,IAAI,WAAQ;AACV,WAAO,CAAC,QAAyB,gBAAe,IAAK,MAAM,IAAI,KAAK,GAAG,IAAI;EAC7E;EACA,IAAI,cAAW;AACb,WAAO,CAAC,QAAyB,gBAAe,IAAK,MAAM,KAAK,OAAO,GAAG,IAAI;EAChF;EACA,IAAI,YAAS;AACX,WAAO,CAAC,QAAyB,gBAAe,IAAK,MAAM,KAAK,KAAK,GAAG,IAAI;EAC9E;EACA,IAAI,aAAU;AACZ,WAAO,CAAC,QAAyB,gBAAe,IAAK,MAAM,KAAK,MAAM,GAAG,IAAI;EAC/E;;AAGI,SAAU,mBAAgB;AAC9B,SAAO,gBAAe;AACxB;",
6
+ "names": []
7
+ }
@@ -1,8 +1,22 @@
1
1
  import { getAllLayerIds } from "../generated/layer-registry.js";
2
- const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, KNOWN_LAYERS = new Set(getAllLayerIds());
2
+ import { UUID_PATTERN, QUALIFIED_PREFIX_PATTERN, parseReferencePath, ReferencePathParseError } from "./reference-path-parser.js";
3
+ const KNOWN_LAYERS = new Set(getAllLayerIds());
3
4
  function looksLikeElementReference(value) {
4
5
  if (UUID_PATTERN.test(value))
5
6
  return !0;
7
+ if (QUALIFIED_PREFIX_PATTERN.test(value))
8
+ try {
9
+ const parsed = parseReferencePath(value), dotIndex2 = parsed.segment.indexOf(".");
10
+ if (dotIndex2 > 0) {
11
+ const layer = parsed.segment.slice(0, dotIndex2);
12
+ return KNOWN_LAYERS.has(layer);
13
+ }
14
+ return !1;
15
+ } catch (error) {
16
+ if (error instanceof ReferencePathParseError)
17
+ return !1;
18
+ throw error;
19
+ }
6
20
  const dotIndex = value.indexOf(".");
7
21
  return dotIndex <= 0 ? !1 : KNOWN_LAYERS.has(value.slice(0, dotIndex));
8
22
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/element-reference-shape.ts"],
4
- "sourcesContent": ["/**\n * Shape check for \"does this string plausibly identify a DR model element\" \u2014 a UUIDv4\n * (element.id) or a dot-separated `{layer}.{type}.{slug}` id whose first segment is a real,\n * known DR layer (matching the id scheme `NamingValidator` enforces on every element).\n *\n * Used by `ReferenceRegistry` to gate its `*Ref`/`*Reference` attribute-name heuristic: an\n * attribute NAME matching that convention is not, by itself, reliable evidence that its VALUE\n * is an element reference \u2014 `api.link.operationRef` is a schema-declared OpenAPI URL fragment\n * (e.g. \"#/paths/~1users/get\"), not an element id, and matches the naming convention purely by\n * coincidence. Checking the value's shape catches that case (and any future one like it)\n * without needing per-attribute schema annotations that don't exist today.\n *\n * Deliberately a shape check, not an existence check: a value that looks like an element id but\n * doesn't resolve to one is exactly what `ReferenceRegistry.findBrokenReferences()` is supposed\n * to catch, so this must not filter those out too.\n */\nimport { getAllLayerIds } from \"../generated/layer-registry.js\";\n\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst KNOWN_LAYERS = new Set(getAllLayerIds());\n\nexport function looksLikeElementReference(value: string): boolean {\n if (UUID_PATTERN.test(value)) return true;\n\n const dotIndex = value.indexOf(\".\");\n if (dotIndex <= 0) return false;\n\n return KNOWN_LAYERS.has(value.slice(0, dotIndex));\n}\n"],
5
- "mappings": "AAgBA,SAAS,sBAAsB;AAE/B,MAAM,eAAe,mEACf,eAAe,IAAI,IAAI,eAAc,CAAE;AAEvC,SAAU,0BAA0B,OAAa;AACrD,MAAI,aAAa,KAAK,KAAK;AAAG,WAAO;AAErC,QAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,SAAI,YAAY,IAAU,KAEnB,aAAa,IAAI,MAAM,MAAM,GAAG,QAAQ,CAAC;AAClD;",
6
- "names": []
4
+ "sourcesContent": ["/**\n * Shape check for \"does this string plausibly identify a DR model element\" \u2014 a UUIDv4\n * (element.id), a dot-separated `{layer}.{type}.{slug}` id whose first segment is a real,\n * known DR layer (matching the id scheme `NamingValidator` enforces on every element), or\n * a qualified cross-model reference in the format `@{model-name}/{layer}.{type}.{slug}`.\n *\n * Used by `ReferenceRegistry` to gate its `*Ref`/`*Reference` attribute-name heuristic: an\n * attribute NAME matching that convention is not, by itself, reliable evidence that its VALUE\n * is an element reference \u2014 `api.link.operationRef` is a schema-declared OpenAPI URL fragment\n * (e.g. \"#/paths/~1users/get\"), not an element id, and matches the naming convention purely by\n * coincidence. Checking the value's shape catches that case (and any future one like it)\n * without needing per-attribute schema annotations that don't exist today.\n *\n * Deliberately a shape check, not an existence check: a value that looks like an element id but\n * doesn't resolve to one is exactly what `ReferenceRegistry.findBrokenReferences()` is supposed\n * to catch, so this must not filter those out too.\n */\nimport { getAllLayerIds } from \"../generated/layer-registry.js\";\nimport {\n UUID_PATTERN,\n QUALIFIED_PREFIX_PATTERN,\n parseReferencePath,\n ReferencePathParseError,\n} from \"./reference-path-parser.js\";\n\nconst KNOWN_LAYERS = new Set(getAllLayerIds());\n\nexport function looksLikeElementReference(value: string): boolean {\n if (UUID_PATTERN.test(value)) return true;\n\n // Check for qualified reference: must match without whitespace\n if (QUALIFIED_PREFIX_PATTERN.test(value)) {\n try {\n const parsed = parseReferencePath(value);\n // Extract layer name from segment (first dot-separated component)\n const dotIndex = parsed.segment.indexOf(\".\");\n if (dotIndex > 0) {\n const layer = parsed.segment.slice(0, dotIndex);\n return KNOWN_LAYERS.has(layer);\n }\n return false;\n } catch (error) {\n // Malformed qualified reference\n if (error instanceof ReferencePathParseError) {\n return false;\n }\n throw error;\n }\n }\n\n // Check for unqualified reference: {layer}.{type}.{slug}\n const dotIndex = value.indexOf(\".\");\n if (dotIndex <= 0) return false;\n\n return KNOWN_LAYERS.has(value.slice(0, dotIndex));\n}\n"],
5
+ "mappings": "AAiBA,SAAS,sBAAsB;AAC/B,SACE,cACA,0BACA,oBACA,+BACK;AAEP,MAAM,eAAe,IAAI,IAAI,eAAc,CAAE;AAEvC,SAAU,0BAA0B,OAAa;AACrD,MAAI,aAAa,KAAK,KAAK;AAAG,WAAO;AAGrC,MAAI,yBAAyB,KAAK,KAAK;AACrC,QAAI;AACF,YAAM,SAAS,mBAAmB,KAAK,GAEjCA,YAAW,OAAO,QAAQ,QAAQ,GAAG;AAC3C,UAAIA,YAAW,GAAG;AAChB,cAAM,QAAQ,OAAO,QAAQ,MAAM,GAAGA,SAAQ;AAC9C,eAAO,aAAa,IAAI,KAAK;MAC/B;AACA,aAAO;IACT,SAAS,OAAO;AAEd,UAAI,iBAAiB;AACnB,eAAO;AAET,YAAM;IACR;AAIF,QAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,SAAI,YAAY,IAAU,KAEnB,aAAa,IAAI,MAAM,MAAM,GAAG,QAAQ,CAAC;AAClD;",
6
+ "names": ["dotIndex"]
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import ansis from "ansis";
2
+ import { isJson } from "./globals.js";
2
3
  const MAX_SUGGESTIONS = 5;
3
4
  var ErrorCategory;
4
5
  (function(ErrorCategory2) {
@@ -105,20 +106,77 @@ class InvalidJSONError extends CLIError {
105
106
  }
106
107
  }
107
108
  function handleError(error) {
108
- throw error instanceof CLIError ? (console.error(error.format()), error) : error instanceof Error ? (console.error(ansis.red(`Error: ${error.message}`)), process.env.DEBUG && console.error(ansis.dim(error.stack)), error) : (console.error(ansis.red("An unexpected error occurred")), process.env.DEBUG && console.error(ansis.dim(String(error))), new Error(String(error)));
109
+ if (error instanceof CLIError) {
110
+ if (isJson()) {
111
+ const output = {
112
+ status: "error",
113
+ code: error.exitCode,
114
+ message: error.message
115
+ };
116
+ error.suggestions && error.suggestions.length > 0 && (output.suggestions = error.suggestions), error.context && (error.context.operation && (output.operation = error.context.operation), error.context.relatedElements && error.context.relatedElements.length > 0 && (output.relatedElements = error.context.relatedElements), error.context.partialProgress && (output.partialProgress = error.context.partialProgress)), console.log(JSON.stringify(output));
117
+ } else
118
+ console.error(error.format());
119
+ throw error;
120
+ } else if (error instanceof Error) {
121
+ if (isJson()) {
122
+ const output = {
123
+ status: "error",
124
+ code: ErrorCategory.USER,
125
+ message: error.message
126
+ };
127
+ process.env.DEBUG && error.stack && (output.stack = error.stack), console.log(JSON.stringify(output));
128
+ } else
129
+ console.error(ansis.red(`Error: ${error.message}`)), process.env.DEBUG && console.error(ansis.dim(error.stack));
130
+ throw error;
131
+ } else {
132
+ if (isJson()) {
133
+ const output = {
134
+ status: "error",
135
+ code: ErrorCategory.USER,
136
+ message: "An unexpected error occurred"
137
+ };
138
+ process.env.DEBUG && (output.details = String(error)), console.log(JSON.stringify(output));
139
+ } else
140
+ console.error(ansis.red("An unexpected error occurred")), process.env.DEBUG && console.error(ansis.dim(String(error)));
141
+ throw new Error(String(error));
142
+ }
109
143
  }
110
144
  function handleWarning(message, suggestions) {
111
- const lines = [];
112
- if (lines.push(ansis.yellow(`Warning: ${message}`)), suggestions && suggestions.length > 0) {
113
- lines.push(""), lines.push(ansis.dim("Suggestions:"));
114
- for (const suggestion of suggestions)
115
- lines.push(ansis.dim(` \u2022 ${suggestion}`));
116
- }
117
- console.warn(lines.join(`
145
+ if (isJson()) {
146
+ const output = {
147
+ level: "warning",
148
+ message
149
+ };
150
+ suggestions && suggestions.length > 0 && (output.suggestions = suggestions), console.error(JSON.stringify(output));
151
+ } else {
152
+ const lines = [];
153
+ if (lines.push(ansis.yellow(`Warning: ${message}`)), suggestions && suggestions.length > 0) {
154
+ lines.push(""), lines.push(ansis.dim("Suggestions:"));
155
+ for (const suggestion of suggestions)
156
+ lines.push(ansis.dim(` \u2022 ${suggestion}`));
157
+ }
158
+ console.warn(lines.join(`
118
159
  `));
160
+ }
161
+ }
162
+ function handleSuccess(message, details, options) {
163
+ if (isJson()) {
164
+ const output = {
165
+ status: "ok",
166
+ ...details
167
+ };
168
+ console.log(JSON.stringify(output));
169
+ } else if (console.log(ansis.green(`\u2713 ${message}`)), details && (options?.verbose || details.changesetStatus))
170
+ for (const [key, value] of Object.entries(details)) {
171
+ const displayKey = keyToDisplayName(key);
172
+ console.log(ansis.dim(` ${displayKey}: ${value}`));
173
+ }
174
+ }
175
+ function keyToDisplayName(key) {
176
+ return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
119
177
  }
120
- function handleSuccess(message, details) {
121
- if (console.log(ansis.green(`\u2713 ${message}`)), details)
178
+ function handleInfo(message, details) {
179
+ if (!isJson() && (console.log(message), details))
122
180
  for (const [key, value] of Object.entries(details))
123
181
  console.log(ansis.dim(` ${key}: ${value}`));
124
182
  }
@@ -172,6 +230,7 @@ export {
172
230
  formatValidOptions,
173
231
  getErrorMessage,
174
232
  handleError,
233
+ handleInfo,
175
234
  handleSuccess,
176
235
  handleWarning
177
236
  };
@@ -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": "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",
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 status: \"ok\",\n ...details,\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 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
  }
@@ -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
- setGlobalOptions
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": "AAUA,IAAI,gBAA+B,CAAA;AAK7B,SAAU,iBAAiB,SAAsB;AACrD,kBAAgB,EAAE,GAAG,eAAe,GAAG,QAAO;AAChD;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,WAAW,SAAe;AACxC,EAAI,UAAS,KACX,QAAQ,IAAI,OAAO;AAEvB;AAKM,SAAU,SAAS,SAAe;AACtC,EAAI,QAAO,KACT,QAAQ,MAAM,WAAW,OAAO,EAAE;AAEtC;",
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
  }