@principal-ai/principal-view-cli 0.3.3 → 0.3.4
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/dist/commands/coverage.d.ts +9 -0
- package/dist/commands/coverage.d.ts.map +1 -0
- package/dist/commands/coverage.js +158 -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/doctor.d.ts +10 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +274 -0
- package/dist/commands/formats.d.ts +6 -0
- package/dist/commands/formats.d.ts.map +1 -0
- package/dist/commands/formats.js +475 -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/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +271 -0
- package/dist/commands/lint.d.ts +6 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +506 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +80 -0
- package/dist/commands/narrative/index.d.ts +3 -0
- package/dist/commands/narrative/index.d.ts.map +1 -0
- package/dist/commands/narrative/index.js +17 -0
- package/dist/commands/narrative/inspect.d.ts +3 -0
- package/dist/commands/narrative/inspect.d.ts.map +1 -0
- package/dist/commands/narrative/inspect.js +109 -0
- package/dist/commands/narrative/list.d.ts +3 -0
- package/dist/commands/narrative/list.d.ts.map +1 -0
- package/dist/commands/narrative/list.js +101 -0
- package/dist/commands/narrative/render.d.ts +3 -0
- package/dist/commands/narrative/render.d.ts.map +1 -0
- package/dist/commands/narrative/render.js +99 -0
- package/dist/commands/narrative/test.d.ts +3 -0
- package/dist/commands/narrative/test.d.ts.map +1 -0
- package/dist/commands/narrative/test.js +150 -0
- package/dist/commands/narrative/utils.d.ts +49 -0
- package/dist/commands/narrative/utils.d.ts.map +1 -0
- package/dist/commands/narrative/utils.js +164 -0
- package/dist/commands/narrative/validate.d.ts +3 -0
- package/dist/commands/narrative/validate.d.ts.map +1 -0
- package/dist/commands/narrative/validate.js +149 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +336 -0
- package/dist/commands/validate-execution.d.ts +11 -0
- package/dist/commands/validate-execution.d.ts.map +1 -0
- package/dist/commands/validate-execution.js +223 -0
- package/dist/commands/validate.d.ts +6 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +1065 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/package.json +2 -2
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks command - Manage husky pre-commit hooks for Principal View
|
|
3
|
+
*
|
|
4
|
+
* This command installs/removes pre-commit hooks into a target project
|
|
5
|
+
* that will run `npx @principal-ai/principal-view-cli doctor` and `npx @principal-ai/principal-view-cli validate` before each commit.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from 'node:fs';
|
|
9
|
+
import { resolve, join } from 'node:path';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
const HUSKY_DIR = '.husky';
|
|
13
|
+
const PRE_COMMIT_HOOK = 'pre-commit';
|
|
14
|
+
const VV_HOOK_MARKER = '# Principal View checks';
|
|
15
|
+
/**
|
|
16
|
+
* Get the Principal View pre-commit hook content
|
|
17
|
+
*/
|
|
18
|
+
function getVVHookContent() {
|
|
19
|
+
return `${VV_HOOK_MARKER}
|
|
20
|
+
echo "Running Principal View doctor check..."
|
|
21
|
+
npx @principal-ai/principal-view-cli doctor --errors-only || {
|
|
22
|
+
echo "❌ Principal View doctor check failed (errors found)"
|
|
23
|
+
echo " Run 'npx @principal-ai/principal-view-cli doctor' to see details"
|
|
24
|
+
exit 1
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
echo "Running Principal View canvas validation..."
|
|
28
|
+
npx @principal-ai/principal-view-cli validate --quiet 2>/dev/null || {
|
|
29
|
+
if [ $? -ne 0 ]; then
|
|
30
|
+
echo "❌ Canvas validation failed"
|
|
31
|
+
echo " Run 'npx @principal-ai/principal-view-cli validate' to see details"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Find the repository root by looking for .git directory
|
|
39
|
+
*/
|
|
40
|
+
function findRepoRoot(startPath) {
|
|
41
|
+
let current = resolve(startPath);
|
|
42
|
+
const root = resolve('/');
|
|
43
|
+
while (current !== root) {
|
|
44
|
+
if (existsSync(join(current, '.git'))) {
|
|
45
|
+
return current;
|
|
46
|
+
}
|
|
47
|
+
current = resolve(current, '..');
|
|
48
|
+
}
|
|
49
|
+
throw new Error('Not a git repository (or any parent up to mount point)');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if husky is installed
|
|
53
|
+
*/
|
|
54
|
+
function isHuskyInstalled(repoPath) {
|
|
55
|
+
const huskyPath = join(repoPath, HUSKY_DIR);
|
|
56
|
+
return existsSync(huskyPath);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Initialize husky if not already installed
|
|
60
|
+
*/
|
|
61
|
+
function initializeHusky(repoPath) {
|
|
62
|
+
if (!isHuskyInstalled(repoPath)) {
|
|
63
|
+
console.log('📦 Installing husky...');
|
|
64
|
+
try {
|
|
65
|
+
// Check if package.json exists
|
|
66
|
+
const packageJsonPath = join(repoPath, 'package.json');
|
|
67
|
+
if (!existsSync(packageJsonPath)) {
|
|
68
|
+
throw new Error('No package.json found. Please run npm init first.');
|
|
69
|
+
}
|
|
70
|
+
// Install husky
|
|
71
|
+
execSync('npm install --save-dev husky', {
|
|
72
|
+
cwd: repoPath,
|
|
73
|
+
stdio: 'inherit',
|
|
74
|
+
});
|
|
75
|
+
// Initialize husky
|
|
76
|
+
execSync('npx husky init', {
|
|
77
|
+
cwd: repoPath,
|
|
78
|
+
stdio: 'inherit',
|
|
79
|
+
});
|
|
80
|
+
// Remove the default placeholder if it exists
|
|
81
|
+
const hookPath = join(repoPath, HUSKY_DIR, PRE_COMMIT_HOOK);
|
|
82
|
+
if (existsSync(hookPath)) {
|
|
83
|
+
const content = readFileSync(hookPath, 'utf8').trim();
|
|
84
|
+
if (content === 'npm test') {
|
|
85
|
+
// Remove the placeholder file - we'll create our own when --add is used
|
|
86
|
+
unlinkSync(hookPath);
|
|
87
|
+
console.log('ℹ️ Removed default husky placeholder hook');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
console.log('✅ Husky installed and initialized');
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
throw new Error(`Failed to initialize husky: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if pre-commit hook has PV validation
|
|
99
|
+
*/
|
|
100
|
+
function hasVVHook(repoPath) {
|
|
101
|
+
const hookPath = join(repoPath, HUSKY_DIR, PRE_COMMIT_HOOK);
|
|
102
|
+
if (!existsSync(hookPath)) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const content = readFileSync(hookPath, 'utf8');
|
|
106
|
+
return content.includes(VV_HOOK_MARKER);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Add PV validation to pre-commit hook
|
|
110
|
+
*/
|
|
111
|
+
function addVVHook(repoPath) {
|
|
112
|
+
const hookPath = join(repoPath, HUSKY_DIR, PRE_COMMIT_HOOK);
|
|
113
|
+
const vvContent = getVVHookContent();
|
|
114
|
+
if (existsSync(hookPath)) {
|
|
115
|
+
// Append to existing hook
|
|
116
|
+
const existingContent = readFileSync(hookPath, 'utf8');
|
|
117
|
+
// Check if already has PV hook
|
|
118
|
+
if (existingContent.includes(VV_HOOK_MARKER)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Remove default husky placeholder if it's the only content
|
|
122
|
+
const trimmedContent = existingContent.trim();
|
|
123
|
+
if (trimmedContent === 'npm test') {
|
|
124
|
+
// Replace the placeholder entirely
|
|
125
|
+
writeFileSync(hookPath, vvContent, 'utf8');
|
|
126
|
+
console.log('ℹ️ Replaced default husky placeholder with Principal View checks');
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Add PV hook at the end
|
|
130
|
+
const updatedContent = existingContent.trimEnd() + '\n\n' + vvContent;
|
|
131
|
+
writeFileSync(hookPath, updatedContent, 'utf8');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Create new hook file
|
|
136
|
+
writeFileSync(hookPath, vvContent, 'utf8');
|
|
137
|
+
// Make it executable
|
|
138
|
+
chmodSync(hookPath, 0o755);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Remove PV validation from pre-commit hook
|
|
143
|
+
*/
|
|
144
|
+
function removeVVHook(repoPath) {
|
|
145
|
+
const hookPath = join(repoPath, HUSKY_DIR, PRE_COMMIT_HOOK);
|
|
146
|
+
if (!existsSync(hookPath)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const content = readFileSync(hookPath, 'utf8');
|
|
150
|
+
if (!content.includes(VV_HOOK_MARKER)) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Split content by lines and find the PV section
|
|
154
|
+
const lines = content.split('\n');
|
|
155
|
+
const startIndex = lines.findIndex((line) => line.includes(VV_HOOK_MARKER));
|
|
156
|
+
if (startIndex === -1) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// Find the end of the PV section
|
|
160
|
+
let endIndex = lines.length - 1;
|
|
161
|
+
let inVVBlock = true;
|
|
162
|
+
let i = startIndex + 1;
|
|
163
|
+
while (i < lines.length && inVVBlock) {
|
|
164
|
+
const line = lines[i];
|
|
165
|
+
// Check if this line is part of the PV block
|
|
166
|
+
if (line &&
|
|
167
|
+
(line.includes('@principal-ai/principal-view-cli ') ||
|
|
168
|
+
line.includes('Principal View') ||
|
|
169
|
+
line.includes('echo "Running Visual') ||
|
|
170
|
+
(line.includes('exit 1') && i > startIndex && i < startIndex + 15) ||
|
|
171
|
+
(line === '}' && i > startIndex && i < startIndex + 15) ||
|
|
172
|
+
(line.trim() === '' && i === startIndex + 1))) {
|
|
173
|
+
endIndex = i;
|
|
174
|
+
i++;
|
|
175
|
+
}
|
|
176
|
+
else if (line && line.trim() === '' && i < startIndex + 15) {
|
|
177
|
+
// Empty line might be part of our block
|
|
178
|
+
endIndex = i;
|
|
179
|
+
i++;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// We've reached content that's not part of our block
|
|
183
|
+
inVVBlock = false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Remove the section (inclusive)
|
|
187
|
+
lines.splice(startIndex, endIndex - startIndex + 1);
|
|
188
|
+
// Clean up extra blank lines
|
|
189
|
+
let result = lines.join('\n');
|
|
190
|
+
result = result.replace(/\n{3,}/g, '\n\n').trim();
|
|
191
|
+
if (result) {
|
|
192
|
+
writeFileSync(hookPath, result + '\n', 'utf8');
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// If hook is now empty, remove it
|
|
196
|
+
unlinkSync(hookPath);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export function createHooksCommand() {
|
|
200
|
+
const command = new Command('hooks');
|
|
201
|
+
command
|
|
202
|
+
.description('Manage husky pre-commit hooks for Principal View')
|
|
203
|
+
.option('-p, --path <path>', 'Repository path (defaults to current directory)')
|
|
204
|
+
.option('--add', 'Add Principal View checks to pre-commit hook')
|
|
205
|
+
.option('--remove', 'Remove Principal View checks from pre-commit hook')
|
|
206
|
+
.option('--check', 'Check if Principal View checks exist in pre-commit hook')
|
|
207
|
+
.option('--init', 'Initialize husky if not already installed')
|
|
208
|
+
.action((options) => {
|
|
209
|
+
try {
|
|
210
|
+
const repoPath = findRepoRoot(options.path || process.cwd());
|
|
211
|
+
// Check if it's a git repository
|
|
212
|
+
if (!existsSync(join(repoPath, '.git'))) {
|
|
213
|
+
console.error(chalk.red('❌ Not a git repository'));
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
// Handle init option
|
|
217
|
+
if (options.init) {
|
|
218
|
+
initializeHusky(repoPath);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// Check if husky is installed
|
|
222
|
+
if (!isHuskyInstalled(repoPath)) {
|
|
223
|
+
if (options.check) {
|
|
224
|
+
console.log(chalk.red('❌ Husky is not installed'));
|
|
225
|
+
console.log(' Run "npx @principal-ai/principal-view-cli hooks --init" to install husky');
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
else if (options.add) {
|
|
229
|
+
console.log(chalk.red('❌ Husky is not installed'));
|
|
230
|
+
console.log(' Run "npx @principal-ai/principal-view-cli hooks --init" first to install husky');
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
else if (options.remove) {
|
|
234
|
+
console.log('ℹ️ Husky is not installed');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
console.log(chalk.red('❌ Husky is not installed in this repository'));
|
|
239
|
+
console.log('\nTo install husky and set up Principal View hooks:');
|
|
240
|
+
console.log(' npx @principal-ai/principal-view-cli hooks --init --add');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const hasHook = hasVVHook(repoPath);
|
|
245
|
+
if (options.check) {
|
|
246
|
+
if (hasHook) {
|
|
247
|
+
console.log(chalk.green('✅ Principal View checks found in pre-commit hook'));
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
console.log(chalk.red('❌ No Principal View checks in pre-commit hook'));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else if (options.add) {
|
|
255
|
+
if (hasHook) {
|
|
256
|
+
console.log('ℹ️ Principal View checks already exist in pre-commit hook');
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
addVVHook(repoPath);
|
|
260
|
+
console.log(chalk.green('✅ Added Principal View checks to pre-commit hook'));
|
|
261
|
+
console.log('\nPre-commit hook will now:');
|
|
262
|
+
console.log(' • Run npx @principal-ai/principal-view-cli doctor to check for stale configurations');
|
|
263
|
+
console.log(' • Validate all .canvas files');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else if (options.remove) {
|
|
267
|
+
if (!hasHook) {
|
|
268
|
+
console.log('ℹ️ No Principal View checks found in pre-commit hook');
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
removeVVHook(repoPath);
|
|
272
|
+
console.log(chalk.green('✅ Removed Principal View checks from pre-commit hook'));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
// Default action: show status
|
|
277
|
+
console.log(chalk.bold('\nPrincipal View Hooks Status\n'));
|
|
278
|
+
console.log(`Repository: ${repoPath}`);
|
|
279
|
+
console.log(`Husky: ${chalk.green('installed')}`);
|
|
280
|
+
console.log(`PV Hooks: ${hasHook ? chalk.green('configured') : chalk.yellow('not configured')}`);
|
|
281
|
+
if (hasHook) {
|
|
282
|
+
console.log('\nUse --remove to remove or --check to verify');
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
console.log('\nUse --add to add or --check to verify');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
return command;
|
|
295
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+GpC,wBAAgB,iBAAiB,IAAI,OAAO,CA4L3C"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init command - Initialize a .principal-views folder with template files and linting setup
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
const TEMPLATE_CANVAS = {
|
|
10
|
+
nodes: [],
|
|
11
|
+
edges: [],
|
|
12
|
+
};
|
|
13
|
+
const TEMPLATE_PRIVURC = `# Principal View Configuration Lint Rules
|
|
14
|
+
# See: https://github.com/principal-ai/principal-view
|
|
15
|
+
|
|
16
|
+
# File patterns to include
|
|
17
|
+
include:
|
|
18
|
+
- ".principal-views/**/*.yaml"
|
|
19
|
+
- ".principal-views/**/*.yml"
|
|
20
|
+
- ".principal-views/**/*.json"
|
|
21
|
+
|
|
22
|
+
# File patterns to exclude
|
|
23
|
+
exclude:
|
|
24
|
+
- "**/node_modules/**"
|
|
25
|
+
- ".principal-views/library.yaml"
|
|
26
|
+
|
|
27
|
+
# Path to component library (optional)
|
|
28
|
+
library: ".principal-views/library.yaml"
|
|
29
|
+
|
|
30
|
+
# Rule configuration
|
|
31
|
+
# Severity: "off" | "warn" | "error" (or 0 | 1 | 2)
|
|
32
|
+
rules:
|
|
33
|
+
# Schema rules - validate structure
|
|
34
|
+
no-unknown-fields: error
|
|
35
|
+
required-metadata: error
|
|
36
|
+
valid-node-types: error
|
|
37
|
+
valid-edge-types: error
|
|
38
|
+
valid-color-format: error
|
|
39
|
+
|
|
40
|
+
# Reference rules - check cross-references
|
|
41
|
+
connection-type-references: error
|
|
42
|
+
state-transition-references: error
|
|
43
|
+
|
|
44
|
+
# Structure rules - ensure completeness
|
|
45
|
+
minimum-node-sources:
|
|
46
|
+
severity: error
|
|
47
|
+
options:
|
|
48
|
+
minimum: 1
|
|
49
|
+
excludeNodeTypes: []
|
|
50
|
+
orphaned-node-types: error
|
|
51
|
+
orphaned-edge-types: error
|
|
52
|
+
unreachable-states: error
|
|
53
|
+
dead-end-states: error
|
|
54
|
+
|
|
55
|
+
# Pattern rules - validate regex patterns
|
|
56
|
+
valid-action-patterns:
|
|
57
|
+
severity: error
|
|
58
|
+
options:
|
|
59
|
+
strictMode: false
|
|
60
|
+
|
|
61
|
+
# Library rules - check against component library
|
|
62
|
+
library-node-type-match:
|
|
63
|
+
severity: error
|
|
64
|
+
options:
|
|
65
|
+
allowExtra: true
|
|
66
|
+
`;
|
|
67
|
+
const HUSKY_PRE_COMMIT = `# Run principal view linting on staged .principal-views files
|
|
68
|
+
npx @principal-ai/principal-view-cli lint --quiet
|
|
69
|
+
`;
|
|
70
|
+
/**
|
|
71
|
+
* Check if we're in a git repository
|
|
72
|
+
*/
|
|
73
|
+
function isGitRepo() {
|
|
74
|
+
try {
|
|
75
|
+
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the git root directory
|
|
84
|
+
*/
|
|
85
|
+
function getGitRoot() {
|
|
86
|
+
try {
|
|
87
|
+
return execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Check if husky is already installed
|
|
95
|
+
*/
|
|
96
|
+
function isHuskyInstalled(gitRoot) {
|
|
97
|
+
return existsSync(join(gitRoot, '.husky'));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Detect package manager
|
|
101
|
+
*/
|
|
102
|
+
function detectPackageManager() {
|
|
103
|
+
if (existsSync('bun.lockb'))
|
|
104
|
+
return 'bun';
|
|
105
|
+
if (existsSync('pnpm-lock.yaml'))
|
|
106
|
+
return 'pnpm';
|
|
107
|
+
if (existsSync('yarn.lock'))
|
|
108
|
+
return 'yarn';
|
|
109
|
+
return 'npm';
|
|
110
|
+
}
|
|
111
|
+
export function createInitCommand() {
|
|
112
|
+
const command = new Command('init');
|
|
113
|
+
command
|
|
114
|
+
.description('Initialize a .principal-views folder with template files and linting setup')
|
|
115
|
+
.option('-f, --force', 'Overwrite existing files')
|
|
116
|
+
.option('-n, --name <name>', 'Name for the canvas file', 'architecture')
|
|
117
|
+
.option('--no-husky', 'Skip Husky pre-commit hook setup')
|
|
118
|
+
.option('--no-lint-config', 'Skip .privurc.yaml creation')
|
|
119
|
+
.action(async (options) => {
|
|
120
|
+
try {
|
|
121
|
+
const cwd = process.cwd();
|
|
122
|
+
const principalViewsDir = join(cwd, '.principal-views');
|
|
123
|
+
const canvasFile = join(principalViewsDir, `${options.name}.canvas`);
|
|
124
|
+
const libraryFile = join(principalViewsDir, 'library.yaml');
|
|
125
|
+
const privurcFile = join(cwd, '.privurc.yaml');
|
|
126
|
+
// Check if .principal-views directory exists
|
|
127
|
+
if (!existsSync(principalViewsDir)) {
|
|
128
|
+
mkdirSync(principalViewsDir, { recursive: true });
|
|
129
|
+
console.log(chalk.green(`Created directory: .principal-views/`));
|
|
130
|
+
}
|
|
131
|
+
// Create canvas file
|
|
132
|
+
if (existsSync(canvasFile) && !options.force) {
|
|
133
|
+
console.log(chalk.yellow(`Canvas file already exists: .principal-views/${options.name}.canvas`));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
writeFileSync(canvasFile, JSON.stringify(TEMPLATE_CANVAS, null, 2));
|
|
137
|
+
console.log(chalk.green(`Created canvas file: .principal-views/${options.name}.canvas`));
|
|
138
|
+
}
|
|
139
|
+
// Create library file
|
|
140
|
+
if (existsSync(libraryFile) && !options.force) {
|
|
141
|
+
console.log(chalk.yellow(`Library file already exists: .principal-views/library.yaml`));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const libraryYaml = `version: "1.0.0"
|
|
145
|
+
name: "Component Library"
|
|
146
|
+
|
|
147
|
+
nodeComponents: {}
|
|
148
|
+
|
|
149
|
+
edgeComponents: {}
|
|
150
|
+
`;
|
|
151
|
+
writeFileSync(libraryFile, libraryYaml);
|
|
152
|
+
console.log(chalk.green(`Created library file: .principal-views/library.yaml`));
|
|
153
|
+
}
|
|
154
|
+
// Create .privurc.yaml config file
|
|
155
|
+
if (options.lintConfig !== false) {
|
|
156
|
+
if (existsSync(privurcFile) && !options.force) {
|
|
157
|
+
console.log(chalk.yellow(`Config file already exists: .privurc.yaml`));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
writeFileSync(privurcFile, TEMPLATE_PRIVURC);
|
|
161
|
+
console.log(chalk.green(`Created lint config: .privurc.yaml`));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Set up Husky pre-commit hook
|
|
165
|
+
let huskySetup = false;
|
|
166
|
+
if (options.husky !== false) {
|
|
167
|
+
if (!isGitRepo()) {
|
|
168
|
+
console.log(chalk.yellow(`Skipping Husky setup: not a git repository`));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const gitRoot = getGitRoot();
|
|
172
|
+
if (!gitRoot) {
|
|
173
|
+
console.log(chalk.yellow(`Skipping Husky setup: could not find git root`));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const huskyDir = join(gitRoot, '.husky');
|
|
177
|
+
const preCommitFile = join(huskyDir, 'pre-commit');
|
|
178
|
+
if (isHuskyInstalled(gitRoot)) {
|
|
179
|
+
// Husky is already installed, just add/update pre-commit hook
|
|
180
|
+
if (existsSync(preCommitFile)) {
|
|
181
|
+
// Check if our hook is already in the file
|
|
182
|
+
const existingContent = readFileSync(preCommitFile, 'utf8');
|
|
183
|
+
if (existingContent.includes('principal-view-cli lint')) {
|
|
184
|
+
console.log(chalk.yellow(`Husky pre-commit hook already includes principal view linting`));
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// Append our lint command to existing pre-commit
|
|
188
|
+
const updatedContent = existingContent.trimEnd() +
|
|
189
|
+
'\n\n# Run principal view linting\nnpx @principal-ai/principal-view-cli lint --quiet\n';
|
|
190
|
+
writeFileSync(preCommitFile, updatedContent);
|
|
191
|
+
console.log(chalk.green(`Updated Husky pre-commit hook with principal view linting`));
|
|
192
|
+
huskySetup = true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
// Create new pre-commit hook
|
|
197
|
+
writeFileSync(preCommitFile, HUSKY_PRE_COMMIT);
|
|
198
|
+
chmodSync(preCommitFile, '755');
|
|
199
|
+
console.log(chalk.green(`Created Husky pre-commit hook`));
|
|
200
|
+
huskySetup = true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// Husky is not installed, try to install it
|
|
205
|
+
const pm = detectPackageManager();
|
|
206
|
+
const packageJsonPath = join(gitRoot, 'package.json');
|
|
207
|
+
if (!existsSync(packageJsonPath)) {
|
|
208
|
+
console.log(chalk.yellow(`Skipping Husky setup: no package.json found`));
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
console.log(chalk.dim(`Installing Husky...`));
|
|
212
|
+
try {
|
|
213
|
+
// Install husky as dev dependency
|
|
214
|
+
const installCmd = {
|
|
215
|
+
npm: 'npm install --save-dev husky',
|
|
216
|
+
yarn: 'yarn add --dev husky',
|
|
217
|
+
pnpm: 'pnpm add --save-dev husky',
|
|
218
|
+
bun: 'bun add --dev husky',
|
|
219
|
+
}[pm];
|
|
220
|
+
execSync(installCmd, { stdio: 'inherit', cwd: gitRoot });
|
|
221
|
+
// Initialize husky
|
|
222
|
+
execSync('npx husky init', { stdio: 'inherit', cwd: gitRoot });
|
|
223
|
+
// Write our pre-commit hook
|
|
224
|
+
writeFileSync(preCommitFile, HUSKY_PRE_COMMIT);
|
|
225
|
+
chmodSync(preCommitFile, '755');
|
|
226
|
+
console.log(chalk.green(`Installed Husky and created pre-commit hook`));
|
|
227
|
+
huskySetup = true;
|
|
228
|
+
}
|
|
229
|
+
catch (installError) {
|
|
230
|
+
console.log(chalk.yellow(`Could not install Husky automatically: ${installError.message}`));
|
|
231
|
+
console.log(chalk.dim(` You can install it manually: ${pm} add --dev husky && npx husky init`));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
console.log('');
|
|
239
|
+
console.log(chalk.bold('Setup complete!'));
|
|
240
|
+
console.log('');
|
|
241
|
+
console.log(chalk.bold('Files created:'));
|
|
242
|
+
console.log(` • ${chalk.cyan('.principal-views/library.yaml')} - Component library definitions`);
|
|
243
|
+
console.log(` • ${chalk.cyan(`.principal-views/${options.name}.canvas`)} - Graph canvas file`);
|
|
244
|
+
if (options.lintConfig !== false) {
|
|
245
|
+
console.log(` • ${chalk.cyan('.privurc.yaml')} - Lint configuration`);
|
|
246
|
+
}
|
|
247
|
+
if (huskySetup) {
|
|
248
|
+
console.log(` • ${chalk.cyan('.husky/pre-commit')} - Pre-commit hook`);
|
|
249
|
+
}
|
|
250
|
+
console.log('');
|
|
251
|
+
console.log(chalk.bold('Next steps:'));
|
|
252
|
+
console.log(` 1. Define components in ${chalk.cyan('.principal-views/library.yaml')}`);
|
|
253
|
+
console.log(` 2. Build your graph in ${chalk.cyan(`.principal-views/${options.name}.canvas`)}`);
|
|
254
|
+
console.log(` 3. Run ${chalk.cyan('npx @principal-ai/principal-view-cli lint')} to validate your configuration`);
|
|
255
|
+
if (huskySetup) {
|
|
256
|
+
console.log(` 4. Commits will now automatically lint .principal-views files`);
|
|
257
|
+
}
|
|
258
|
+
console.log('');
|
|
259
|
+
console.log(chalk.bold('Commands:'));
|
|
260
|
+
console.log(` • ${chalk.cyan('npx @principal-ai/principal-view-cli lint')} - Lint configuration files`);
|
|
261
|
+
console.log(` • ${chalk.cyan('npx @principal-ai/principal-view-cli lint --json')} - Output lint results as JSON`);
|
|
262
|
+
console.log(` • ${chalk.cyan('npx @principal-ai/principal-view-cli validate')} - Validate canvas files`);
|
|
263
|
+
console.log(` • ${chalk.cyan('npx @principal-ai/principal-view-cli doctor')} - Check project setup`);
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
console.error(chalk.red('Error:'), error.message);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
return command;
|
|
271
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/commands/lint.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsUpC,wBAAgB,iBAAiB,IAAI,OAAO,CA+P3C"}
|