@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.
- package/README.md +172 -0
- package/dist/commands/agent-session.d.ts +3 -0
- package/dist/commands/agent-session.d.ts.map +1 -0
- package/dist/commands/agent-session.js +67 -0
- package/dist/commands/agent-sessions.d.ts +11 -0
- package/dist/commands/agent-sessions.d.ts.map +1 -0
- package/dist/commands/agent-sessions.js +39 -0
- package/dist/commands/auxiliary/index.d.ts +3 -0
- package/dist/commands/auxiliary/index.d.ts.map +1 -0
- package/dist/commands/auxiliary/index.js +9 -0
- package/dist/commands/auxiliary/validate.d.ts +3 -0
- package/dist/commands/auxiliary/validate.d.ts.map +1 -0
- package/dist/commands/auxiliary/validate.js +123 -0
- package/dist/commands/collector/check.d.ts +6 -0
- package/dist/commands/collector/check.d.ts.map +1 -0
- package/dist/commands/collector/check.js +153 -0
- package/dist/commands/collector/diagnose.d.ts +6 -0
- package/dist/commands/collector/diagnose.d.ts.map +1 -0
- package/dist/commands/collector/diagnose.js +229 -0
- package/dist/commands/collector/index.d.ts +6 -0
- package/dist/commands/collector/index.d.ts.map +1 -0
- package/dist/commands/collector/index.js +16 -0
- package/dist/commands/collector/status.d.ts +6 -0
- package/dist/commands/collector/status.d.ts.map +1 -0
- package/dist/commands/collector/status.js +77 -0
- package/dist/commands/collector/utils.d.ts +92 -0
- package/dist/commands/collector/utils.d.ts.map +1 -0
- package/dist/commands/collector/utils.js +154 -0
- package/dist/commands/coverage.d.ts +9 -0
- package/dist/commands/coverage.d.ts.map +1 -0
- package/dist/commands/coverage.js +252 -0
- package/dist/commands/create.d.ts +6 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +50 -0
- package/dist/commands/events/index.d.ts +3 -0
- package/dist/commands/events/index.d.ts.map +1 -0
- package/dist/commands/events/index.js +9 -0
- package/dist/commands/events/validate.d.ts +3 -0
- package/dist/commands/events/validate.d.ts.map +1 -0
- package/dist/commands/events/validate.js +124 -0
- package/dist/commands/formats.d.ts +6 -0
- package/dist/commands/formats.d.ts.map +1 -0
- package/dist/commands/formats.js +1118 -0
- package/dist/commands/hooks.d.ts +9 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +295 -0
- package/dist/commands/inbox.d.ts +13 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +179 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +220 -0
- package/dist/commands/lint.d.ts +11 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +71 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +78 -0
- package/dist/commands/migrate-nodes.d.ts +9 -0
- package/dist/commands/migrate-nodes.d.ts.map +1 -0
- package/dist/commands/migrate-nodes.js +363 -0
- package/dist/commands/migrate-scopes-to-canvas.d.ts +3 -0
- package/dist/commands/migrate-scopes-to-canvas.d.ts.map +1 -0
- package/dist/commands/migrate-scopes-to-canvas.js +143 -0
- package/dist/commands/migration.d.ts +6 -0
- package/dist/commands/migration.d.ts.map +1 -0
- package/dist/commands/migration.js +247 -0
- package/dist/commands/open-studio.d.ts +10 -0
- package/dist/commands/open-studio.d.ts.map +1 -0
- package/dist/commands/open-studio.js +32 -0
- package/dist/commands/opencode/fetch.d.ts +3 -0
- package/dist/commands/opencode/fetch.d.ts.map +1 -0
- package/dist/commands/opencode/fetch.js +25 -0
- package/dist/commands/opencode/index.d.ts +3 -0
- package/dist/commands/opencode/index.d.ts.map +1 -0
- package/dist/commands/opencode/index.js +11 -0
- package/dist/commands/opencode/list-sessions.d.ts +3 -0
- package/dist/commands/opencode/list-sessions.d.ts.map +1 -0
- package/dist/commands/opencode/list-sessions.js +20 -0
- package/dist/commands/repo.d.ts +18 -0
- package/dist/commands/repo.d.ts.map +1 -0
- package/dist/commands/repo.js +233 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +367 -0
- package/dist/commands/scopes/index.d.ts +3 -0
- package/dist/commands/scopes/index.d.ts.map +1 -0
- package/dist/commands/scopes/index.js +9 -0
- package/dist/commands/scopes/validate.d.ts +3 -0
- package/dist/commands/scopes/validate.d.ts.map +1 -0
- package/dist/commands/scopes/validate.js +187 -0
- package/dist/commands/setup.d.ts +6 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +406 -0
- package/dist/commands/starred-collections.d.ts +12 -0
- package/dist/commands/starred-collections.d.ts.map +1 -0
- package/dist/commands/starred-collections.js +310 -0
- package/dist/commands/subsystem-model.d.ts +11 -0
- package/dist/commands/subsystem-model.d.ts.map +1 -0
- package/dist/commands/subsystem-model.js +249 -0
- package/dist/commands/topic.d.ts +15 -0
- package/dist/commands/topic.d.ts.map +1 -0
- package/dist/commands/topic.js +683 -0
- package/dist/commands/tour.d.ts +23 -0
- package/dist/commands/tour.d.ts.map +1 -0
- package/dist/commands/tour.js +783 -0
- package/dist/commands/trace/flow.d.ts +6 -0
- package/dist/commands/trace/flow.d.ts.map +1 -0
- package/dist/commands/trace/flow.js +158 -0
- package/dist/commands/trace/index.d.ts +6 -0
- package/dist/commands/trace/index.d.ts.map +1 -0
- package/dist/commands/trace/index.js +20 -0
- package/dist/commands/trace/inspect.d.ts +6 -0
- package/dist/commands/trace/inspect.d.ts.map +1 -0
- package/dist/commands/trace/inspect.js +187 -0
- package/dist/commands/trace/list.d.ts +6 -0
- package/dist/commands/trace/list.d.ts.map +1 -0
- package/dist/commands/trace/list.js +145 -0
- package/dist/commands/trace/registrations.d.ts +6 -0
- package/dist/commands/trace/registrations.d.ts.map +1 -0
- package/dist/commands/trace/registrations.js +81 -0
- package/dist/commands/trace/validate.d.ts +6 -0
- package/dist/commands/trace/validate.d.ts.map +1 -0
- package/dist/commands/trace/validate.js +276 -0
- package/dist/commands/trail.d.ts +36 -0
- package/dist/commands/trail.d.ts.map +1 -0
- package/dist/commands/trail.js +622 -0
- package/dist/commands/validate.d.ts +12 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +3334 -0
- package/dist/commands/workflow/implementation.d.ts +3 -0
- package/dist/commands/workflow/implementation.d.ts.map +1 -0
- package/dist/commands/workflow/implementation.js +51 -0
- package/dist/commands/workflow/index.d.ts +3 -0
- package/dist/commands/workflow/index.d.ts.map +1 -0
- package/dist/commands/workflow/index.js +19 -0
- package/dist/commands/workflow/inspect.d.ts +3 -0
- package/dist/commands/workflow/inspect.d.ts.map +1 -0
- package/dist/commands/workflow/inspect.js +123 -0
- package/dist/commands/workflow/list.d.ts +3 -0
- package/dist/commands/workflow/list.d.ts.map +1 -0
- package/dist/commands/workflow/list.js +95 -0
- package/dist/commands/workflow/render.d.ts +3 -0
- package/dist/commands/workflow/render.d.ts.map +1 -0
- package/dist/commands/workflow/render.js +88 -0
- package/dist/commands/workflow/test.d.ts +3 -0
- package/dist/commands/workflow/test.d.ts.map +1 -0
- package/dist/commands/workflow/test.js +141 -0
- package/dist/commands/workflow/utils.d.ts +49 -0
- package/dist/commands/workflow/utils.d.ts.map +1 -0
- package/dist/commands/workflow/utils.js +164 -0
- package/dist/commands/workflow/validate.d.ts +3 -0
- package/dist/commands/workflow/validate.d.ts.map +1 -0
- package/dist/commands/workflow/validate.js +344 -0
- package/dist/file-utils.d.ts +15 -0
- package/dist/file-utils.d.ts.map +1 -0
- package/dist/file-utils.js +29 -0
- package/dist/index.cjs +285718 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/lib/bridge-ipc.d.ts +29 -0
- package/dist/lib/bridge-ipc.d.ts.map +1 -0
- package/dist/lib/bridge-ipc.js +128 -0
- package/dist/lib/github-user.d.ts +17 -0
- package/dist/lib/github-user.d.ts.map +1 -0
- package/dist/lib/github-user.js +53 -0
- package/dist/lib/open-url.d.ts +11 -0
- package/dist/lib/open-url.d.ts.map +1 -0
- package/dist/lib/open-url.js +45 -0
- package/dist/lib/subsystem-model-store.d.ts +74 -0
- package/dist/lib/subsystem-model-store.d.ts.map +1 -0
- package/dist/lib/subsystem-model-store.js +292 -0
- package/dist/lib/tour-cache.d.ts +26 -0
- package/dist/lib/tour-cache.d.ts.map +1 -0
- package/dist/lib/tour-cache.js +46 -0
- package/dist/lib/trail-cache.d.ts +40 -0
- package/dist/lib/trail-cache.d.ts.map +1 -0
- package/dist/lib/trail-cache.js +97 -0
- package/dist/lib/viewer-ipc.d.ts +44 -0
- package/dist/lib/viewer-ipc.d.ts.map +1 -0
- package/dist/lib/viewer-ipc.js +68 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|