@principal-ai/principal-studio-cli 0.35.3

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 (184) hide show
  1. package/README.md +172 -0
  2. package/dist/commands/agent-session.d.ts +3 -0
  3. package/dist/commands/agent-session.d.ts.map +1 -0
  4. package/dist/commands/agent-session.js +67 -0
  5. package/dist/commands/agent-sessions.d.ts +11 -0
  6. package/dist/commands/agent-sessions.d.ts.map +1 -0
  7. package/dist/commands/agent-sessions.js +39 -0
  8. package/dist/commands/auxiliary/index.d.ts +3 -0
  9. package/dist/commands/auxiliary/index.d.ts.map +1 -0
  10. package/dist/commands/auxiliary/index.js +9 -0
  11. package/dist/commands/auxiliary/validate.d.ts +3 -0
  12. package/dist/commands/auxiliary/validate.d.ts.map +1 -0
  13. package/dist/commands/auxiliary/validate.js +123 -0
  14. package/dist/commands/collector/check.d.ts +6 -0
  15. package/dist/commands/collector/check.d.ts.map +1 -0
  16. package/dist/commands/collector/check.js +153 -0
  17. package/dist/commands/collector/diagnose.d.ts +6 -0
  18. package/dist/commands/collector/diagnose.d.ts.map +1 -0
  19. package/dist/commands/collector/diagnose.js +229 -0
  20. package/dist/commands/collector/index.d.ts +6 -0
  21. package/dist/commands/collector/index.d.ts.map +1 -0
  22. package/dist/commands/collector/index.js +16 -0
  23. package/dist/commands/collector/status.d.ts +6 -0
  24. package/dist/commands/collector/status.d.ts.map +1 -0
  25. package/dist/commands/collector/status.js +77 -0
  26. package/dist/commands/collector/utils.d.ts +92 -0
  27. package/dist/commands/collector/utils.d.ts.map +1 -0
  28. package/dist/commands/collector/utils.js +154 -0
  29. package/dist/commands/coverage.d.ts +9 -0
  30. package/dist/commands/coverage.d.ts.map +1 -0
  31. package/dist/commands/coverage.js +252 -0
  32. package/dist/commands/create.d.ts +6 -0
  33. package/dist/commands/create.d.ts.map +1 -0
  34. package/dist/commands/create.js +50 -0
  35. package/dist/commands/events/index.d.ts +3 -0
  36. package/dist/commands/events/index.d.ts.map +1 -0
  37. package/dist/commands/events/index.js +9 -0
  38. package/dist/commands/events/validate.d.ts +3 -0
  39. package/dist/commands/events/validate.d.ts.map +1 -0
  40. package/dist/commands/events/validate.js +124 -0
  41. package/dist/commands/formats.d.ts +6 -0
  42. package/dist/commands/formats.d.ts.map +1 -0
  43. package/dist/commands/formats.js +1118 -0
  44. package/dist/commands/hooks.d.ts +9 -0
  45. package/dist/commands/hooks.d.ts.map +1 -0
  46. package/dist/commands/hooks.js +295 -0
  47. package/dist/commands/inbox.d.ts +13 -0
  48. package/dist/commands/inbox.d.ts.map +1 -0
  49. package/dist/commands/inbox.js +179 -0
  50. package/dist/commands/init.d.ts +6 -0
  51. package/dist/commands/init.d.ts.map +1 -0
  52. package/dist/commands/init.js +220 -0
  53. package/dist/commands/lint.d.ts +11 -0
  54. package/dist/commands/lint.d.ts.map +1 -0
  55. package/dist/commands/lint.js +71 -0
  56. package/dist/commands/list.d.ts +6 -0
  57. package/dist/commands/list.d.ts.map +1 -0
  58. package/dist/commands/list.js +78 -0
  59. package/dist/commands/migrate-nodes.d.ts +9 -0
  60. package/dist/commands/migrate-nodes.d.ts.map +1 -0
  61. package/dist/commands/migrate-nodes.js +363 -0
  62. package/dist/commands/migrate-scopes-to-canvas.d.ts +3 -0
  63. package/dist/commands/migrate-scopes-to-canvas.d.ts.map +1 -0
  64. package/dist/commands/migrate-scopes-to-canvas.js +143 -0
  65. package/dist/commands/migration.d.ts +6 -0
  66. package/dist/commands/migration.d.ts.map +1 -0
  67. package/dist/commands/migration.js +247 -0
  68. package/dist/commands/open-studio.d.ts +10 -0
  69. package/dist/commands/open-studio.d.ts.map +1 -0
  70. package/dist/commands/open-studio.js +32 -0
  71. package/dist/commands/opencode/fetch.d.ts +3 -0
  72. package/dist/commands/opencode/fetch.d.ts.map +1 -0
  73. package/dist/commands/opencode/fetch.js +25 -0
  74. package/dist/commands/opencode/index.d.ts +3 -0
  75. package/dist/commands/opencode/index.d.ts.map +1 -0
  76. package/dist/commands/opencode/index.js +11 -0
  77. package/dist/commands/opencode/list-sessions.d.ts +3 -0
  78. package/dist/commands/opencode/list-sessions.d.ts.map +1 -0
  79. package/dist/commands/opencode/list-sessions.js +20 -0
  80. package/dist/commands/repo.d.ts +18 -0
  81. package/dist/commands/repo.d.ts.map +1 -0
  82. package/dist/commands/repo.js +233 -0
  83. package/dist/commands/schema.d.ts +6 -0
  84. package/dist/commands/schema.d.ts.map +1 -0
  85. package/dist/commands/schema.js +367 -0
  86. package/dist/commands/scopes/index.d.ts +3 -0
  87. package/dist/commands/scopes/index.d.ts.map +1 -0
  88. package/dist/commands/scopes/index.js +9 -0
  89. package/dist/commands/scopes/validate.d.ts +3 -0
  90. package/dist/commands/scopes/validate.d.ts.map +1 -0
  91. package/dist/commands/scopes/validate.js +187 -0
  92. package/dist/commands/setup.d.ts +6 -0
  93. package/dist/commands/setup.d.ts.map +1 -0
  94. package/dist/commands/setup.js +406 -0
  95. package/dist/commands/starred-collections.d.ts +12 -0
  96. package/dist/commands/starred-collections.d.ts.map +1 -0
  97. package/dist/commands/starred-collections.js +310 -0
  98. package/dist/commands/subsystem-model.d.ts +11 -0
  99. package/dist/commands/subsystem-model.d.ts.map +1 -0
  100. package/dist/commands/subsystem-model.js +249 -0
  101. package/dist/commands/topic.d.ts +15 -0
  102. package/dist/commands/topic.d.ts.map +1 -0
  103. package/dist/commands/topic.js +683 -0
  104. package/dist/commands/tour.d.ts +23 -0
  105. package/dist/commands/tour.d.ts.map +1 -0
  106. package/dist/commands/tour.js +783 -0
  107. package/dist/commands/trace/flow.d.ts +6 -0
  108. package/dist/commands/trace/flow.d.ts.map +1 -0
  109. package/dist/commands/trace/flow.js +158 -0
  110. package/dist/commands/trace/index.d.ts +6 -0
  111. package/dist/commands/trace/index.d.ts.map +1 -0
  112. package/dist/commands/trace/index.js +20 -0
  113. package/dist/commands/trace/inspect.d.ts +6 -0
  114. package/dist/commands/trace/inspect.d.ts.map +1 -0
  115. package/dist/commands/trace/inspect.js +187 -0
  116. package/dist/commands/trace/list.d.ts +6 -0
  117. package/dist/commands/trace/list.d.ts.map +1 -0
  118. package/dist/commands/trace/list.js +145 -0
  119. package/dist/commands/trace/registrations.d.ts +6 -0
  120. package/dist/commands/trace/registrations.d.ts.map +1 -0
  121. package/dist/commands/trace/registrations.js +81 -0
  122. package/dist/commands/trace/validate.d.ts +6 -0
  123. package/dist/commands/trace/validate.d.ts.map +1 -0
  124. package/dist/commands/trace/validate.js +276 -0
  125. package/dist/commands/trail.d.ts +36 -0
  126. package/dist/commands/trail.d.ts.map +1 -0
  127. package/dist/commands/trail.js +622 -0
  128. package/dist/commands/validate.d.ts +12 -0
  129. package/dist/commands/validate.d.ts.map +1 -0
  130. package/dist/commands/validate.js +3334 -0
  131. package/dist/commands/workflow/implementation.d.ts +3 -0
  132. package/dist/commands/workflow/implementation.d.ts.map +1 -0
  133. package/dist/commands/workflow/implementation.js +51 -0
  134. package/dist/commands/workflow/index.d.ts +3 -0
  135. package/dist/commands/workflow/index.d.ts.map +1 -0
  136. package/dist/commands/workflow/index.js +19 -0
  137. package/dist/commands/workflow/inspect.d.ts +3 -0
  138. package/dist/commands/workflow/inspect.d.ts.map +1 -0
  139. package/dist/commands/workflow/inspect.js +123 -0
  140. package/dist/commands/workflow/list.d.ts +3 -0
  141. package/dist/commands/workflow/list.d.ts.map +1 -0
  142. package/dist/commands/workflow/list.js +95 -0
  143. package/dist/commands/workflow/render.d.ts +3 -0
  144. package/dist/commands/workflow/render.d.ts.map +1 -0
  145. package/dist/commands/workflow/render.js +88 -0
  146. package/dist/commands/workflow/test.d.ts +3 -0
  147. package/dist/commands/workflow/test.d.ts.map +1 -0
  148. package/dist/commands/workflow/test.js +141 -0
  149. package/dist/commands/workflow/utils.d.ts +49 -0
  150. package/dist/commands/workflow/utils.d.ts.map +1 -0
  151. package/dist/commands/workflow/utils.js +164 -0
  152. package/dist/commands/workflow/validate.d.ts +3 -0
  153. package/dist/commands/workflow/validate.d.ts.map +1 -0
  154. package/dist/commands/workflow/validate.js +344 -0
  155. package/dist/file-utils.d.ts +15 -0
  156. package/dist/file-utils.d.ts.map +1 -0
  157. package/dist/file-utils.js +29 -0
  158. package/dist/index.cjs +285718 -0
  159. package/dist/index.cjs.map +7 -0
  160. package/dist/index.d.ts +8 -0
  161. package/dist/index.d.ts.map +1 -0
  162. package/dist/index.js +81 -0
  163. package/dist/lib/bridge-ipc.d.ts +29 -0
  164. package/dist/lib/bridge-ipc.d.ts.map +1 -0
  165. package/dist/lib/bridge-ipc.js +128 -0
  166. package/dist/lib/github-user.d.ts +17 -0
  167. package/dist/lib/github-user.d.ts.map +1 -0
  168. package/dist/lib/github-user.js +53 -0
  169. package/dist/lib/open-url.d.ts +11 -0
  170. package/dist/lib/open-url.d.ts.map +1 -0
  171. package/dist/lib/open-url.js +45 -0
  172. package/dist/lib/subsystem-model-store.d.ts +74 -0
  173. package/dist/lib/subsystem-model-store.d.ts.map +1 -0
  174. package/dist/lib/subsystem-model-store.js +292 -0
  175. package/dist/lib/tour-cache.d.ts +26 -0
  176. package/dist/lib/tour-cache.d.ts.map +1 -0
  177. package/dist/lib/tour-cache.js +46 -0
  178. package/dist/lib/trail-cache.d.ts +40 -0
  179. package/dist/lib/trail-cache.d.ts.map +1 -0
  180. package/dist/lib/trail-cache.js +97 -0
  181. package/dist/lib/viewer-ipc.d.ts +44 -0
  182. package/dist/lib/viewer-ipc.d.ts.map +1 -0
  183. package/dist/lib/viewer-ipc.js +68 -0
  184. package/package.json +68 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Coverage command - Measure telemetry coverage for canvas nodes
