@documentation-robotics/cli 0.1.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 (169) hide show
  1. package/PYTHON_CLI_PARITY.md +265 -0
  2. package/PYTHON_CLI_STRUCTURE_UPDATE.md +246 -0
  3. package/README.md +926 -0
  4. package/biome.json +42 -0
  5. package/bunfig.toml +3 -0
  6. package/dist/ai/claude-client.js +71 -0
  7. package/dist/ai/claude-client.js.map +7 -0
  8. package/dist/ai/context-provider.js +129 -0
  9. package/dist/ai/context-provider.js.map +7 -0
  10. package/dist/ai/tools.js +231 -0
  11. package/dist/ai/tools.js.map +7 -0
  12. package/dist/cli.js +224 -0
  13. package/dist/cli.js.map +7 -0
  14. package/dist/commands/add.js +51 -0
  15. package/dist/commands/add.js.map +7 -0
  16. package/dist/commands/changeset.js +174 -0
  17. package/dist/commands/changeset.js.map +7 -0
  18. package/dist/commands/chat.js +111 -0
  19. package/dist/commands/chat.js.map +7 -0
  20. package/dist/commands/conformance.js +132 -0
  21. package/dist/commands/conformance.js.map +7 -0
  22. package/dist/commands/delete.js +31 -0
  23. package/dist/commands/delete.js.map +7 -0
  24. package/dist/commands/element.js +28 -0
  25. package/dist/commands/element.js.map +7 -0
  26. package/dist/commands/export.js +65 -0
  27. package/dist/commands/export.js.map +7 -0
  28. package/dist/commands/info.js +40 -0
  29. package/dist/commands/info.js.map +7 -0
  30. package/dist/commands/init.js +45 -0
  31. package/dist/commands/init.js.map +7 -0
  32. package/dist/commands/list.js +32 -0
  33. package/dist/commands/list.js.map +7 -0
  34. package/dist/commands/migrate.js +36 -0
  35. package/dist/commands/migrate.js.map +7 -0
  36. package/dist/commands/project.js +74 -0
  37. package/dist/commands/project.js.map +7 -0
  38. package/dist/commands/relationship.js +118 -0
  39. package/dist/commands/relationship.js.map +7 -0
  40. package/dist/commands/search.js +48 -0
  41. package/dist/commands/search.js.map +7 -0
  42. package/dist/commands/show.js +38 -0
  43. package/dist/commands/show.js.map +7 -0
  44. package/dist/commands/trace.js +60 -0
  45. package/dist/commands/trace.js.map +7 -0
  46. package/dist/commands/update.js +37 -0
  47. package/dist/commands/update.js.map +7 -0
  48. package/dist/commands/upgrade.js +152 -0
  49. package/dist/commands/upgrade.js.map +7 -0
  50. package/dist/commands/validate.js +71 -0
  51. package/dist/commands/validate.js.map +7 -0
  52. package/dist/commands/version.js +20 -0
  53. package/dist/commands/version.js.map +7 -0
  54. package/dist/commands/visualize.js +51 -0
  55. package/dist/commands/visualize.js.map +7 -0
  56. package/dist/core/active-changeset.js +52 -0
  57. package/dist/core/active-changeset.js.map +7 -0
  58. package/dist/core/changeset.js +244 -0
  59. package/dist/core/changeset.js.map +7 -0
  60. package/dist/core/dependency-tracker.js +322 -0
  61. package/dist/core/dependency-tracker.js.map +7 -0
  62. package/dist/core/element.js +65 -0
  63. package/dist/core/element.js.map +7 -0
  64. package/dist/core/layer.js +75 -0
  65. package/dist/core/layer.js.map +7 -0
  66. package/dist/core/manifest.js +57 -0
  67. package/dist/core/manifest.js.map +7 -0
  68. package/dist/core/migration-registry.js +131 -0
  69. package/dist/core/migration-registry.js.map +7 -0
  70. package/dist/core/model.js +455 -0
  71. package/dist/core/model.js.map +7 -0
  72. package/dist/core/projection-engine.js +288 -0
  73. package/dist/core/projection-engine.js.map +7 -0
  74. package/dist/core/reference-registry.js +188 -0
  75. package/dist/core/reference-registry.js.map +7 -0
  76. package/dist/core/relationship-registry.js +114 -0
  77. package/dist/core/relationship-registry.js.map +7 -0
  78. package/dist/core/relationships.js +78 -0
  79. package/dist/core/relationships.js.map +7 -0
  80. package/dist/export/archimate-exporter.js +111 -0
  81. package/dist/export/archimate-exporter.js.map +7 -0
  82. package/dist/export/export-manager.js +63 -0
  83. package/dist/export/export-manager.js.map +7 -0
  84. package/dist/export/graphml-exporter.js +39 -0
  85. package/dist/export/graphml-exporter.js.map +7 -0
  86. package/dist/export/index.js +17 -0
  87. package/dist/export/index.js.map +7 -0
  88. package/dist/export/json-schema-exporter.js +60 -0
  89. package/dist/export/json-schema-exporter.js.map +7 -0
  90. package/dist/export/markdown-exporter.js +99 -0
  91. package/dist/export/markdown-exporter.js.map +7 -0
  92. package/dist/export/openapi-exporter.js +85 -0
  93. package/dist/export/openapi-exporter.js.map +7 -0
  94. package/dist/export/plantuml-exporter.js +60 -0
  95. package/dist/export/plantuml-exporter.js.map +7 -0
  96. package/dist/export/types.js +36 -0
  97. package/dist/export/types.js.map +7 -0
  98. package/dist/schemas/bundled/01-motivation-layer.schema.json +942 -0
  99. package/dist/schemas/bundled/02-business-layer.schema.json +1293 -0
  100. package/dist/schemas/bundled/03-security-layer.schema.json +2177 -0
  101. package/dist/schemas/bundled/04-application-layer.schema.json +1328 -0
  102. package/dist/schemas/bundled/05-technology-layer.schema.json +1465 -0
  103. package/dist/schemas/bundled/06-api-layer.schema.json +1743 -0
  104. package/dist/schemas/bundled/07-data-model-layer.schema.json +1253 -0
  105. package/dist/schemas/bundled/08-datastore-layer.schema.json +892 -0
  106. package/dist/schemas/bundled/09-ux-layer.schema.json +1506 -0
  107. package/dist/schemas/bundled/10-navigation-layer.schema.json +843 -0
  108. package/dist/schemas/bundled/11-apm-observability-layer.schema.json +1430 -0
  109. package/dist/schemas/bundled/12-testing-layer.schema.json +1091 -0
  110. package/dist/schemas/bundled/common/layer-extensions.schema.json +266 -0
  111. package/dist/schemas/bundled/common/predicates.schema.json +493 -0
  112. package/dist/schemas/bundled/common/relationships.schema.json +791 -0
  113. package/dist/schemas/bundled/common/source-references.schema.json +71 -0
  114. package/dist/schemas/bundled/link-registry.json +1839 -0
  115. package/dist/schemas/bundled/link-registry.schema.json +339 -0
  116. package/dist/schemas/bundled/relationship-catalog.json +1302 -0
  117. package/dist/schemas/bundled/relationship-type.schema.json +221 -0
  118. package/dist/server/server.js +1207 -0
  119. package/dist/server/server.js.map +7 -0
  120. package/dist/server/telemetry-middleware.js +24 -0
  121. package/dist/server/telemetry-middleware.js.map +7 -0
  122. package/dist/telemetry/console-interceptor.js +34 -0
  123. package/dist/telemetry/console-interceptor.js.map +7 -0
  124. package/dist/telemetry/index.js +54 -0
  125. package/dist/telemetry/index.js.map +7 -0
  126. package/dist/telemetry/resilient-exporter.js +41 -0
  127. package/dist/telemetry/resilient-exporter.js.map +7 -0
  128. package/dist/telemetry/resilient-log-exporter.js +41 -0
  129. package/dist/telemetry/resilient-log-exporter.js.map +7 -0
  130. package/dist/telemetry/test-instrumentation.js +32 -0
  131. package/dist/telemetry/test-instrumentation.js.map +7 -0
  132. package/dist/types/index.js +1 -0
  133. package/dist/types/index.js.map +7 -0
  134. package/dist/utils/element-utils.js +11 -0
  135. package/dist/utils/element-utils.js.map +7 -0
  136. package/dist/utils/errors.js +105 -0
  137. package/dist/utils/errors.js.map +7 -0
  138. package/dist/utils/file-io.js +69 -0
  139. package/dist/utils/file-io.js.map +7 -0
  140. package/dist/utils/globals.js +28 -0
  141. package/dist/utils/globals.js.map +7 -0
  142. package/dist/utils/model-path.js +22 -0
  143. package/dist/utils/model-path.js.map +7 -0
  144. package/dist/utils/project-paths.js +45 -0
  145. package/dist/utils/project-paths.js.map +7 -0
  146. package/dist/utils/spec-installer.js +85 -0
  147. package/dist/utils/spec-installer.js.map +7 -0
  148. package/dist/utils/spec-version.js +33 -0
  149. package/dist/utils/spec-version.js.map +7 -0
  150. package/dist/validators/index.js +15 -0
  151. package/dist/validators/index.js.map +7 -0
  152. package/dist/validators/naming-validator.js +82 -0
  153. package/dist/validators/naming-validator.js.map +7 -0
  154. package/dist/validators/reference-validator.js +106 -0
  155. package/dist/validators/reference-validator.js.map +7 -0
  156. package/dist/validators/schema-validator.js +148 -0
  157. package/dist/validators/schema-validator.js.map +7 -0
  158. package/dist/validators/semantic-validator.js +80 -0
  159. package/dist/validators/semantic-validator.js.map +7 -0
  160. package/dist/validators/types.js +48 -0
  161. package/dist/validators/types.js.map +7 -0
  162. package/dist/validators/validator.js +67 -0
  163. package/dist/validators/validator.js.map +7 -0
  164. package/docs/TEST-INSTRUMENTATION.md +405 -0
  165. package/docs/telemetry.md +427 -0
  166. package/package.json +70 -0
  167. package/test-debug.js +33 -0
  168. package/test-special.mjs +46 -0
  169. package/test_runner.ts +7 -0
