@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,363 @@
1
+ /**
2
+ * Migrate Canvas Node Types
3
+ *
4
+ * Transforms canvas files from the legacy format (type: "text" + pv.nodeType)
5
+ * to the new semantic format (type: "otel-event", "otel-scope", etc.)
6
+ */
7
+ import { Command } from 'commander';
8
+ import chalk from 'chalk';
9
+ import { resolve, relative } from 'node:path';
10
+ import { readFileSync, writeFileSync, existsSync, copyFileSync, readdirSync, statSync } from 'node:fs';
11
+ /**
12
+ * Strip markdown formatting from a string
13
+ * Removes: **bold**, *italic*, __underline__, # headers, `code`, [links](url)
14
+ */
15
+ function stripMarkdown(text) {
16
+ return text
17
+ .replace(/\*\*/g, '') // Remove bold **
18
+ .replace(/\*/g, '') // Remove italic *
19
+ .replace(/__/g, '') // Remove underline __
20
+ .replace(/_/g, ' ') // Replace _ with space
21
+ .replace(/^#+\s*/gm, '') // Remove headers
22
+ .replace(/`/g, '') // Remove code backticks
23
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Replace [text](url) with text
24
+ .trim();
25
+ }
26
+ // Node type mapping from pv.nodeType to new type
27
+ const NODE_TYPE_MAP = {
28
+ 'event': 'otel-event',
29
+ 'span': 'otel-span-convention',
30
+ 'span-convention': 'otel-span-convention',
31
+ 'scope': 'otel-scope',
32
+ 'resource': 'otel-resource',
33
+ 'boundary': 'otel-boundary',
34
+ };
35
+ /**
36
+ * Transform a legacy node to the new format
37
+ */
38
+ function transformNode(node) {
39
+ // Only transform text nodes with pv.nodeType
40
+ if (node.type !== 'text' || !node.pv?.nodeType) {
41
+ return { transformed: node, changed: false };
42
+ }
43
+ const nodeType = node.pv.nodeType;
44
+ const newType = NODE_TYPE_MAP[nodeType];
45
+ if (!newType) {
46
+ // Unknown nodeType, skip
47
+ return { transformed: node, changed: false };
48
+ }
49
+ const pv = node.pv;
50
+ // Build the new node
51
+ const newNode = {
52
+ id: node.id,
53
+ type: newType,
54
+ x: node.x,
55
+ y: node.y,
56
+ width: node.width,
57
+ height: node.height,
58
+ color: node.color,
59
+ };
60
+ // Add label from pv.name (required for all otel-* nodes)
61
+ // Strip markdown formatting from label
62
+ const rawLabel = pv.name || node.text?.split('\n')[0]?.replace(/^#+ /, '').substring(0, 50) || 'Unnamed';
63
+ const label = stripMarkdown(rawLabel);
64
+ newNode.label = label;
65
+ // Transfer visual properties
66
+ if (pv.icon)
67
+ newNode.icon = pv.icon;
68
+ if (pv.fill)
69
+ newNode.fill = pv.fill;
70
+ if (pv.stroke)
71
+ newNode.stroke = pv.stroke;
72
+ if (pv.shape)
73
+ newNode.shape = pv.shape;
74
+ // Handle type-specific fields
75
+ switch (newType) {
76
+ case 'otel-event': {
77
+ // Transfer event schema
78
+ if (pv.event) {
79
+ newNode.event = pv.event;
80
+ }
81
+ if (pv.eventRef) {
82
+ newNode.eventRef = pv.eventRef;
83
+ }
84
+ if (pv.dataSchema) {
85
+ newNode.dataSchema = pv.dataSchema;
86
+ }
87
+ // Build otel metadata
88
+ const otel = {};
89
+ if (pv.status)
90
+ otel.status = pv.status;
91
+ if (pv.otel?.scope)
92
+ otel.scope = pv.otel.scope;
93
+ if (pv.otel?.files)
94
+ otel.files = pv.otel.files;
95
+ if (pv.otel?.origin)
96
+ otel.origin = pv.otel.origin;
97
+ if (pv.otel?.references)
98
+ otel.references = pv.otel.references;
99
+ if (Object.keys(otel).length > 0) {
100
+ newNode.otel = otel;
101
+ }
102
+ break;
103
+ }
104
+ case 'otel-span-convention': {
105
+ // Description for span nodes
106
+ if (pv.description) {
107
+ newNode.description = pv.description;
108
+ }
109
+ // Build otel metadata (spanPattern required)
110
+ const otel = {};
111
+ if (pv.status)
112
+ otel.status = pv.status;
113
+ if (pv.otel?.spanPattern)
114
+ otel.spanPattern = pv.otel.spanPattern;
115
+ if (pv.otel?.spanKind)
116
+ otel.spanKind = pv.otel.spanKind;
117
+ if (pv.otel?.spanMatch)
118
+ otel.spanMatch = pv.otel.spanMatch;
119
+ if (pv.otel?.scope)
120
+ otel.scope = pv.otel.scope;
121
+ if (pv.otel?.files)
122
+ otel.files = pv.otel.files;
123
+ if (pv.otel?.origin)
124
+ otel.origin = pv.otel.origin;
125
+ if (pv.otel?.references)
126
+ otel.references = pv.otel.references;
127
+ newNode.otel = otel;
128
+ break;
129
+ }
130
+ case 'otel-scope': {
131
+ // Description for scope nodes
132
+ if (pv.description) {
133
+ newNode.description = pv.description;
134
+ }
135
+ // Build otel metadata (scope required)
136
+ const otel = {};
137
+ if (pv.status)
138
+ otel.status = pv.status;
139
+ if (pv.otel?.scope)
140
+ otel.scope = pv.otel.scope;
141
+ if (pv.otel?.files)
142
+ otel.files = pv.otel.files;
143
+ if (pv.otel?.origin)
144
+ otel.origin = pv.otel.origin;
145
+ if (pv.otel?.references)
146
+ otel.references = pv.otel.references;
147
+ newNode.otel = otel;
148
+ break;
149
+ }
150
+ case 'otel-resource': {
151
+ // Description for resource nodes
152
+ if (pv.description) {
153
+ newNode.description = pv.description;
154
+ }
155
+ // Build otel metadata (resourceMatch required)
156
+ const otel = {};
157
+ if (pv.status)
158
+ otel.status = pv.status;
159
+ if (pv.otel?.resourceMatch)
160
+ otel.resourceMatch = pv.otel.resourceMatch;
161
+ if (pv.otel?.scope)
162
+ otel.scope = pv.otel.scope;
163
+ if (pv.otel?.files)
164
+ otel.files = pv.otel.files;
165
+ if (pv.otel?.origin)
166
+ otel.origin = pv.otel.origin;
167
+ if (pv.otel?.references)
168
+ otel.references = pv.otel.references;
169
+ newNode.otel = otel;
170
+ break;
171
+ }
172
+ case 'otel-boundary': {
173
+ // Description for boundary nodes
174
+ if (pv.description) {
175
+ newNode.description = pv.description;
176
+ }
177
+ // Build otel metadata
178
+ const otel = {};
179
+ if (pv.status)
180
+ otel.status = pv.status;
181
+ if (pv.otel?.scope)
182
+ otel.scope = pv.otel.scope;
183
+ if (pv.otel?.files)
184
+ otel.files = pv.otel.files;
185
+ if (pv.otel?.origin)
186
+ otel.origin = pv.otel.origin;
187
+ if (pv.otel?.references)
188
+ otel.references = pv.otel.references;
189
+ if (Object.keys(otel).length > 0) {
190
+ newNode.otel = otel;
191
+ }
192
+ // Transfer boundary data
193
+ if (pv.boundary) {
194
+ newNode.boundary = pv.boundary;
195
+ }
196
+ break;
197
+ }
198
+ }
199
+ return { transformed: newNode, changed: true };
200
+ }
201
+ /**
202
+ * Migrate a single canvas file
203
+ */
204
+ function migrateCanvas(filePath, options) {
205
+ const result = {
206
+ path: filePath,
207
+ nodesTransformed: 0,
208
+ nodesSkipped: 0,
209
+ errors: [],
210
+ };
211
+ try {
212
+ // Read the canvas file
213
+ const content = readFileSync(filePath, 'utf-8');
214
+ const canvas = JSON.parse(content);
215
+ if (!canvas.nodes || canvas.nodes.length === 0) {
216
+ return result;
217
+ }
218
+ // Transform each node
219
+ const newNodes = [];
220
+ for (const node of canvas.nodes) {
221
+ const { transformed, changed } = transformNode(node);
222
+ newNodes.push(transformed);
223
+ if (changed) {
224
+ result.nodesTransformed++;
225
+ if (options.verbose) {
226
+ // If changed, we know it was a text node with pv.nodeType
227
+ const oldType = (node.type === 'text' && 'pv' in node && node.pv?.nodeType)
228
+ ? `text+${node.pv.nodeType}`
229
+ : node.type;
230
+ console.log(chalk.gray(` • Transformed node "${node.id}" from ${oldType} to ${transformed.type}`));
231
+ }
232
+ }
233
+ else {
234
+ result.nodesSkipped++;
235
+ }
236
+ }
237
+ // Only write if there were changes
238
+ if (result.nodesTransformed > 0) {
239
+ // Create backup if requested
240
+ if (options.backup && !options.dryRun) {
241
+ const backupPath = filePath + '.backup';
242
+ copyFileSync(filePath, backupPath);
243
+ result.backup = backupPath;
244
+ }
245
+ // Write the migrated canvas
246
+ if (!options.dryRun) {
247
+ const newCanvas = { ...canvas, nodes: newNodes };
248
+ // Preserve the original formatting (2-space indent)
249
+ writeFileSync(filePath, JSON.stringify(newCanvas, null, 2) + '\n');
250
+ }
251
+ }
252
+ }
253
+ catch (error) {
254
+ result.errors.push(error.message);
255
+ }
256
+ return result;
257
+ }
258
+ /**
259
+ * Find all canvas files in a directory
260
+ */
261
+ function findCanvasFiles(dir) {
262
+ const files = [];
263
+ function walkDir(currentPath) {
264
+ const entries = readdirSync(currentPath);
265
+ for (const entry of entries) {
266
+ // Skip node_modules and hidden directories
267
+ if (entry === 'node_modules' || entry.startsWith('.')) {
268
+ continue;
269
+ }
270
+ const fullPath = resolve(currentPath, entry);
271
+ const stat = statSync(fullPath);
272
+ if (stat.isDirectory()) {
273
+ walkDir(fullPath);
274
+ }
275
+ else if (entry.endsWith('.canvas') || entry.endsWith('.otel.canvas')) {
276
+ files.push(fullPath);
277
+ }
278
+ }
279
+ }
280
+ walkDir(dir);
281
+ return files;
282
+ }
283
+ export function createMigrateNodesCommand() {
284
+ const command = new Command('migrate-nodes');
285
+ command
286
+ .description('Migrate canvas files from legacy node format to new semantic types')
287
+ .argument('[path]', 'Path to canvas file or directory (default: .principal-views)')
288
+ .option('--dry-run', 'Show what would be changed without modifying files')
289
+ .option('--no-backup', 'Do not create backup files')
290
+ .option('-d, --dir <path>', 'Base directory (default: cwd)')
291
+ .option('-v, --verbose', 'Show detailed transformation info')
292
+ .action(async (path, options) => {
293
+ try {
294
+ const baseDir = options.dir || process.cwd();
295
+ const targetPath = path ? resolve(baseDir, path) : resolve(baseDir, '.principal-views');
296
+ if (!existsSync(targetPath)) {
297
+ console.error(chalk.red('Error:'), `Path not found: ${targetPath}`);
298
+ process.exit(1);
299
+ }
300
+ // Find canvas files
301
+ let canvasFiles;
302
+ const stat = statSync(targetPath);
303
+ if (stat.isDirectory()) {
304
+ canvasFiles = findCanvasFiles(targetPath);
305
+ console.log(chalk.bold(`\nMigrating canvas files in: ${relative(baseDir, targetPath)}\n`));
306
+ }
307
+ else {
308
+ canvasFiles = [targetPath];
309
+ console.log(chalk.bold(`\nMigrating canvas file: ${relative(baseDir, targetPath)}\n`));
310
+ }
311
+ if (canvasFiles.length === 0) {
312
+ console.log(chalk.yellow('No canvas files found.'));
313
+ return;
314
+ }
315
+ if (options.dryRun) {
316
+ console.log(chalk.cyan('Dry run mode - no files will be modified\n'));
317
+ }
318
+ // Migrate each file
319
+ let totalTransformed = 0;
320
+ let totalSkipped = 0;
321
+ let filesModified = 0;
322
+ for (const filePath of canvasFiles) {
323
+ const relativePath = relative(baseDir, filePath);
324
+ const result = migrateCanvas(filePath, { ...options, backup: options.backup !== false });
325
+ if (result.errors.length > 0) {
326
+ console.log(chalk.red('✗'), relativePath);
327
+ for (const error of result.errors) {
328
+ console.log(chalk.red(` Error: ${error}`));
329
+ }
330
+ continue;
331
+ }
332
+ if (result.nodesTransformed > 0) {
333
+ filesModified++;
334
+ console.log(chalk.green('✓'), relativePath);
335
+ console.log(chalk.gray(` ${result.nodesTransformed} node(s) transformed, ${result.nodesSkipped} skipped`));
336
+ if (result.backup) {
337
+ console.log(chalk.gray(` Backup: ${relative(baseDir, result.backup)}`));
338
+ }
339
+ }
340
+ else if (options.verbose) {
341
+ console.log(chalk.gray('○'), relativePath, chalk.gray('(no changes needed)'));
342
+ }
343
+ totalTransformed += result.nodesTransformed;
344
+ totalSkipped += result.nodesSkipped;
345
+ }
346
+ // Summary
347
+ console.log(chalk.bold('\nSummary:'));
348
+ console.log(chalk.gray(` • Files processed: ${canvasFiles.length}`));
349
+ console.log(chalk.gray(` • Files modified: ${filesModified}`));
350
+ console.log(chalk.gray(` • Nodes transformed: ${totalTransformed}`));
351
+ console.log(chalk.gray(` • Nodes skipped: ${totalSkipped}`));
352
+ if (options.dryRun && totalTransformed > 0) {
353
+ console.log(chalk.cyan('\nRun without --dry-run to apply changes.'));
354
+ }
355
+ console.log();
356
+ }
357
+ catch (error) {
358
+ console.error(chalk.red('Error:'), error.message);
359
+ process.exit(1);
360
+ }
361
+ });
362
+ return command;
363
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createMigrateScopesCommand(): Command;
3
+ //# sourceMappingURL=migrate-scopes-to-canvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-scopes-to-canvas.d.ts","sourceRoot":"","sources":["../../src/commands/migrate-scopes-to-canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,wBAAgB,0BAA0B,IAAI,OAAO,CAkJpD"}
@@ -0,0 +1,143 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { resolve, relative } from 'node:path';
4
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
5
+ import yaml from 'js-yaml';
6
+ export function createMigrateScopesCommand() {
7
+ const command = new Command('scopes-to-canvas');
8
+ command
9
+ .description('Migrate scopes from library.yaml to .scopes.canvas')
10
+ .option('--dry-run', 'Preview changes without writing files')
11
+ .option('--force', 'Overwrite existing scope nodes in canvas')
12
+ .option('-d, --dir <path>', 'Project directory (default: cwd)')
13
+ .option('-o, --output <path>', 'Output path for scopes canvas')
14
+ .action(async (options) => {
15
+ const baseDir = resolve(options.dir || process.cwd());
16
+ // 1. Load library.yaml
17
+ const libraryPath = resolve(baseDir, '.principal-views/library.yaml');
18
+ if (!existsSync(libraryPath)) {
19
+ console.error(chalk.red('✗ Error: library.yaml not found at .principal-views/library.yaml'));
20
+ process.exit(1);
21
+ }
22
+ const library = yaml.load(readFileSync(libraryPath, 'utf-8'));
23
+ if (!library.scopes || Object.keys(library.scopes).length === 0) {
24
+ console.log(chalk.green('✓ No scopes section found - nothing to migrate'));
25
+ return;
26
+ }
27
+ // 2. Find or create scopes canvas
28
+ const scopesCanvasPath = options.output || findScopesCanvasPath(baseDir) || resolve(baseDir, '.principal-views/architecture.scopes.canvas');
29
+ let scopesCanvas;
30
+ const existingScopes = new Set();
31
+ if (existsSync(scopesCanvasPath)) {
32
+ scopesCanvas = JSON.parse(readFileSync(scopesCanvasPath, 'utf-8'));
33
+ // Track existing scopes
34
+ for (const node of scopesCanvas.nodes || []) {
35
+ if (node.type === 'otel-scope' && node.otel?.scope) {
36
+ existingScopes.add(node.otel.scope);
37
+ }
38
+ }
39
+ }
40
+ else {
41
+ scopesCanvas = {
42
+ name: 'Instrumentation Scopes',
43
+ description: 'Defines instrumentation scopes for the application',
44
+ nodes: [],
45
+ edges: [],
46
+ };
47
+ }
48
+ // 3. Convert scopes to nodes
49
+ const scopeEntries = Object.entries(library.scopes);
50
+ const added = [];
51
+ const skipped = [];
52
+ const overwritten = [];
53
+ // Calculate starting X position (append to right of existing nodes)
54
+ let maxX = 0;
55
+ for (const node of scopesCanvas.nodes || []) {
56
+ const nodeMaxX = node.x + (node.width || 200);
57
+ maxX = Math.max(maxX, nodeMaxX);
58
+ }
59
+ let x = maxX > 0 ? maxX + 50 : 0;
60
+ for (const [scopeName, scopeDef] of scopeEntries) {
61
+ const existsInCanvas = existingScopes.has(scopeName);
62
+ if (existsInCanvas && !options.force) {
63
+ skipped.push(scopeName);
64
+ continue;
65
+ }
66
+ if (existsInCanvas && options.force) {
67
+ // Remove existing node
68
+ scopesCanvas.nodes =
69
+ scopesCanvas.nodes?.filter(n => !(n.type === 'otel-scope' && n.otel?.scope === scopeName)) || [];
70
+ overwritten.push(scopeName);
71
+ }
72
+ else {
73
+ added.push(scopeName);
74
+ }
75
+ // Create new node
76
+ const newNode = {
77
+ id: `${scopeName}-scope`,
78
+ type: 'otel-scope',
79
+ label: scopeDef.description || scopeName,
80
+ color: scopeDef.color,
81
+ description: scopeDef.description,
82
+ x,
83
+ y: 0,
84
+ width: 200,
85
+ height: 80,
86
+ otel: {
87
+ scope: scopeName,
88
+ },
89
+ };
90
+ // Add icon if present
91
+ if (scopeDef.icon) {
92
+ newNode.icon = scopeDef.icon;
93
+ }
94
+ scopesCanvas.nodes = scopesCanvas.nodes || [];
95
+ scopesCanvas.nodes.push(newNode);
96
+ x += 250; // Space nodes horizontally
97
+ }
98
+ // 4. Report results
99
+ console.log(chalk.bold('\nScope Migration Summary\n'));
100
+ console.log('━'.repeat(60));
101
+ if (added.length > 0) {
102
+ console.log(chalk.green(`✓ Added ${added.length} scope(s):`));
103
+ added.forEach(s => console.log(chalk.gray(` • ${s}`)));
104
+ }
105
+ if (skipped.length > 0) {
106
+ console.log(chalk.yellow(`\n⊘ Skipped ${skipped.length} scope(s) (already in canvas):`));
107
+ skipped.forEach(s => console.log(chalk.gray(` • ${s}`)));
108
+ console.log(chalk.gray(` Run with --force to overwrite existing nodes`));
109
+ }
110
+ if (overwritten.length > 0) {
111
+ console.log(chalk.cyan(`\n↻ Overwritten ${overwritten.length} scope(s):`));
112
+ overwritten.forEach(s => console.log(chalk.gray(` • ${s}`)));
113
+ }
114
+ // 5. Write canvas (unless dry run)
115
+ const relativePath = relative(baseDir, scopesCanvasPath);
116
+ if (!options.dryRun) {
117
+ writeFileSync(scopesCanvasPath, JSON.stringify(scopesCanvas, null, 2) + '\n');
118
+ console.log(chalk.green(`\n✓ Wrote ${relativePath}`));
119
+ }
120
+ else {
121
+ console.log(chalk.cyan(`\n[DRY RUN] Would write to: ${relativePath}`));
122
+ }
123
+ // 6. Next steps
124
+ console.log(chalk.bold('\nNext Steps:'));
125
+ console.log(chalk.gray(' 1. Review the generated .scopes.canvas file'));
126
+ console.log(chalk.gray(' 2. Remove the "scopes:" section from library.yaml'));
127
+ console.log(chalk.gray(' 3. Run "pv validate" to verify the migration'));
128
+ console.log();
129
+ });
130
+ return command;
131
+ }
132
+ /**
133
+ * Find existing .scopes.canvas file in project
134
+ */
135
+ function findScopesCanvasPath(baseDir) {
136
+ const candidates = ['.principal-views/architecture.scopes.canvas', '.principal-views/scopes.canvas'];
137
+ for (const candidate of candidates) {
138
+ const path = resolve(baseDir, candidate);
139
+ if (existsSync(path))
140
+ return path;
141
+ }
142
+ return null;
143
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Migration command - Display the migration guide for moving from legacy flat structure to storyboards
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function createMigrationCommand(): Command;
6
+ //# sourceMappingURL=migration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/commands/migration.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4OpC,wBAAgB,sBAAsB,IAAI,OAAO,CAWhD"}