3
+ *
4
+ * This command analyzes .otel.canvas files and checks which implementation files
5
+ * contain the OpenTelemetry events documented in canvas nodes.
6
+ */
7
+ import { Command } from 'commander';
8
+ import { resolve } from 'node:path';
9
+ import { readFile } from 'node:fs/promises';
10
+ import chalk from 'chalk';
11
+ import { analyzeCoverage, } from '@principal-ai/subsystems-core/node';
12
+ import { FilesystemService, NodeFileSystemAdapter } from '@principal-ai/codebase-composition/node';
13
+ /**
14
+ * Print coverage report in human-readable format
15
+ */
16
+ function printCoverageReport(metrics, rootDir, options) {
17
+ const { verbose } = options;
18
+ console.log(chalk.bold('\n' + '─'.repeat(70)));
19
+ console.log(chalk.bold('📈 TELEMETRY COVERAGE REPORT'));
20
+ console.log(chalk.bold('─'.repeat(70)));
21
+ // Canvas files
22
+ console.log(chalk.bold(`\n📋 Canvas Files Analyzed: ${metrics.canvasFiles.length}`));
23
+ if (verbose) {
24
+ metrics.canvasFiles.forEach(f => console.log(chalk.dim(` - ${f}`)));
25
+ }
26
+ // Validation errors (show first)
27
+ if (metrics.validationErrors && metrics.validationErrors.length > 0) {
28
+ console.log(chalk.red(`\n⚠️ Validation Errors (${metrics.validationErrors.length}):`));
29
+ metrics.validationErrors.forEach(err => {
30
+ console.log(chalk.yellow(`\n Canvas: ${err.canvasPath}`));
31
+ console.log(chalk.dim(` Node ID: ${err.nodeId}`));
32
+ if (err.status) {
33
+ console.log(chalk.dim(` Status: ${err.status}`));
34
+ }
35
+ console.log(chalk.red(` Error: ${err.error}`));
36
+ });
37
+ }
38
+ // Node status breakdown
39
+ if (metrics.nodeStatus) {
40
+ const ns = metrics.nodeStatus;
41
+ console.log(chalk.bold('\n📊 Node Status:'));
42
+ if (ns.missingStatusCount > 0) {
43
+ console.log(chalk.red(` ⚠️ Missing Status: ${ns.missingStatusCount} nodes (must add pv.status field)`));
44
+ }
45
+ console.log(chalk.dim(` Draft: ${ns.draftCount} nodes (design phase)`));
46
+ console.log(chalk.yellow(` Approved: ${ns.approvedCount} nodes, ${ns.approvedWithFilesCount} with files specified (awaiting implementation)`));
47
+ console.log(chalk.green(` Implemented: ${ns.implementedCount} nodes, ${ns.implementedWithFilesCount} with files specified`));
48
+ }
49
+ // File summary
50
+ console.log(chalk.bold('\n🎯 File Coverage Summary:'));
51
+ console.log(` Implementation files checked: ${metrics.totalImplementationFiles}`);
52
+ console.log(` Files with instrumentation: ${chalk.green(metrics.filesWithInstrumentation.toString())}`);
53
+ console.log(` Files without instrumentation: ${chalk.dim((metrics.totalImplementationFiles - metrics.filesWithInstrumentation).toString())}`);
54
+ const coverageColor = metrics.coveragePercentage >= 80
55
+ ? chalk.green
56
+ : metrics.coveragePercentage >= 50
57
+ ? chalk.yellow
58
+ : chalk.red;
59
+ console.log(` Coverage: ${coverageColor(metrics.coveragePercentage.toFixed(1) + '%')}`);
60
+ // Event summary
61
+ console.log(chalk.bold('\n📊 Event Summary:'));
62
+ console.log(` Total expected events: ${metrics.totalExpectedEvents}`);
63
+ console.log(` Events found: ${chalk.green(metrics.totalFoundEvents.toString())}`);
64
+ console.log(` Events missing: ${chalk.red((metrics.totalExpectedEvents - metrics.totalFoundEvents).toString())}`);
65
+ // Package breakdown
66
+ if (metrics.packageCoverage && metrics.packageCoverage.length > 0) {
67
+ console.log(chalk.bold('\n📦 Package Breakdown:'));
68
+ metrics.packageCoverage.forEach(pkg => {
69
+ const pkgColor = pkg.coveragePercentage >= 80
70
+ ? chalk.green
71
+ : pkg.coveragePercentage >= 50
72
+ ? chalk.yellow
73
+ : chalk.red;
74
+ const extSummary = Object.entries(pkg.filesByExtension)
75
+ .map(([ext, count]) => `${ext}: ${count}`)
76
+ .join(', ');
77
+ console.log(chalk.cyan(`\n ${pkg.packageName}:`));
78
+ console.log(chalk.dim(` Path: ${pkg.packagePath || '(root)'}`));
79
+ console.log(` Files: ${pkg.totalFiles}${extSummary ? ` (${extSummary})` : ''}`);
80
+ console.log(` Instrumented: ${pkg.filesWithInstrumentation}`);
81
+ console.log(` Coverage: ${pkgColor(pkg.coveragePercentage.toFixed(1) + '%')}`);
82
+ });
83
+ }
84
+ // Files WITHOUT instrumentation
85
+ const uninstrumented = metrics.fileCoverage.filter(f => !f.hasInstrumentation);
86
+ if (uninstrumented.length > 0) {
87
+ console.log(chalk.red(`\n❌ Files Missing Instrumentation (${uninstrumented.length}):`));
88
+ const display = verbose ? uninstrumented : uninstrumented.slice(0, 5);
89
+ display.forEach(file => {
90
+ console.log(chalk.yellow(`\n File: ${file.filePath}`));
91
+ console.log(chalk.dim(` Missing events: ${file.missingEvents.join(', ')}`));
92
+ });
93
+ if (!verbose && uninstrumented.length > 5) {
94
+ console.log(chalk.dim(`\n ... and ${uninstrumented.length - 5} more (use --verbose to see all)`));
95
+ }
96
+ }
97
+ // Files WITH instrumentation
98
+ const instrumented = metrics.fileCoverage.filter(f => f.hasInstrumentation);
99
+ if (instrumented.length > 0) {
100
+ console.log(chalk.green(`\n✅ Files With Instrumentation (${instrumented.length}):`));
101
+ const display = verbose ? instrumented : instrumented.slice(0, 5);
102
+ display.forEach(file => {
103
+ const eventCount = file.instrumentationCount;
104
+ const totalEvents = file.expectedEvents.length;
105
+ const percentage = ((eventCount / totalEvents) * 100).toFixed(0);
106
+ console.log(chalk.dim(` - ${file.filePath}: ${eventCount}/${totalEvents} events (${percentage}%)`));
107
+ if (verbose && file.foundEvents.length > 0) {
108
+ console.log(chalk.dim(` Found: ${file.foundEvents.join(', ')}`));
109
+ }
110
+ if (verbose && file.missingEvents.length > 0) {
111
+ console.log(chalk.yellow(` Missing: ${file.missingEvents.join(', ')}`));
112
+ }
113
+ });
114
+ if (!verbose && instrumented.length > 5) {
115
+ console.log(chalk.dim(` ... and ${instrumented.length - 5} more (use --verbose to see all)`));
116
+ }
117
+ }
118
+ // Heat map summary (top 5 most instrumented files)
119
+ if (instrumented.length > 0) {
120
+ console.log(chalk.bold('\n🔥 Top Instrumented Files (Heat Map):'));
121
+ const sorted = [...instrumented].sort((a, b) => b.instrumentationCount - a.instrumentationCount);
122
+ const top5 = sorted.slice(0, 5);
123
+ top5.forEach((file, idx) => {
124
+ const bar = '█'.repeat(Math.ceil(file.instrumentationCount / 2));
125
+ console.log(chalk.cyan(` ${idx + 1}. ${file.filePath}: ${bar} ${file.instrumentationCount} events`));
126
+ });
127
+ }
128
+ // Recommendations
129
+ console.log(chalk.bold('\n💡 Recommendations:'));
130
+ if (metrics.validationErrors && metrics.validationErrors.length > 0) {
131
+ const missingStatusErrors = metrics.validationErrors.filter(e => !e.status);
132
+ const otherErrors = metrics.validationErrors.filter(e => e.status);
133
+ if (missingStatusErrors.length > 0) {
134
+ console.log(chalk.red(` 🔴 ${missingStatusErrors.length} node(s) missing pv.status field - add status to track implementation lifecycle`));
135
+ console.log(chalk.dim('\n 📚 About the status field:'));
136
+ console.log(chalk.dim(' • "draft" - Design/proposal phase, not counted in coverage'));
137
+ console.log(chalk.dim(' • "approved" - Design finalized, ready to implement (requires pv.otel.files)'));
138
+ console.log(chalk.dim(' • "implemented" - Code exists with instrumentation (requires pv.otel.files)'));
139
+ }
140
+ if (otherErrors.length > 0) {
141
+ console.log(chalk.red(` 🔴 ${otherErrors.length} approved/implemented node(s) missing pv.otel.files`));
142
+ }
143
+ }
144
+ else if (metrics.totalImplementationFiles === 0) {
145
+ console.log(chalk.red(' 🔴 No implemented nodes with pv.otel.files - mark nodes as status="implemented" and add files'));
146
+ }
147
+ else if (metrics.coveragePercentage === 0) {
148
+ console.log(chalk.red(' 🔴 No instrumentation found - add OTEL events to files referenced in canvas'));
149
+ }
150
+ else if (metrics.coveragePercentage < 30) {
151
+ console.log(chalk.red(' 🔴 Low coverage - add OTEL instrumentation to missing files'));
152
+ }
153
+ else if (metrics.coveragePercentage < 60) {
154
+ console.log(chalk.yellow(' 🟡 Moderate coverage - continue instrumenting remaining files'));
155
+ }
156
+ else if (metrics.coveragePercentage < 100) {
157
+ console.log(chalk.green(' 🟢 Good coverage - finish instrumenting remaining files'));
158
+ }
159
+ else {
160
+ console.log(chalk.green(' 🟢 Excellent coverage - all files have instrumentation!'));
161
+ }
162
+ // Next steps
163
+ console.log(chalk.bold('\n📝 Next Steps:'));
164
+ if (metrics.validationErrors && metrics.validationErrors.length > 0) {
165
+ const missingStatusErrors = metrics.validationErrors.filter(e => !e.status);
166
+ if (missingStatusErrors.length > 0) {
167
+ console.log(chalk.dim(` 1. Add pv.status to the ${missingStatusErrors.length} node(s) missing it`));
168
+ console.log(chalk.dim(` 2. Choose based on implementation state:`));
169
+ console.log(chalk.dim(` - "draft" if designing/proposing the event`));
170
+ console.log(chalk.dim(` - "approved" if ready to implement`));
171
+ console.log(chalk.dim(` - "implemented" if code already exists`));
172
+ console.log(chalk.dim(` 3. For approved/implemented, add pv.otel.files`));
173
+ console.log(chalk.dim(` 4. Example: { "pv": { "status": "implemented", "event": {...}, "otel": { "files": ["src/file.ts"] } } }`));
174
+ console.log(chalk.dim(` 5. Re-run: principal-ai coverage`));
175
+ }
176
+ else {
177
+ console.log(chalk.dim(` 1. Fix the ${metrics.validationErrors.length} validation error(s) above`));
178
+ console.log(chalk.dim(` 2. Add pv.otel.files to approved/implemented nodes`));
179
+ console.log(chalk.dim(` 3. Example: { "pv": { "status": "implemented", "otel": { "files": ["src/file.ts"] } } }`));
180
+ console.log(chalk.dim(` 4. Re-run: principal-ai coverage`));
181
+ }
182
+ }
183
+ else if (metrics.totalImplementationFiles === 0) {
184
+ console.log(chalk.dim(` 1. Mark canvas nodes with status: "draft", "approved", or "implemented"`));
185
+ console.log(chalk.dim(` 2. Add pv.otel.files to approved/implemented nodes`));
186
+ console.log(chalk.dim(` 3. Example: { "pv": { "status": "implemented", "otel": { "files": ["src/app/api/route.ts"] } } }`));
187
+ console.log(chalk.dim(` 4. Re-run: principal-ai coverage`));
188
+ }
189
+ else if (uninstrumented.length > 0) {
190
+ console.log(chalk.dim(` 1. Review the ${uninstrumented.length} uninstrumented file(s) above`));
191
+ console.log(chalk.dim(` 2. Add the missing OpenTelemetry events to each file`));
192
+ console.log(chalk.dim(` 3. Use span.addEvent('event.name', { ... }) to emit events`));
193
+ console.log(chalk.dim(` 4. Re-run: principal-ai coverage`));
194
+ }
195
+ else {
196
+ console.log(chalk.green(` ✅ All files have instrumentation!`));
197
+ }
198
+ console.log(chalk.bold('\n' + '─'.repeat(70) + '\n'));
199
+ }
200
+ export function createCoverageCommand() {
201
+ const command = new Command('coverage');
202
+ command
203
+ .description('Measure telemetry coverage for implementation files')
204
+ .option('-d, --dir <path>', 'Project directory (defaults to current directory)')
205
+ .option('--json', 'Output results as JSON')
206
+ .option('-t, --threshold <percentage>', 'Minimum coverage percentage (exit with error if below)')
207
+ .option('-v, --verbose', 'Show all files in output')
208
+ .action(async (options) => {
209
+ try {
210
+ const rootDir = resolve(options.dir || process.cwd());
211
+ // Build FileTree and create fileReader
212
+ const service = new FilesystemService(new NodeFileSystemAdapter());
213
+ const fileTree = await service.buildFileSystemTreeFromPath(rootDir);
214
+ const fileReader = async (path) => readFile(resolve(rootDir, path), 'utf-8');
215
+ // Analyze coverage
216
+ const metrics = await analyzeCoverage(fileTree, fileReader);
217
+ // Output results
218
+ if (options.json) {
219
+ console.log(JSON.stringify(metrics, null, 2));
220
+ }
221
+ else {
222
+ printCoverageReport(metrics, rootDir, options);
223
+ }
224
+ // Check for validation errors
225
+ if (metrics.validationErrors && metrics.validationErrors.length > 0) {
226
+ if (!options.json) {
227
+ console.error(chalk.red(`\n✗ Found ${metrics.validationErrors.length} validation error(s). Fix these before checking coverage.`));
228
+ }
229
+ process.exit(1);
230
+ }
231
+ // Check threshold
232
+ if (options.threshold) {
233
+ const threshold = parseFloat(options.threshold);
234
+ if (isNaN(threshold) || threshold < 0 || threshold > 100) {
235
+ console.error(chalk.red('Error: Threshold must be a number between 0 and 100'));
236
+ process.exit(1);
237
+ }
238
+ if (metrics.coveragePercentage < threshold) {
239
+ if (!options.json) {
240
+ console.error(chalk.red(`\n✗ Coverage ${metrics.coveragePercentage.toFixed(1)}% is below threshold ${threshold}%`));
241
+ }
242
+ process.exit(1);
243
+ }
244
+ }
245
+ }
246
+ catch (error) {
247
+ console.error(chalk.red('Error:'), error.message);
248
+ process.exit(1);
249
+ }
250
+ });
251
+ return command;
252
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Create command - Create a new canvas file in the .principal-views folder
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function createCreateCommand(): Command;
6
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,mBAAmB,IAAI,OAAO,CAkD7C"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Create command - Create a new canvas file in the .principal-views folder
3
+ */
4
+ import { Command } from 'commander';
5
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import chalk from 'chalk';
8
+ const TEMPLATE_CANVAS = {
9
+ nodes: [],
10
+ edges: [],
11
+ };
12
+ export function createCreateCommand() {
13
+ const command = new Command('create');
14
+ command
15
+ .description('Create a new canvas file in the .principal-views folder')
16
+ .requiredOption('-n, --name <name>', 'Name for the canvas file (e.g., "cache-sync-architecture")')
17
+ .option('-f, --force', 'Overwrite existing file')
18
+ .action(async (options) => {
19
+ try {
20
+ const vgcDir = join(process.cwd(), '.principal-views');
21
+ const canvasFile = join(vgcDir, `${options.name}.canvas`);
22
+ // Check if .principal-views directory exists
23
+ if (!existsSync(vgcDir)) {
24
+ mkdirSync(vgcDir, { recursive: true });
25
+ console.log(chalk.green(`Created directory: .principal-views/`));
26
+ }
27
+ // Check if canvas file already exists
28
+ if (existsSync(canvasFile) && !options.force) {
29
+ console.error(chalk.red(`Error: Canvas file already exists: .principal-views/${options.name}.canvas`));
30
+ console.log(chalk.yellow(`Use ${chalk.cyan('--force')} to overwrite`));
31
+ process.exit(1);
32
+ }
33
+ // Create the canvas file
34
+ writeFileSync(canvasFile, JSON.stringify(TEMPLATE_CANVAS, null, 2));
35
+ console.log(chalk.green(`✓ Created canvas file: .principal-views/${options.name}.canvas`));
36
+ // Show next steps
37
+ console.log('');
38
+ console.log(chalk.bold('Next steps:'));
39
+ console.log(` 1. Open ${chalk.cyan(`.principal-views/${options.name}.canvas`)} in your editor`);
40
+ console.log(` 2. Add nodes and edges to define your architecture`);
41
+ console.log(` 3. Run ${chalk.cyan('npx @principal-ai/principal-studio-cli validate')} to check your configuration`);
42
+ console.log(` 4. Run ${chalk.cyan('npx @principal-ai/principal-studio-cli doctor')} to verify source mappings`);
43
+ }
44
+ catch (error) {
45
+ console.error(chalk.red('Error:'), error.message);
46
+ process.exit(1);
47
+ }
48
+ });
49
+ return command;
50
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createEventsCommand(): Command;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/events/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,wBAAgB,mBAAmB,IAAI,OAAO,CAQ7C"}
@@ -0,0 +1,9 @@
1
+ import { Command } from 'commander';
2
+ import { createValidateCommand } from './validate.js';
3
+ export function createEventsCommand() {
4
+ const command = new Command('events');
5
+ command
6
+ .description('Manage and validate event namespace canvases')
7
+ .addCommand(createValidateCommand());
8
+ return command;
9
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createValidateCommand(): Command;
3
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/commands/events/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,wBAAgB,qBAAqB,IAAI,OAAO,CAiI/C"}
@@ -0,0 +1,124 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { resolve } from 'node:path';
4
+ import { readFileSync } from 'node:fs';
5
+ import { EventsCanvasValidator, CanvasDiscovery, } from '@principal-ai/subsystems-core/node';
6
+ import { FilesystemService, NodeFileSystemAdapter } from '@principal-ai/codebase-composition/node';
7
+ import { NodeFileSystemAdapter as RepoNodeFileSystemAdapter } from '@principal-ai/repository-abstraction/node';
8
+ export function createValidateCommand() {
9
+ const command = new Command('validate');
10
+ command
11
+ .description('Validate events.canvas namespace structure and event organization')
12
+ .option('--json', 'Output as JSON')
13
+ .option('-d, --dir <path>', 'Project directory (default: cwd)')
14
+ .action(async (options) => {
15
+ try {
16
+ const baseDir = options.dir || process.cwd();
17
+ // Discover events canvas
18
+ const service = new FilesystemService(new NodeFileSystemAdapter());
19
+ const fileTree = await service.buildFileSystemTreeFromPath(baseDir);
20
+ const discovery = new CanvasDiscovery();
21
+ const discoveryResult = await discovery.discover(fileTree, {
22
+ fileReader: async (path) => readFileSync(resolve(baseDir, path), 'utf-8'),
23
+ includeContent: true,
24
+ });
25
+ // Find events canvas
26
+ const eventsCanvas = discoveryResult.canvases.find(c => c.type === 'events');
27
+ let eventsCanvasContent;
28
+ let eventsCanvasPath;
29
+ if (eventsCanvas) {
30
+ eventsCanvasPath = eventsCanvas.path;
31
+ // Check if content is already loaded
32
+ const canvasWithContent = eventsCanvas;
33
+ if (canvasWithContent.content) {
34
+ eventsCanvasContent = canvasWithContent.content;
35
+ }
36
+ else {
37
+ // Load content if not already loaded
38
+ try {
39
+ const fullPath = resolve(baseDir, eventsCanvas.path);
40
+ const content = readFileSync(fullPath, 'utf-8');
41
+ eventsCanvasContent = JSON.parse(content);
42
+ }
43
+ catch {
44
+ // Will be handled by validator
45
+ }
46
+ }
47
+ }
48
+ // Validate
49
+ const validator = new EventsCanvasValidator(new RepoNodeFileSystemAdapter());
50
+ const result = await validator.validate({
51
+ eventsCanvas: eventsCanvasContent,
52
+ eventsCanvasPath,
53
+ basePath: baseDir,
54
+ });
55
+ // Output results
56
+ if (options.json) {
57
+ console.log(JSON.stringify({
58
+ valid: result.valid,
59
+ violations: result.violations,
60
+ metrics: result.metrics,
61
+ }, null, 2));
62
+ }
63
+ else {
64
+ console.log(chalk.bold('\nEvents Canvas Validation\n'));
65
+ console.log('━'.repeat(60));
66
+ // Metrics summary
67
+ console.log(chalk.bold('\nMetrics:'));
68
+ console.log(chalk.gray(' • Total namespaces:'), result.metrics.totalNamespaces);
69
+ console.log(chalk.gray(' • Documented:'), result.metrics.documentedNamespaces.length);
70
+ console.log(chalk.gray(' • Total events:'), result.metrics.totalEvents);
71
+ console.log(chalk.gray(' • Registered:'), result.metrics.registeredEvents.length);
72
+ if (result.metrics.missingNamespaces.length > 0) {
73
+ console.log(chalk.red(' • Missing namespaces:'), result.metrics.missingNamespaces.join(', '));
74
+ }
75
+ if (result.metrics.unregisteredEvents.length > 0) {
76
+ console.log(chalk.red(' • Unregistered events:'), result.metrics.unregisteredEvents.length);
77
+ }
78
+ // Violations
79
+ if (result.violations.length > 0) {
80
+ console.log(chalk.bold('\nIssues:'));
81
+ console.log('━'.repeat(60));
82
+ for (const violation of result.violations) {
83
+ const label = violation.severity === 'error'
84
+ ? chalk.red('error')
85
+ : chalk.yellow('warning');
86
+ console.log(`\n${label}: ${violation.message}`);
87
+ if (violation.path) {
88
+ console.log(chalk.gray(` Location: ${violation.path}`));
89
+ }
90
+ if (violation.impact) {
91
+ console.log(chalk.gray(` Impact: ${violation.impact}`));
92
+ }
93
+ if (violation.suggestion) {
94
+ console.log(chalk.cyan(` Suggestion: ${violation.suggestion}`));
95
+ }
96
+ }
97
+ }
98
+ // Summary
99
+ const errors = result.violations.filter((v) => v.severity === 'error');
100
+ const warnings = result.violations.filter((v) => v.severity === 'warn');
101
+ console.log(chalk.bold('\nSummary:'));
102
+ if (result.valid) {
103
+ console.log(chalk.green(' ✓ Events canvas is valid'));
104
+ }
105
+ else {
106
+ console.log(chalk.red(` ✗ ${errors.length} error(s)`));
107
+ }
108
+ if (warnings.length > 0) {
109
+ console.log(chalk.yellow(` ⚠ ${warnings.length} warning(s)`));
110
+ }
111
+ console.log();
112
+ }
113
+ // Exit with error code if validation failed
114
+ if (!result.valid) {
115
+ process.exit(1);
116
+ }
117
+ }
118
+ catch (error) {
119
+ console.error(chalk.red('Error:'), error.message);
120
+ process.exit(1);
121
+ }
122
+ });
123
+ return command;
124
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formats command - Display documentation about file formats
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function createFormatsCommand(): Command;
6
+ //# sourceMappingURL=formats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../../src/commands/formats.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAglCpC,wBAAgB,oBAAoB,IAAI,OAAO,CA6B9C"}