@@ -0,0 +1,105 @@
1
+ import ansis from "ansis";
2
+ class CLIError extends Error {
3
+ exitCode;
4
+ suggestions;
5
+ constructor(message, exitCode = 1, suggestions) {
6
+ super(message), this.exitCode = exitCode, this.suggestions = suggestions, this.name = "CLIError";
7
+ }
8
+ format() {
9
+ const lines = [];
10
+ if (lines.push(ansis.red(`Error: ${this.message}`)), this.suggestions && this.suggestions.length > 0) {
11
+ lines.push(""), lines.push(ansis.dim("Suggestions:"));
12
+ for (const suggestion of this.suggestions)
13
+ lines.push(ansis.dim(` \u2022 ${suggestion}`));
14
+ }
15
+ return lines.join(`
16
+ `);
17
+ }
18
+ }
19
+ class ValidationError extends CLIError {
20
+ errors;
21
+ constructor(message, errors = [], suggestions) {
22
+ super(message, 1, suggestions), this.errors = errors, this.name = "ValidationError";
23
+ }
24
+ format() {
25
+ const lines = [];
26
+ if (lines.push(ansis.red(`Error: ${this.message}`)), this.errors.length > 0) {
27
+ lines.push(""), lines.push(ansis.dim("Details:"));
28
+ for (const error of this.errors) {
29
+ let detail = "";
30
+ error.layer && error.elementId ? detail = ` [${error.layer}:${error.elementId}] ${error.message}` : error.layer ? detail = ` [${error.layer}] ${error.message}` : detail = ` ${error.message}`, lines.push(ansis.dim(detail));
31
+ }
32
+ }
33
+ if (this.suggestions && this.suggestions.length > 0) {
34
+ lines.push(""), lines.push(ansis.dim("Suggestions:"));
35
+ for (const suggestion of this.suggestions)
36
+ lines.push(ansis.dim(` \u2022 ${suggestion}`));
37
+ }
38
+ return lines.join(`
39
+ `);
40
+ }
41
+ }
42
+ class FileNotFoundError extends CLIError {
43
+ constructor(path, context) {
44
+ const message = context ? `${context}: ${path}` : `File not found: ${path}`;
45
+ super(message, 2, [
46
+ `Check that the path is correct: ${path}`,
47
+ 'Use "dr init" to create a new model',
48
+ 'Use "dr list <layer>" to see available elements'
49
+ ]), this.name = "FileNotFoundError";
50
+ }
51
+ }
52
+ class ElementNotFoundError extends CLIError {
53
+ constructor(elementId) {
54
+ super(`Element not found: ${elementId}`, 2, [
55
+ `Use "dr search ${elementId}" to find similar elements`,
56
+ 'Use "dr list <layer>" to list all elements in a layer',
57
+ "Check the element ID format: {layer}-{type}-{kebab-case-name}"
58
+ ]), this.name = "ElementNotFoundError";
59
+ }
60
+ }
61
+ class ModelNotFoundError extends CLIError {
62
+ constructor(rootPath = ".") {
63
+ super(`No model found at ${rootPath}`, 2, ['Run "dr init" to initialize a new model', "Check that you are in the correct directory"]), this.name = "ModelNotFoundError";
64
+ }
65
+ }
66
+ class InvalidJSONError extends CLIError {
67
+ constructor(input, context) {
68
+ const message = context ? `Invalid JSON in ${context}` : "Invalid JSON format";
69
+ super(message, 1, [
70
+ `Check your JSON syntax: ${input}`,
71
+ `Use single quotes to wrap JSON: --properties '{"key":"value"}'`,
72
+ "Escape special characters properly"
73
+ ]), this.name = "InvalidJSONError";
74
+ }
75
+ }
76
+ function handleError(error) {
77
+ error instanceof CLIError ? (console.error(error.format()), process.exit(error.exitCode)) : error instanceof Error ? (console.error(ansis.red(`Error: ${error.message}`)), process.env.DEBUG && console.error(ansis.dim(error.stack)), process.exit(1)) : (console.error(ansis.red("An unexpected error occurred")), process.env.DEBUG && console.error(ansis.dim(String(error))), process.exit(1));
78
+ }
79
+ function handleWarning(message, suggestions) {
80
+ const lines = [];
81
+ if (lines.push(ansis.yellow(`Warning: ${message}`)), suggestions && suggestions.length > 0) {
82
+ lines.push(""), lines.push(ansis.dim("Suggestions:"));
83
+ for (const suggestion of suggestions)
84
+ lines.push(ansis.dim(` \u2022 ${suggestion}`));
85
+ }
86
+ console.warn(lines.join(`
87
+ `));
88
+ }
89
+ function handleSuccess(message, details) {
90
+ if (console.log(ansis.green(`\u2713 ${message}`)), details)
91
+ for (const [key, value] of Object.entries(details))
92
+ console.log(ansis.dim(` ${key}: ${value}`));
93
+ }
94
+ export {
95
+ CLIError,
96
+ ElementNotFoundError,
97
+ FileNotFoundError,
98
+ InvalidJSONError,
99
+ ModelNotFoundError,
100
+ ValidationError,
101
+ handleError,
102
+ handleSuccess,
103
+ handleWarning
104
+ };
105
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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 * NOTE: The add.ts command demonstrates usage of CLIError.\n * Future commands should adopt this pattern to ensure consistent error handling.\n */\n\nimport ansis from 'ansis';\n\nexport class CLIError extends Error {\n constructor(\n message: string,\n public exitCode: number = 1,\n public suggestions?: string[]\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.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 ) {\n super(message, 1, suggestions);\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 lines.push('');\n lines.push(ansis.dim('Details:'));\n for (const error of this.errors) {\n let detail = '';\n if (error.layer && error.elementId) {\n detail = ` [${error.layer}:${error.elementId}] ${error.message}`;\n } else if (error.layer) {\n detail = ` [${error.layer}] ${error.message}`;\n } else {\n detail = ` ${error.message}`;\n }\n lines.push(ansis.dim(detail));\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, 2, [\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(\n `Element not found: ${elementId}`,\n 2,\n [\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 );\n this.name = 'ElementNotFoundError';\n }\n}\n\nexport class ModelNotFoundError extends CLIError {\n constructor(rootPath: string = '.') {\n super(\n `No model found at ${rootPath}`,\n 2,\n [`Run \"dr init\" to initialize a new model`, '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, 1, [\n `Check your JSON syntax: ${input}`,\n 'Use single quotes to wrap JSON: --properties \\'{\"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 process.exit(error.exitCode);\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 process.exit(1);\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 process.exit(1);\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"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,69 @@
1
+ import { mkdir, rename, readFile as fsReadFile, writeFile as fsWriteFile, stat } from "node:fs/promises";
2
+ import { existsSync as fsExistsSync } from "node:fs";
3
+ import { startSpan, endSpan } from "../telemetry/index.js";
4
+ const isTelemetryEnabled = !1;
5
+ async function ensureDir(path) {
6
+ await mkdir(path, { recursive: !0 });
7
+ }
8
+ async function atomicWrite(path, content) {
9
+ const fileExists2 = fsExistsSync(path), span = startSpan("file.write", {
10
+ "file.path": path,
11
+ "file.size": content.length,
12
+ "file.created": !fileExists2
13
+ });
14
+ try {
15
+ const tempPath = `${path}.tmp`;
16
+ await fsWriteFile(tempPath, content, "utf-8"), await rename(tempPath, path);
17
+ } finally {
18
+ endSpan(span);
19
+ }
20
+ }
21
+ async function fileExists(path) {
22
+ try {
23
+ return await stat(path), !0;
24
+ } catch {
25
+ return !1;
26
+ }
27
+ }
28
+ async function readFile(path) {
29
+ const span = startSpan("file.read", {
30
+ "file.path": path,
31
+ "file.exists": fsExistsSync(path)
32
+ });
33
+ try {
34
+ const content = await fsReadFile(path, "utf-8");
35
+ return isTelemetryEnabled && span && span.setAttribute("file.size", content.length), content;
36
+ } finally {
37
+ endSpan(span);
38
+ }
39
+ }
40
+ async function writeFile(path, content) {
41
+ const fileExists2 = fsExistsSync(path), span = startSpan("file.write", {
42
+ "file.path": path,
43
+ "file.size": content.length,
44
+ "file.created": !fileExists2
45
+ });
46
+ try {
47
+ await fsWriteFile(path, content, "utf-8");
48
+ } finally {
49
+ endSpan(span);
50
+ }
51
+ }
52
+ async function readJSON(path) {
53
+ const content = await readFile(path);
54
+ return JSON.parse(content);
55
+ }
56
+ async function writeJSON(path, data, pretty = !0) {
57
+ const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
58
+ await writeFile(path, content);
59
+ }
60
+ export {
61
+ atomicWrite,
62
+ ensureDir,
63
+ fileExists,
64
+ readFile,
65
+ readJSON,
66
+ writeFile,
67
+ writeJSON
68
+ };
69
+ //# sourceMappingURL=file-io.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/file-io.ts"],
4
+ "sourcesContent": ["import { mkdir, rename, readFile as fsReadFile, writeFile as fsWriteFile, stat } from \"node:fs/promises\";\nimport { existsSync as fsExistsSync } from \"node:fs\";\nimport { startSpan, endSpan } from \"../telemetry/index.js\";\n\n// Fallback for runtime environments where TELEMETRY_ENABLED is not defined by esbuild\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== 'undefined' ? TELEMETRY_ENABLED : false;\n\n/**\n * Ensure a directory exists, creating it if necessary\n */\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, { recursive: true });\n}\n\n/**\n * Write content to a file atomically using a temporary file\n */\nexport async function atomicWrite(path: string, content: string): Promise<void> {\n const fileExists = fsExistsSync(path);\n\n const span = startSpan('file.write', {\n 'file.path': path,\n 'file.size': content.length,\n 'file.created': !fileExists,\n });\n\n try {\n const tempPath = `${path}.tmp`;\n await fsWriteFile(tempPath, content, 'utf-8');\n // Rename temp file to target path (atomic operation)\n await rename(tempPath, path);\n } finally {\n endSpan(span);\n }\n}\n\n/**\n * Check if a file exists\n */\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Read a file as text\n */\nexport async function readFile(path: string): Promise<string> {\n const span = startSpan('file.read', {\n 'file.path': path,\n 'file.exists': fsExistsSync(path),\n });\n\n try {\n const content = await fsReadFile(path, 'utf-8');\n\n if (isTelemetryEnabled && span) {\n span.setAttribute('file.size', content.length);\n }\n\n return content;\n } finally {\n endSpan(span);\n }\n}\n\n/**\n * Write content to a file\n */\nexport async function writeFile(path: string, content: string): Promise<void> {\n const fileExists = fsExistsSync(path);\n\n const span = startSpan('file.write', {\n 'file.path': path,\n 'file.size': content.length,\n 'file.created': !fileExists,\n });\n\n try {\n await fsWriteFile(path, content, 'utf-8');\n } finally {\n endSpan(span);\n }\n}\n\n/**\n * Read a JSON file\n */\nexport async function readJSON<T>(path: string): Promise<T> {\n const content = await readFile(path);\n return JSON.parse(content) as T;\n}\n\n/**\n * Write a JSON object to a file\n */\nexport async function writeJSON<T>(path: string, data: T, pretty: boolean = true): Promise<void> {\n const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);\n await writeFile(path, content);\n}\n"],
5
+ "mappings": "AAAA,SAAS,OAAO,QAAQ,YAAY,YAAY,aAAa,aAAa,YAAY;AACtF,SAAS,cAAc,oBAAoB;AAC3C,SAAS,WAAW,eAAe;AAInC,MAAM,qBAAgE;AAKtE,eAAsB,UAAU,MAAY;AAC1C,QAAM,MAAM,MAAM,EAAE,WAAW,GAAI,CAAE;AACvC;AAKA,eAAsB,YAAY,MAAc,SAAe;AAC7D,QAAMA,cAAa,aAAa,IAAI,GAE9B,OAAO,UAAU,cAAc;IACnC,aAAa;IACb,aAAa,QAAQ;IACrB,gBAAgB,CAACA;GAClB;AAED,MAAI;AACF,UAAM,WAAW,GAAG,IAAI;AACxB,UAAM,YAAY,UAAU,SAAS,OAAO,GAE5C,MAAM,OAAO,UAAU,IAAI;EAC7B;AACE,YAAQ,IAAI;EACd;AACF;AAKA,eAAsB,WAAW,MAAY;AAC3C,MAAI;AACF,iBAAM,KAAK,IAAI,GACR;EACT,QAAQ;AACN,WAAO;EACT;AACF;AAKA,eAAsB,SAAS,MAAY;AACzC,QAAM,OAAO,UAAU,aAAa;IAClC,aAAa;IACb,eAAe,aAAa,IAAI;GACjC;AAED,MAAI;AACF,UAAM,UAAU,MAAM,WAAW,MAAM,OAAO;AAE9C,WAAI,sBAAsB,QACxB,KAAK,aAAa,aAAa,QAAQ,MAAM,GAGxC;EACT;AACE,YAAQ,IAAI;EACd;AACF;AAKA,eAAsB,UAAU,MAAc,SAAe;AAC3D,QAAMA,cAAa,aAAa,IAAI,GAE9B,OAAO,UAAU,cAAc;IACnC,aAAa;IACb,aAAa,QAAQ;IACrB,gBAAgB,CAACA;GAClB;AAED,MAAI;AACF,UAAM,YAAY,MAAM,SAAS,OAAO;EAC1C;AACE,YAAQ,IAAI;EACd;AACF;AAKA,eAAsB,SAAY,MAAY;AAC5C,QAAM,UAAU,MAAM,SAAS,IAAI;AACnC,SAAO,KAAK,MAAM,OAAO;AAC3B;AAKA,eAAsB,UAAa,MAAc,MAAS,SAAkB,IAAI;AAC9E,QAAM,UAAU,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI;AAC5E,QAAM,UAAU,MAAM,OAAO;AAC/B;",
6
+ "names": ["fileExists"]
7
+ }
@@ -0,0 +1,28 @@
1
+ let globalOptions = {};
2
+ function setGlobalOptions(options) {
3
+ globalOptions = { ...globalOptions, ...options };
4
+ }
5
+ function getGlobalOptions() {
6
+ return { ...globalOptions };
7
+ }
8
+ function isVerbose() {
9
+ return globalOptions.verbose === !0;
10
+ }
11
+ function isDebug() {
12
+ return globalOptions.debug === !0;
13
+ }
14
+ function logVerbose(message) {
15
+ isVerbose() && console.log(message);
16
+ }
17
+ function logDebug(message) {
18
+ isDebug() && console.debug(`[DEBUG] ${message}`);
19
+ }
20
+ export {
21
+ getGlobalOptions,
22
+ isDebug,
23
+ isVerbose,
24
+ logDebug,
25
+ logVerbose,
26
+ setGlobalOptions
27
+ };
28
+ //# sourceMappingURL=globals.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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;",
6
+ "names": []
7
+ }
@@ -0,0 +1,22 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs/promises";
3
+ async function resolveModelRoot(opts) {
4
+ const cwd = opts?.cwd ? path.resolve(opts.cwd) : process.cwd(), input = opts?.modelPath ? path.resolve(cwd, opts.modelPath) : cwd, candidates = [];
5
+ if (input.endsWith("manifest.yaml") && candidates.push(input), candidates.push(path.join(input, "manifest.yaml")), candidates.push(path.join(input, "model", "manifest.yaml")), candidates.push(path.join(input, "documentation-robotics", "model", "manifest.yaml")), process.env.DR_MODEL_PATH) {
6
+ const envPath = path.resolve(process.env.DR_MODEL_PATH);
7
+ envPath.endsWith("manifest.yaml") ? candidates.unshift(envPath) : (candidates.unshift(path.join(envPath, "manifest.yaml")), candidates.unshift(path.join(envPath, "model", "manifest.yaml")));
8
+ }
9
+ for (const manifestPath of candidates)
10
+ try {
11
+ await fs.access(manifestPath);
12
+ const normalized = path.normalize(manifestPath);
13
+ let rootPath;
14
+ return normalized.endsWith(path.join("documentation-robotics", "model", "manifest.yaml")) ? rootPath = path.dirname(path.dirname(path.dirname(normalized))) : normalized.endsWith(path.join("model", "manifest.yaml")) ? rootPath = path.dirname(path.dirname(normalized)) : rootPath = path.dirname(normalized), { rootPath, manifestPath: normalized };
15
+ } catch {
16
+ }
17
+ throw new Error("Model not found. Provide --model <path> (directory containing documentation-robotics/model/manifest.yaml) or set DR_MODEL_PATH.");
18
+ }
19
+ export {
20
+ resolveModelRoot
21
+ };
22
+ //# sourceMappingURL=model-path.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/model-path.ts"],
4
+ "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs/promises'\n\n/**\n * Resolve the model root directory and manifest path.\n * Supports both TS layout (root/model/manifest.yaml) and Python layout\n * (root/documentation-robotics/model/manifest.yaml).\n */\nexport async function resolveModelRoot(opts?: {\n modelPath?: string\n cwd?: string\n}): Promise<{ rootPath: string; manifestPath: string }> {\n const cwd = opts?.cwd ? path.resolve(opts.cwd) : process.cwd()\n const input = opts?.modelPath ? path.resolve(cwd, opts.modelPath) : cwd\n\n const candidates: string[] = []\n\n // If a manifest file is directly provided\n if (input.endsWith('manifest.yaml')) {\n candidates.push(input)\n }\n\n // If a model directory is provided\n candidates.push(path.join(input, 'manifest.yaml'))\n\n // TypeScript layout: <root>/model/manifest.yaml\n candidates.push(path.join(input, 'model', 'manifest.yaml'))\n\n // Python/TypeScript unified layout: <root>/documentation-robotics/model/manifest.yaml\n candidates.push(path.join(input, 'documentation-robotics', 'model', 'manifest.yaml'))\n\n // Optional override via env var (highest priority)\n if (process.env.DR_MODEL_PATH) {\n const envPath = path.resolve(process.env.DR_MODEL_PATH)\n if (envPath.endsWith('manifest.yaml')) {\n candidates.unshift(envPath)\n } else {\n candidates.unshift(path.join(envPath, 'manifest.yaml'))\n candidates.unshift(path.join(envPath, 'model', 'manifest.yaml'))\n }\n }\n\n for (const manifestPath of candidates) {\n try {\n await fs.access(manifestPath)\n const normalized = path.normalize(manifestPath)\n // rootPath is the parent of the model directory\n let rootPath: string\n if (normalized.endsWith(path.join('documentation-robotics', 'model', 'manifest.yaml'))) {\n // <root>/documentation-robotics/model/manifest.yaml -> <root>\n rootPath = path.dirname(path.dirname(path.dirname(normalized)))\n } else if (normalized.endsWith(path.join('model', 'manifest.yaml'))) {\n // <root>/model/manifest.yaml -> <root>\n rootPath = path.dirname(path.dirname(normalized))\n } else {\n // <root>/manifest.yaml -> <root>\n rootPath = path.dirname(normalized)\n }\n return { rootPath, manifestPath: normalized }\n } catch {\n // continue searching\n }\n }\n\n throw new Error(\n 'Model not found. Provide --model <path> (directory containing documentation-robotics/model/manifest.yaml) or set DR_MODEL_PATH.'\n )\n}\n"],
5
+ "mappings": "AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAOf,eAAsB,iBAAiB,MAGtC;AACC,QAAM,MAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,GAAG,IAAI,QAAQ,IAAG,GACtD,QAAQ,MAAM,YAAY,KAAK,QAAQ,KAAK,KAAK,SAAS,IAAI,KAE9D,aAAuB,CAAA;AAiB7B,MAdI,MAAM,SAAS,eAAe,KAChC,WAAW,KAAK,KAAK,GAIvB,WAAW,KAAK,KAAK,KAAK,OAAO,eAAe,CAAC,GAGjD,WAAW,KAAK,KAAK,KAAK,OAAO,SAAS,eAAe,CAAC,GAG1D,WAAW,KAAK,KAAK,KAAK,OAAO,0BAA0B,SAAS,eAAe,CAAC,GAGhF,QAAQ,IAAI,eAAe;AAC7B,UAAM,UAAU,KAAK,QAAQ,QAAQ,IAAI,aAAa;AAClD,YAAQ,SAAS,eAAe,IAClC,WAAW,QAAQ,OAAO,KAE1B,WAAW,QAAQ,KAAK,KAAK,SAAS,eAAe,CAAC,GACtD,WAAW,QAAQ,KAAK,KAAK,SAAS,SAAS,eAAe,CAAC;EAEnE;AAEA,aAAW,gBAAgB;AACzB,QAAI;AACF,YAAM,GAAG,OAAO,YAAY;AAC5B,YAAM,aAAa,KAAK,UAAU,YAAY;AAE9C,UAAI;AACJ,aAAI,WAAW,SAAS,KAAK,KAAK,0BAA0B,SAAS,eAAe,CAAC,IAEnF,WAAW,KAAK,QAAQ,KAAK,QAAQ,KAAK,QAAQ,UAAU,CAAC,CAAC,IACrD,WAAW,SAAS,KAAK,KAAK,SAAS,eAAe,CAAC,IAEhE,WAAW,KAAK,QAAQ,KAAK,QAAQ,UAAU,CAAC,IAGhD,WAAW,KAAK,QAAQ,UAAU,GAE7B,EAAE,UAAU,cAAc,WAAU;IAC7C,QAAQ;IAER;AAGF,QAAM,IAAI,MACR,iIAAiI;AAErI;",
6
+ "names": []
7
+ }
@@ -0,0 +1,45 @@
1
+ import { fileExists } from "./file-io.js";
2
+ import { join, dirname } from "path";
3
+ const MAX_SEARCH_DEPTH = 5, PROJECT_FOLDER_NAME = "documentation-robotics", SPEC_FOLDER_NAME = ".dr";
4
+ async function findProjectRoot(startPath) {
5
+ let currentPath = startPath || process.cwd();
6
+ for (let depth = 0; depth < MAX_SEARCH_DEPTH; depth++) {
7
+ const projectPath = join(currentPath, PROJECT_FOLDER_NAME);
8
+ if (await fileExists(projectPath))
9
+ return currentPath;
10
+ const parentPath = dirname(currentPath);
11
+ if (parentPath === currentPath)
12
+ break;
13
+ currentPath = parentPath;
14
+ }
15
+ return null;
16
+ }
17
+ async function getDocumentationRobotsPath(startPath) {
18
+ const root = await findProjectRoot(startPath);
19
+ return root ? join(root, PROJECT_FOLDER_NAME) : null;
20
+ }
21
+ async function getModelPath(startPath) {
22
+ const drPath = await getDocumentationRobotsPath(startPath);
23
+ if (!drPath)
24
+ return null;
25
+ const modelPath = join(drPath, "model");
26
+ return await fileExists(modelPath) ? modelPath : null;
27
+ }
28
+ async function getSpecReferencePath(startPath) {
29
+ const root = await findProjectRoot(startPath);
30
+ if (!root)
31
+ return null;
32
+ const specPath = join(root, SPEC_FOLDER_NAME);
33
+ return await fileExists(specPath) ? specPath : null;
34
+ }
35
+ async function isInDRProject(startPath) {
36
+ return await findProjectRoot(startPath) !== null;
37
+ }
38
+ export {
39
+ findProjectRoot,
40
+ getDocumentationRobotsPath,
41
+ getModelPath,
42
+ getSpecReferencePath,
43
+ isInDRProject
44
+ };
45
+ //# sourceMappingURL=project-paths.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/project-paths.ts"],
4
+ "sourcesContent": ["/**\n * Project Path Utilities\n *\n * Handles finding project root and related paths in the DR project structure:\n *\n * my-project/\n * \u251C\u2500\u2500 .dr/ # Reference spec (ephemeral, git-ignored)\n * \u2502 \u251C\u2500\u2500 manifest.json # Installed spec version\n * \u2502 \u2514\u2500\u2500 schemas/\n * \u2514\u2500\u2500 documentation-robotics/ # Project model (permanent, git-committed)\n * \u251C\u2500\u2500 model/\n * \u2502 \u251C\u2500\u2500 manifest.yaml # Model spec version\n * \u2502 \u2514\u2500\u2500 layers/\n * \u251C\u2500\u2500 annotations/\n * \u2514\u2500\u2500 changesets/\n */\n\nimport { fileExists } from './file-io.js';\nimport { join, dirname } from 'path';\n\nconst MAX_SEARCH_DEPTH = 5;\nconst PROJECT_FOLDER_NAME = 'documentation-robotics';\nconst SPEC_FOLDER_NAME = '.dr';\n\n/**\n * Find the project root by searching for the documentation-robotics/ folder\n *\n * Searches upward from the current working directory up to MAX_SEARCH_DEPTH levels.\n * Returns the directory containing the documentation_robotics/ folder.\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to project root, or null if not found\n *\n * @example\n * // From /home/user/my-project/documentation-robotics/model/01_motivation\n * // Returns: /home/user/my-project\n * const root = await findProjectRoot();\n */\nexport async function findProjectRoot(startPath?: string): Promise<string | null> {\n let currentPath = startPath || process.cwd();\n\n for (let depth = 0; depth < MAX_SEARCH_DEPTH; depth++) {\n const projectPath = join(currentPath, PROJECT_FOLDER_NAME);\n\n if (await fileExists(projectPath)) {\n return currentPath;\n }\n\n // Move up one directory\n const parentPath = dirname(currentPath);\n\n // Check if we've reached the root\n if (parentPath === currentPath) {\n break;\n }\n\n currentPath = parentPath;\n }\n\n return null;\n}\n\n/**\n * Get the path to the documentation-robotics/ folder\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to documentation-robotics/ folder, or null if not found\n */\nexport async function getDocumentationRobotsPath(startPath?: string): Promise<string | null> {\n const root = await findProjectRoot(startPath);\n return root ? join(root, PROJECT_FOLDER_NAME) : null;\n}\n\n/**\n * Get the path to the model directory (documentation-robotics/model/)\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to model directory, or null if not found\n */\nexport async function getModelPath(startPath?: string): Promise<string | null> {\n const drPath = await getDocumentationRobotsPath(startPath);\n if (!drPath) return null;\n\n const modelPath = join(drPath, 'model');\n return await fileExists(modelPath) ? modelPath : null;\n}\n\n/**\n * Get the path to the .dr/ folder (spec reference directory)\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns Path to .dr/ folder, or null if not found\n */\nexport async function getSpecReferencePath(startPath?: string): Promise<string | null> {\n const root = await findProjectRoot(startPath);\n if (!root) return null;\n\n const specPath = join(root, SPEC_FOLDER_NAME);\n return await fileExists(specPath) ? specPath : null;\n}\n\n/**\n * Check if currently in a DR project\n *\n * @param startPath - Optional starting path (defaults to process.cwd())\n * @returns True if documentation-robotics/ folder exists in hierarchy\n */\nexport async function isInDRProject(startPath?: string): Promise<boolean> {\n return (await findProjectRoot(startPath)) !== null;\n}\n"],
5
+ "mappings": "AAiBA,SAAS,kBAAkB;AAC3B,SAAS,MAAM,eAAe;AAE9B,MAAM,mBAAmB,GACnB,sBAAsB,0BACtB,mBAAmB;AAgBzB,eAAsB,gBAAgB,WAAkB;AACtD,MAAI,cAAc,aAAa,QAAQ,IAAG;AAE1C,WAAS,QAAQ,GAAG,QAAQ,kBAAkB,SAAS;AACrD,UAAM,cAAc,KAAK,aAAa,mBAAmB;AAEzD,QAAI,MAAM,WAAW,WAAW;AAC9B,aAAO;AAIT,UAAM,aAAa,QAAQ,WAAW;AAGtC,QAAI,eAAe;AACjB;AAGF,kBAAc;EAChB;AAEA,SAAO;AACT;AAQA,eAAsB,2BAA2B,WAAkB;AACjE,QAAM,OAAO,MAAM,gBAAgB,SAAS;AAC5C,SAAO,OAAO,KAAK,MAAM,mBAAmB,IAAI;AAClD;AAQA,eAAsB,aAAa,WAAkB;AACnD,QAAM,SAAS,MAAM,2BAA2B,SAAS;AACzD,MAAI,CAAC;AAAQ,WAAO;AAEpB,QAAM,YAAY,KAAK,QAAQ,OAAO;AACtC,SAAO,MAAM,WAAW,SAAS,IAAI,YAAY;AACnD;AAQA,eAAsB,qBAAqB,WAAkB;AAC3D,QAAM,OAAO,MAAM,gBAAgB,SAAS;AAC5C,MAAI,CAAC;AAAM,WAAO;AAElB,QAAM,WAAW,KAAK,MAAM,gBAAgB;AAC5C,SAAO,MAAM,WAAW,QAAQ,IAAI,WAAW;AACjD;AAQA,eAAsB,cAAc,WAAkB;AACpD,SAAQ,MAAM,gBAAgB,SAAS,MAAO;AAChD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,85 @@
1
+ import { ensureDir, writeFile } from "./file-io.js";
2
+ import { join } from "path";
3
+ import { getCliBundledSpecVersion } from "./spec-version.js";
4
+ import fs from "fs/promises";
5
+ async function installSpecReference(projectRoot, _force = !1) {
6
+ const drPath = join(projectRoot, ".dr"), specVersion = getCliBundledSpecVersion();
7
+ await ensureDir(drPath), await ensureDir(join(drPath, "schemas")), await ensureDir(join(drPath, "schemas", "common")), await ensureDir(join(drPath, "changesets"));
8
+ const manifestPath = join(drPath, "manifest.json"), manifest = {
9
+ specVersion,
10
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
11
+ };
12
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
13
+ const schemaSourceDir = new URL("../schemas/bundled", import.meta.url).pathname, layerSchemas = [
14
+ "01-motivation-layer.schema.json",
15
+ "02-business-layer.schema.json",
16
+ "03-security-layer.schema.json",
17
+ "04-application-layer.schema.json",
18
+ "05-technology-layer.schema.json",
19
+ "06-api-layer.schema.json",
20
+ "07-data-model-layer.schema.json",
21
+ "08-datastore-layer.schema.json",
22
+ "09-ux-layer.schema.json",
23
+ "10-navigation-layer.schema.json",
24
+ "11-apm-observability-layer.schema.json",
25
+ "12-testing-layer.schema.json"
26
+ ];
27
+ for (const schema of layerSchemas) {
28
+ const sourcePath = join(schemaSourceDir, schema), targetPath = join(drPath, "schemas", schema);
29
+ await fs.copyFile(sourcePath, targetPath);
30
+ }
31
+ const catalogFiles = [
32
+ "relationship-catalog.json",
33
+ "link-registry.json",
34
+ "link-registry.schema.json",
35
+ "relationship-type.schema.json"
36
+ ];
37
+ for (const file of catalogFiles) {
38
+ const sourcePath = join(schemaSourceDir, file), targetPath = join(drPath, "schemas", file);
39
+ await fs.copyFile(sourcePath, targetPath);
40
+ }
41
+ const commonSchemas = [
42
+ "layer-extensions.schema.json",
43
+ "predicates.schema.json",
44
+ "relationships.schema.json",
45
+ "source-references.schema.json"
46
+ ];
47
+ for (const schema of commonSchemas) {
48
+ const sourcePath = join(schemaSourceDir, "common", schema), targetPath = join(drPath, "schemas", "common", schema);
49
+ await fs.copyFile(sourcePath, targetPath);
50
+ }
51
+ const readmePath = join(drPath, "README.md"), readme = `# Documentation Robotics - Spec Reference
52
+
53
+ This directory contains the specification reference for Documentation Robotics.
54
+
55
+ **Spec Version:** ${specVersion}
56
+ **Installed:** ${(/* @__PURE__ */ new Date()).toISOString()}
57
+
58
+ ## Structure
59
+
60
+ - \`manifest.json\` - Spec version information
61
+ - \`schemas/\` - JSON Schema definitions for all layers
62
+ - \`changesets/\` - Model changesets (active and saved)
63
+
64
+ ## Important
65
+
66
+ This directory is ephemeral and should be git-ignored. It will be recreated
67
+ automatically by the CLI when needed.
68
+
69
+ Do not manually edit files in this directory.
70
+ `;
71
+ await writeFile(readmePath, readme);
72
+ }
73
+ async function needsSpecReferenceInstall(projectRoot) {
74
+ const manifestPath = join(projectRoot, ".dr", "manifest.json");
75
+ try {
76
+ return await (await import("fs/promises")).access(manifestPath), !1;
77
+ } catch {
78
+ return !0;
79
+ }
80
+ }
81
+ export {
82
+ installSpecReference,
83
+ needsSpecReferenceInstall
84
+ };
85
+ //# sourceMappingURL=spec-installer.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/spec-installer.ts"],
4
+ "sourcesContent": ["/**\n * Spec Reference Installer\n *\n * Manages the .dr/ folder which contains the spec reference (schemas, etc.)\n * This is an ephemeral folder that should be git-ignored\n */\n\nimport { ensureDir, writeFile } from './file-io.js';\nimport { join } from 'path';\nimport { getCliBundledSpecVersion } from './spec-version.js';\nimport fs from 'fs/promises';\n\n/**\n * Install or update the .dr/ spec reference folder\n *\n * Creates:\n * - .dr/manifest.json with spec version\n * - .dr/schemas/ with all bundled schemas\n * - .dr/changesets/ directory\n * - .dr/README.md\n *\n * @param projectRoot - Path to project root\n * @param _force - If true, overwrite existing files (reserved for future use)\n */\nexport async function installSpecReference(\n projectRoot: string,\n _force: boolean = false\n): Promise<void> {\n const drPath = join(projectRoot, '.dr');\n const specVersion = getCliBundledSpecVersion();\n\n // Create .dr directory structure\n await ensureDir(drPath);\n await ensureDir(join(drPath, 'schemas'));\n await ensureDir(join(drPath, 'schemas', 'common'));\n await ensureDir(join(drPath, 'changesets'));\n\n // Write manifest.json\n const manifestPath = join(drPath, 'manifest.json');\n const manifest = {\n specVersion: specVersion,\n installedAt: new Date().toISOString(),\n };\n await writeFile(manifestPath, JSON.stringify(manifest, null, 2));\n\n // Copy bundled schemas\n const schemaSourceDir = new URL('../schemas/bundled', import.meta.url).pathname;\n\n // Copy layer schemas\n const layerSchemas = [\n '01-motivation-layer.schema.json',\n '02-business-layer.schema.json',\n '03-security-layer.schema.json',\n '04-application-layer.schema.json',\n '05-technology-layer.schema.json',\n '06-api-layer.schema.json',\n '07-data-model-layer.schema.json',\n '08-datastore-layer.schema.json',\n '09-ux-layer.schema.json',\n '10-navigation-layer.schema.json',\n '11-apm-observability-layer.schema.json',\n '12-testing-layer.schema.json',\n ];\n\n for (const schema of layerSchemas) {\n const sourcePath = join(schemaSourceDir, schema);\n const targetPath = join(drPath, 'schemas', schema);\n await fs.copyFile(sourcePath, targetPath);\n }\n\n // Copy catalog and registry files\n const catalogFiles = [\n 'relationship-catalog.json',\n 'link-registry.json',\n 'link-registry.schema.json',\n 'relationship-type.schema.json',\n ];\n\n for (const file of catalogFiles) {\n const sourcePath = join(schemaSourceDir, file);\n const targetPath = join(drPath, 'schemas', file);\n await fs.copyFile(sourcePath, targetPath);\n }\n\n // Copy common schemas\n const commonSchemas = [\n 'layer-extensions.schema.json',\n 'predicates.schema.json',\n 'relationships.schema.json',\n 'source-references.schema.json',\n ];\n\n for (const schema of commonSchemas) {\n const sourcePath = join(schemaSourceDir, 'common', schema);\n const targetPath = join(drPath, 'schemas', 'common', schema);\n await fs.copyFile(sourcePath, targetPath);\n }\n\n // Create README.md\n const readmePath = join(drPath, 'README.md');\n const readme = `# Documentation Robotics - Spec Reference\n\nThis directory contains the specification reference for Documentation Robotics.\n\n**Spec Version:** ${specVersion}\n**Installed:** ${new Date().toISOString()}\n\n## Structure\n\n- \\`manifest.json\\` - Spec version information\n- \\`schemas/\\` - JSON Schema definitions for all layers\n- \\`changesets/\\` - Model changesets (active and saved)\n\n## Important\n\nThis directory is ephemeral and should be git-ignored. It will be recreated\nautomatically by the CLI when needed.\n\nDo not manually edit files in this directory.\n`;\n await writeFile(readmePath, readme);\n}\n\n/**\n * Check if spec reference needs to be installed or updated\n *\n * @param projectRoot - Path to project root\n * @returns True if installation/update is needed\n */\nexport async function needsSpecReferenceInstall(projectRoot: string): Promise<boolean> {\n const manifestPath = join(projectRoot, '.dr', 'manifest.json');\n\n try {\n const fs = await import('fs/promises');\n await fs.access(manifestPath);\n return false;\n } catch {\n return true;\n }\n}\n"],
5
+ "mappings": "AAOA,SAAS,WAAW,iBAAiB;AACrC,SAAS,YAAY;AACrB,SAAS,gCAAgC;AACzC,OAAO,QAAQ;AAcf,eAAsB,qBACpB,aACA,SAAkB,IAAK;AAEvB,QAAM,SAAS,KAAK,aAAa,KAAK,GAChC,cAAc,yBAAwB;AAG5C,QAAM,UAAU,MAAM,GACtB,MAAM,UAAU,KAAK,QAAQ,SAAS,CAAC,GACvC,MAAM,UAAU,KAAK,QAAQ,WAAW,QAAQ,CAAC,GACjD,MAAM,UAAU,KAAK,QAAQ,YAAY,CAAC;AAG1C,QAAM,eAAe,KAAK,QAAQ,eAAe,GAC3C,WAAW;IACf;IACA,cAAa,oBAAI,KAAI,GAAG,YAAW;;AAErC,QAAM,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAG/D,QAAM,kBAAkB,IAAI,IAAI,sBAAsB,YAAY,GAAG,EAAE,UAGjE,eAAe;IACnB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;AAGF,aAAW,UAAU,cAAc;AACjC,UAAM,aAAa,KAAK,iBAAiB,MAAM,GACzC,aAAa,KAAK,QAAQ,WAAW,MAAM;AACjD,UAAM,GAAG,SAAS,YAAY,UAAU;EAC1C;AAGA,QAAM,eAAe;IACnB;IACA;IACA;IACA;;AAGF,aAAW,QAAQ,cAAc;AAC/B,UAAM,aAAa,KAAK,iBAAiB,IAAI,GACvC,aAAa,KAAK,QAAQ,WAAW,IAAI;AAC/C,UAAM,GAAG,SAAS,YAAY,UAAU;EAC1C;AAGA,QAAM,gBAAgB;IACpB;IACA;IACA;IACA;;AAGF,aAAW,UAAU,eAAe;AAClC,UAAM,aAAa,KAAK,iBAAiB,UAAU,MAAM,GACnD,aAAa,KAAK,QAAQ,WAAW,UAAU,MAAM;AAC3D,UAAM,GAAG,SAAS,YAAY,UAAU;EAC1C;AAGA,QAAM,aAAa,KAAK,QAAQ,WAAW,GACrC,SAAS;;;;oBAIG,WAAW;kBACd,oBAAI,KAAI,GAAG,YAAW,CAAE;;;;;;;;;;;;;;;AAevC,QAAM,UAAU,YAAY,MAAM;AACpC;AAQA,eAAsB,0BAA0B,aAAmB;AACjE,QAAM,eAAe,KAAK,aAAa,OAAO,eAAe;AAE7D,MAAI;AAEF,kBADW,MAAM,OAAO,aAAa,GAC5B,OAAO,YAAY,GACrB;EACT,QAAQ;AACN,WAAO;EACT;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,33 @@
1
+ import { readJSON, fileExists } from "./file-io.js";
2
+ import { join } from "path";
3
+ const BUNDLED_SPEC_VERSION = "0.7.0";
4
+ function getCliBundledSpecVersion() {
5
+ return BUNDLED_SPEC_VERSION;
6
+ }
7
+ async function getInstalledSpecVersion(drPath) {
8
+ const manifestPath = join(drPath, "manifest.json");
9
+ if (!await fileExists(manifestPath))
10
+ return null;
11
+ try {
12
+ return (await readJSON(manifestPath)).specVersion || null;
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+ async function getModelSpecVersion(modelPath) {
18
+ const manifestPath = join(modelPath, "manifest.yaml");
19
+ if (!await fileExists(manifestPath))
20
+ return null;
21
+ try {
22
+ const yaml = await import("yaml"), content = await (await import("fs/promises")).readFile(manifestPath, "utf-8"), manifest = yaml.parse(content);
23
+ return manifest.spec_version || manifest.specVersion || null;
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+ export {
29
+ getCliBundledSpecVersion,
30
+ getInstalledSpecVersion,
31
+ getModelSpecVersion
32
+ };
33
+ //# sourceMappingURL=spec-version.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/spec-version.ts"],
4
+ "sourcesContent": ["/**\n * Spec Version Utilities\n *\n * Manages spec version information for the CLI\n */\n\nimport { readJSON, fileExists } from './file-io.js';\nimport { join } from 'path';\n\n/**\n * Bundled spec version\n * This should match the version in ../../../spec/VERSION\n * Updated during build process\n */\nconst BUNDLED_SPEC_VERSION = '0.7.0';\n\n/**\n * Get the CLI's bundled spec version\n *\n * This is the version of the DR specification that is bundled with this CLI.\n * It represents what version of the spec this CLI supports.\n *\n * @returns Bundled spec version string\n */\nexport function getCliBundledSpecVersion(): string {\n return BUNDLED_SPEC_VERSION;\n}\n\n/**\n * Read spec version from .dr/manifest.json (installed spec reference)\n *\n * @param drPath - Path to .dr/ directory\n * @returns Spec version from .dr/manifest.json, or null if not found\n */\nexport async function getInstalledSpecVersion(drPath: string): Promise<string | null> {\n const manifestPath = join(drPath, 'manifest.json');\n\n if (!(await fileExists(manifestPath))) {\n return null;\n }\n\n try {\n const manifest = await readJSON<{ specVersion?: string }>(manifestPath);\n return manifest.specVersion || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read spec version from documentation_robotics/model/manifest.yaml\n *\n * @param modelPath - Path to model directory\n * @returns Spec version from model manifest, or null if not found\n */\nexport async function getModelSpecVersion(modelPath: string): Promise<string | null> {\n const manifestPath = join(modelPath, 'manifest.yaml');\n\n if (!(await fileExists(manifestPath))) {\n return null;\n }\n\n try {\n // Use YAML parser\n const yaml = await import('yaml');\n const fs = await import('fs/promises');\n const content = await fs.readFile(manifestPath, 'utf-8');\n const manifest = yaml.parse(content) as { spec_version?: string; specVersion?: string };\n // Check snake_case first (Python CLI format), then camelCase (legacy)\n return manifest.spec_version || manifest.specVersion || null;\n } catch {\n return null;\n }\n}\n"],
5
+ "mappings": "AAMA,SAAS,UAAU,kBAAkB;AACrC,SAAS,YAAY;AAOrB,MAAM,uBAAuB;AAUvB,SAAU,2BAAwB;AACtC,SAAO;AACT;AAQA,eAAsB,wBAAwB,QAAc;AAC1D,QAAM,eAAe,KAAK,QAAQ,eAAe;AAEjD,MAAI,CAAE,MAAM,WAAW,YAAY;AACjC,WAAO;AAGT,MAAI;AAEF,YADiB,MAAM,SAAmC,YAAY,GACtD,eAAe;EACjC,QAAQ;AACN,WAAO;EACT;AACF;AAQA,eAAsB,oBAAoB,WAAiB;AACzD,QAAM,eAAe,KAAK,WAAW,eAAe;AAEpD,MAAI,CAAE,MAAM,WAAW,YAAY;AACjC,WAAO;AAGT,MAAI;AAEF,UAAM,OAAO,MAAM,OAAO,MAAM,GAE1B,UAAU,OADL,MAAM,OAAO,aAAa,GACZ,SAAS,cAAc,OAAO,GACjD,WAAW,KAAK,MAAM,OAAO;AAEnC,WAAO,SAAS,gBAAgB,SAAS,eAAe;EAC1D,QAAQ;AACN,WAAO;EACT;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ import { ValidationResult } from "./types.js";
2
+ import { SchemaValidator } from "./schema-validator.js";
3
+ import { NamingValidator } from "./naming-validator.js";
4
+ import { ReferenceValidator } from "./reference-validator.js";
5
+ import { SemanticValidator } from "./semantic-validator.js";
6
+ import { Validator } from "./validator.js";
7
+ export {
8
+ NamingValidator,
9
+ ReferenceValidator,
10
+ SchemaValidator,
11
+ SemanticValidator,
12
+ ValidationResult,
13
+ Validator
14
+ };
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/validators/index.ts"],
4
+ "sourcesContent": ["/**\n * Validators module - 4-stage validation pipeline\n */\n\nexport { ValidationResult, type ValidationIssue } from './types.js';\nexport { SchemaValidator } from './schema-validator.js';\nexport { NamingValidator } from './naming-validator.js';\nexport { ReferenceValidator } from './reference-validator.js';\nexport { SemanticValidator } from './semantic-validator.js';\nexport { Validator } from './validator.js';\n"],
5
+ "mappings": "AAIA,SAAS,wBAA8C;AACvD,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,82 @@
1
+ import { ValidationResult } from "./types.js";
2
+ class NamingValidator {
3
+ // Element ID format: {layer}.{type}.{name} (dot-separated, matching Python CLI)
4
+ // Also accepts: {layer}-{type}-{kebab-case-name} (legacy kebab-case format)
5
+ DOT_SEPARATED_PATTERN = /^[a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_-]*)+$/;
6
+ KEBAB_CASE_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*(-[a-z0-9]+)+$/;
7
+ // Known layer names (including hyphenated and underscored ones)
8
+ KNOWN_LAYERS = [
9
+ "motivation",
10
+ "business",
11
+ "security",
12
+ "application",
13
+ "technology",
14
+ "api",
15
+ "data_model",
16
+ "data-model",
17
+ "datastore",
18
+ "data-store",
19
+ "ux",
20
+ "navigation",
21
+ "apm",
22
+ "testing"
23
+ ];
24
+ /**
25
+ * Validate all elements in a layer
26
+ */
27
+ validateLayer(layer) {
28
+ const result = new ValidationResult();
29
+ for (const element of layer.listElements())
30
+ this.validateElementId(element.id, layer.name, result);
31
+ return result;
32
+ }
33
+ /**
34
+ * Validate an individual element ID
35
+ */
36
+ validateElementId(elementId, layerName, result) {
37
+ const isDotSeparated = this.DOT_SEPARATED_PATTERN.test(elementId), isKebabCase = this.KEBAB_CASE_PATTERN.test(elementId);
38
+ if (!isDotSeparated && !isKebabCase) {
39
+ result.addError({
40
+ layer: layerName,
41
+ elementId,
42
+ message: `Invalid element ID format: ${elementId}`,
43
+ fixSuggestion: "Use format: {layer}.{type}.{name} (e.g., motivation.goal.increase-revenue) or {layer}-{type}-{kebab-case-name}"
44
+ });
45
+ return;
46
+ }
47
+ const idLayer = this.extractLayerFromId(elementId, isDotSeparated), normalizedLayerName = layerName.replace(/-/g, "_");
48
+ if (idLayer.replace(/-/g, "_") !== normalizedLayerName) {
49
+ result.addError({
50
+ layer: layerName,
51
+ elementId,
52
+ message: `Element ID layer prefix '${idLayer}' does not match layer '${layerName}'`,
53
+ fixSuggestion: `Change prefix to '${layerName}' (e.g., ${layerName}.type.name)`
54
+ });
55
+ return;
56
+ }
57
+ const separator = isDotSeparated ? "." : "-";
58
+ if (elementId.slice(idLayer.length + 1).split(separator).length < 2) {
59
+ result.addError({
60
+ layer: layerName,
61
+ elementId,
62
+ message: "Element ID must have type and name components after layer",
63
+ fixSuggestion: "Use format: {layer}.{type}.{name}"
64
+ });
65
+ return;
66
+ }
67
+ }
68
+ /**
69
+ * Extract layer name from element ID, handling hyphenated/underscored layer names
70
+ */
71
+ extractLayerFromId(elementId, isDotSeparated) {
72
+ const sortedLayers = [...this.KNOWN_LAYERS].sort((a, b) => b.length - a.length), separator = isDotSeparated ? "." : "-";
73
+ for (const layer of sortedLayers)
74
+ if (elementId.startsWith(layer + separator))
75
+ return layer;
76
+ return elementId.split(separator)[0];
77
+ }
78
+ }
79
+ export {
80
+ NamingValidator
81
+ };
82
+ //# sourceMappingURL=naming-validator.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/validators/naming-validator.ts"],
4
+ "sourcesContent": ["/**\n * Naming validation for element IDs\n */\n\nimport { ValidationResult } from './types.js';\nimport type { Layer } from '../core/layer.js';\n\n/**\n * Validator for element ID naming conventions\n */\nexport class NamingValidator {\n // Element ID format: {layer}.{type}.{name} (dot-separated, matching Python CLI)\n // Also accepts: {layer}-{type}-{kebab-case-name} (legacy kebab-case format)\n private readonly DOT_SEPARATED_PATTERN = /^[a-z_][a-z0-9_]*(\\.[a-z_][a-z0-9_-]*)+$/;\n private readonly KEBAB_CASE_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*(-[a-z0-9]+)+$/;\n\n // Known layer names (including hyphenated and underscored ones)\n private readonly KNOWN_LAYERS = [\n 'motivation',\n 'business',\n 'security',\n 'application',\n 'technology',\n 'api',\n 'data_model',\n 'data-model',\n 'datastore',\n 'data-store',\n 'ux',\n 'navigation',\n 'apm',\n 'testing',\n ];\n\n /**\n * Validate all elements in a layer\n */\n validateLayer(layer: Layer): ValidationResult {\n const result = new ValidationResult();\n\n for (const element of layer.listElements()) {\n this.validateElementId(element.id, layer.name, result);\n }\n\n return result;\n }\n\n /**\n * Validate an individual element ID\n */\n private validateElementId(\n elementId: string,\n layerName: string,\n result: ValidationResult\n ): void {\n // Check if ID matches dot-separated format (primary) or kebab-case format (legacy)\n const isDotSeparated = this.DOT_SEPARATED_PATTERN.test(elementId);\n const isKebabCase = this.KEBAB_CASE_PATTERN.test(elementId);\n\n if (!isDotSeparated && !isKebabCase) {\n result.addError({\n layer: layerName,\n elementId,\n message: `Invalid element ID format: ${elementId}`,\n fixSuggestion: 'Use format: {layer}.{type}.{name} (e.g., motivation.goal.increase-revenue) or {layer}-{type}-{kebab-case-name}',\n });\n return;\n }\n\n const idLayer = this.extractLayerFromId(elementId, isDotSeparated);\n\n // Verify layer prefix matches the actual layer\n const normalizedLayerName = layerName.replace(/-/g, '_');\n const normalizedIdLayer = idLayer.replace(/-/g, '_');\n\n if (normalizedIdLayer !== normalizedLayerName) {\n result.addError({\n layer: layerName,\n elementId,\n message: `Element ID layer prefix '${idLayer}' does not match layer '${layerName}'`,\n fixSuggestion: `Change prefix to '${layerName}' (e.g., ${layerName}.type.name)`,\n });\n return;\n }\n\n // Extract remaining parts after layer\n const separator = isDotSeparated ? '.' : '-';\n const remainingParts = elementId.slice(idLayer.length + 1).split(separator);\n\n // Verify type is present and non-empty\n if (remainingParts.length < 2) {\n result.addError({\n layer: layerName,\n elementId,\n message: 'Element ID must have type and name components after layer',\n fixSuggestion: 'Use format: {layer}.{type}.{name}',\n });\n return;\n }\n }\n\n /**\n * Extract layer name from element ID, handling hyphenated/underscored layer names\n */\n private extractLayerFromId(elementId: string, isDotSeparated: boolean): string {\n // Try to match known layers in order of specificity (longest first)\n const sortedLayers = [...this.KNOWN_LAYERS].sort((a, b) => b.length - a.length);\n\n const separator = isDotSeparated ? '.' : '-';\n\n for (const layer of sortedLayers) {\n if (elementId.startsWith(layer + separator)) {\n return layer;\n }\n }\n\n // Fallback: take first component before separator\n return elementId.split(separator)[0];\n }\n}\n"],
5
+ "mappings": "AAIA,SAAS,wBAAwB;AAM3B,MAAO,gBAAe;;;EAGT,wBAAwB;EACxB,qBAAqB;;EAGrB,eAAe;IAC9B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;;;EAMF,cAAc,OAAY;AACxB,UAAM,SAAS,IAAI,iBAAgB;AAEnC,eAAW,WAAW,MAAM,aAAY;AACtC,WAAK,kBAAkB,QAAQ,IAAI,MAAM,MAAM,MAAM;AAGvD,WAAO;EACT;;;;EAKQ,kBACN,WACA,WACA,QAAwB;AAGxB,UAAM,iBAAiB,KAAK,sBAAsB,KAAK,SAAS,GAC1D,cAAc,KAAK,mBAAmB,KAAK,SAAS;AAE1D,QAAI,CAAC,kBAAkB,CAAC,aAAa;AACnC,aAAO,SAAS;QACd,OAAO;QACP;QACA,SAAS,8BAA8B,SAAS;QAChD,eAAe;OAChB;AACD;IACF;AAEA,UAAM,UAAU,KAAK,mBAAmB,WAAW,cAAc,GAG3D,sBAAsB,UAAU,QAAQ,MAAM,GAAG;AAGvD,QAF0B,QAAQ,QAAQ,MAAM,GAAG,MAEzB,qBAAqB;AAC7C,aAAO,SAAS;QACd,OAAO;QACP;QACA,SAAS,4BAA4B,OAAO,2BAA2B,SAAS;QAChF,eAAe,qBAAqB,SAAS,YAAY,SAAS;OACnE;AACD;IACF;AAGA,UAAM,YAAY,iBAAiB,MAAM;AAIzC,QAHuB,UAAU,MAAM,QAAQ,SAAS,CAAC,EAAE,MAAM,SAAS,EAGvD,SAAS,GAAG;AAC7B,aAAO,SAAS;QACd,OAAO;QACP;QACA,SAAS;QACT,eAAe;OAChB;AACD;IACF;EACF;;;;EAKQ,mBAAmB,WAAmB,gBAAuB;AAEnE,UAAM,eAAe,CAAC,GAAG,KAAK,YAAY,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,GAExE,YAAY,iBAAiB,MAAM;AAEzC,eAAW,SAAS;AAClB,UAAI,UAAU,WAAW,QAAQ,SAAS;AACxC,eAAO;AAKX,WAAO,UAAU,MAAM,SAAS,EAAE,CAAC;EACrC;;",
6
+ "names": []
7
+ }