busy-cli 0.1.2
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 +129 -0
- package/dist/builders/context.d.ts +50 -0
- package/dist/builders/context.d.ts.map +1 -0
- package/dist/builders/context.js +190 -0
- package/dist/cache/index.d.ts +100 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +270 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +463 -0
- package/dist/commands/package.d.ts +96 -0
- package/dist/commands/package.d.ts.map +1 -0
- package/dist/commands/package.js +285 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/loader.d.ts +6 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +361 -0
- package/dist/merge.d.ts +16 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +102 -0
- package/dist/package/manifest.d.ts +59 -0
- package/dist/package/manifest.d.ts.map +1 -0
- package/dist/package/manifest.js +265 -0
- package/dist/parser.d.ts +28 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +220 -0
- package/dist/parsers/frontmatter.d.ts +14 -0
- package/dist/parsers/frontmatter.d.ts.map +1 -0
- package/dist/parsers/frontmatter.js +110 -0
- package/dist/parsers/imports.d.ts +48 -0
- package/dist/parsers/imports.d.ts.map +1 -0
- package/dist/parsers/imports.js +147 -0
- package/dist/parsers/links.d.ts +12 -0
- package/dist/parsers/links.d.ts.map +1 -0
- package/dist/parsers/links.js +79 -0
- package/dist/parsers/localdefs.d.ts +6 -0
- package/dist/parsers/localdefs.d.ts.map +1 -0
- package/dist/parsers/localdefs.js +132 -0
- package/dist/parsers/operations.d.ts +32 -0
- package/dist/parsers/operations.d.ts.map +1 -0
- package/dist/parsers/operations.js +313 -0
- package/dist/parsers/sections.d.ts +15 -0
- package/dist/parsers/sections.d.ts.map +1 -0
- package/dist/parsers/sections.js +173 -0
- package/dist/parsers/tools.d.ts +30 -0
- package/dist/parsers/tools.d.ts.map +1 -0
- package/dist/parsers/tools.js +178 -0
- package/dist/parsers/triggers.d.ts +35 -0
- package/dist/parsers/triggers.d.ts.map +1 -0
- package/dist/parsers/triggers.js +219 -0
- package/dist/providers/base.d.ts +60 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +34 -0
- package/dist/providers/github.d.ts +18 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +109 -0
- package/dist/providers/gitlab.d.ts +18 -0
- package/dist/providers/gitlab.d.ts.map +1 -0
- package/dist/providers/gitlab.js +101 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +17 -0
- package/dist/providers/local.d.ts +31 -0
- package/dist/providers/local.d.ts.map +1 -0
- package/dist/providers/local.js +116 -0
- package/dist/providers/url.d.ts +16 -0
- package/dist/providers/url.d.ts.map +1 -0
- package/dist/providers/url.js +45 -0
- package/dist/registry/index.d.ts +99 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +320 -0
- package/dist/types/schema.d.ts +3259 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +258 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +23 -0
- package/dist/utils/slugify.d.ts +14 -0
- package/dist/utils/slugify.d.ts.map +1 -0
- package/dist/utils/slugify.js +28 -0
- package/package.json +61 -0
- package/src/__tests__/cache.test.ts +393 -0
- package/src/__tests__/cli-package.test.ts +667 -0
- package/src/__tests__/fixtures/automated-workflow.busy.md +84 -0
- package/src/__tests__/fixtures/concept.busy.md +30 -0
- package/src/__tests__/fixtures/document.busy.md +44 -0
- package/src/__tests__/fixtures/simple-operation.busy.md +45 -0
- package/src/__tests__/fixtures/tool-document.busy.md +71 -0
- package/src/__tests__/fixtures/tool.busy.md +54 -0
- package/src/__tests__/imports.test.ts +244 -0
- package/src/__tests__/integration.test.ts +432 -0
- package/src/__tests__/operations.test.ts +408 -0
- package/src/__tests__/package-manifest.test.ts +455 -0
- package/src/__tests__/providers.test.ts +672 -0
- package/src/__tests__/registry.test.ts +402 -0
- package/src/__tests__/schema.test.ts +467 -0
- package/src/__tests__/tools.test.ts +376 -0
- package/src/__tests__/triggers.test.ts +312 -0
- package/src/builders/context.ts +294 -0
- package/src/cache/index.ts +312 -0
- package/src/cli/index.ts +514 -0
- package/src/commands/package.ts +392 -0
- package/src/index.ts +46 -0
- package/src/loader.ts +474 -0
- package/src/merge.ts +126 -0
- package/src/package/manifest.ts +349 -0
- package/src/parser.ts +278 -0
- package/src/parsers/frontmatter.ts +135 -0
- package/src/parsers/imports.ts +196 -0
- package/src/parsers/links.ts +108 -0
- package/src/parsers/localdefs.ts +166 -0
- package/src/parsers/operations.ts +404 -0
- package/src/parsers/sections.ts +230 -0
- package/src/parsers/tools.ts +215 -0
- package/src/parsers/triggers.ts +252 -0
- package/src/providers/base.ts +77 -0
- package/src/providers/github.ts +129 -0
- package/src/providers/gitlab.ts +121 -0
- package/src/providers/index.ts +25 -0
- package/src/providers/local.ts +129 -0
- package/src/providers/url.ts +56 -0
- package/src/registry/index.ts +408 -0
- package/src/types/schema.ts +369 -0
- package/src/utils/logger.ts +25 -0
- package/src/utils/slugify.ts +31 -0
- package/tsconfig.json +21 -0
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { parseDocument, resolveImports } from '../parser.js';
|
|
5
|
+
import { writeFile, readFile } from 'fs/promises';
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
import { resolve, basename } from 'path';
|
|
8
|
+
import {
|
|
9
|
+
initWorkspace,
|
|
10
|
+
checkWorkspace,
|
|
11
|
+
addPackage,
|
|
12
|
+
removePackage,
|
|
13
|
+
upgradePackage,
|
|
14
|
+
listPackages,
|
|
15
|
+
getPackageInfo,
|
|
16
|
+
} from '../commands/package.js';
|
|
17
|
+
|
|
18
|
+
const program = new Command();
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.name('busy')
|
|
22
|
+
.description('BUSY Document Parser CLI')
|
|
23
|
+
.version('0.1.0');
|
|
24
|
+
|
|
25
|
+
// Parse command - parse a single BUSY document
|
|
26
|
+
program
|
|
27
|
+
.command('parse')
|
|
28
|
+
.description('Parse a BUSY document and output its structure as JSON')
|
|
29
|
+
.argument('<file>', 'Path to the BUSY markdown file')
|
|
30
|
+
.option('-o, --output <file>', 'Output file for parsed JSON')
|
|
31
|
+
.option('--pretty', 'Pretty-print JSON output', true)
|
|
32
|
+
.option('--no-pretty', 'Compact JSON output')
|
|
33
|
+
.action(async (file: string, options) => {
|
|
34
|
+
try {
|
|
35
|
+
const filePath = resolve(file);
|
|
36
|
+
|
|
37
|
+
if (!existsSync(filePath)) {
|
|
38
|
+
console.error(`Error: File not found: ${filePath}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const content = await readFile(filePath, 'utf-8');
|
|
43
|
+
const doc = parseDocument(content);
|
|
44
|
+
|
|
45
|
+
const output = options.pretty
|
|
46
|
+
? JSON.stringify(doc, null, 2)
|
|
47
|
+
: JSON.stringify(doc);
|
|
48
|
+
|
|
49
|
+
if (options.output) {
|
|
50
|
+
await writeFile(options.output, output, 'utf-8');
|
|
51
|
+
console.log(`✓ Parsed ${basename(file)}`);
|
|
52
|
+
console.log(` Type: ${doc.metadata.type}`);
|
|
53
|
+
console.log(` Imports: ${doc.imports.length}`);
|
|
54
|
+
console.log(` Definitions: ${doc.definitions.length}`);
|
|
55
|
+
console.log(` Operations: ${doc.operations.length}`);
|
|
56
|
+
console.log(` Triggers: ${doc.triggers.length}`);
|
|
57
|
+
if ('tools' in doc) {
|
|
58
|
+
console.log(` Tools: ${doc.tools.length}`);
|
|
59
|
+
}
|
|
60
|
+
console.log(`✓ Written to ${options.output}`);
|
|
61
|
+
} else {
|
|
62
|
+
console.log(output);
|
|
63
|
+
}
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error('Error parsing document:', err instanceof Error ? err.message : err);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Validate command - validate a BUSY document
|
|
71
|
+
program
|
|
72
|
+
.command('validate')
|
|
73
|
+
.description('Validate a BUSY document (check frontmatter, structure, imports)')
|
|
74
|
+
.argument('<file>', 'Path to the BUSY markdown file')
|
|
75
|
+
.option('--resolve-imports', 'Also validate that imports can be resolved')
|
|
76
|
+
.action(async (file: string, options) => {
|
|
77
|
+
try {
|
|
78
|
+
const filePath = resolve(file);
|
|
79
|
+
|
|
80
|
+
if (!existsSync(filePath)) {
|
|
81
|
+
console.error(`✗ File not found: ${filePath}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const content = await readFile(filePath, 'utf-8');
|
|
86
|
+
|
|
87
|
+
// Parse document (this validates frontmatter and structure)
|
|
88
|
+
const doc = parseDocument(content);
|
|
89
|
+
|
|
90
|
+
console.log(`✓ Valid BUSY document: ${doc.metadata.name}`);
|
|
91
|
+
console.log(` Type: ${doc.metadata.type}`);
|
|
92
|
+
console.log(` Description: ${doc.metadata.description.slice(0, 60)}${doc.metadata.description.length > 60 ? '...' : ''}`);
|
|
93
|
+
|
|
94
|
+
// Check for common issues
|
|
95
|
+
const warnings: string[] = [];
|
|
96
|
+
const errors: string[] = [];
|
|
97
|
+
|
|
98
|
+
// Check if operations have steps
|
|
99
|
+
for (const op of doc.operations) {
|
|
100
|
+
if (op.steps.length === 0) {
|
|
101
|
+
warnings.push(`Operation "${op.name}" has no steps`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check for empty imports
|
|
106
|
+
if (doc.imports.length === 0 && doc.operations.length > 0) {
|
|
107
|
+
warnings.push('Document has operations but no imports');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Validate imports if requested
|
|
111
|
+
if (options.resolveImports) {
|
|
112
|
+
console.log('\nResolving imports...');
|
|
113
|
+
try {
|
|
114
|
+
const resolved = resolveImports(doc, filePath);
|
|
115
|
+
console.log(`✓ Resolved ${Object.keys(resolved).length} imports`);
|
|
116
|
+
|
|
117
|
+
for (const [name, resolvedDoc] of Object.entries(resolved)) {
|
|
118
|
+
console.log(` - ${name}: ${resolvedDoc.metadata.name} (${resolvedDoc.metadata.type})`);
|
|
119
|
+
}
|
|
120
|
+
} catch (err) {
|
|
121
|
+
errors.push(`Import resolution failed: ${err instanceof Error ? err.message : err}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Print warnings and errors
|
|
126
|
+
if (warnings.length > 0) {
|
|
127
|
+
console.log('\nWarnings:');
|
|
128
|
+
for (const warning of warnings) {
|
|
129
|
+
console.log(` ⚠ ${warning}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (errors.length > 0) {
|
|
134
|
+
console.log('\nErrors:');
|
|
135
|
+
for (const error of errors) {
|
|
136
|
+
console.log(` ✗ ${error}`);
|
|
137
|
+
}
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log('\n✓ Validation passed');
|
|
142
|
+
} catch (err) {
|
|
143
|
+
console.error(`✗ Validation failed: ${err instanceof Error ? err.message : err}`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Resolve command - resolve imports in a document
|
|
149
|
+
program
|
|
150
|
+
.command('resolve')
|
|
151
|
+
.description('Resolve all imports in a BUSY document recursively')
|
|
152
|
+
.argument('<file>', 'Path to the BUSY markdown file')
|
|
153
|
+
.option('-o, --output <file>', 'Output file for resolved imports JSON')
|
|
154
|
+
.option('--flat', 'Output flat list of resolved documents')
|
|
155
|
+
.action(async (file: string, options) => {
|
|
156
|
+
try {
|
|
157
|
+
const filePath = resolve(file);
|
|
158
|
+
|
|
159
|
+
if (!existsSync(filePath)) {
|
|
160
|
+
console.error(`Error: File not found: ${filePath}`);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const content = await readFile(filePath, 'utf-8');
|
|
165
|
+
const doc = parseDocument(content);
|
|
166
|
+
|
|
167
|
+
console.log(`Resolving imports for: ${doc.metadata.name}`);
|
|
168
|
+
|
|
169
|
+
const resolved = resolveImports(doc, filePath);
|
|
170
|
+
const count = Object.keys(resolved).length;
|
|
171
|
+
|
|
172
|
+
console.log(`✓ Resolved ${count} imports`);
|
|
173
|
+
|
|
174
|
+
let output: string;
|
|
175
|
+
if (options.flat) {
|
|
176
|
+
// Flat list of document names and their metadata
|
|
177
|
+
const flat = Object.entries(resolved).map(([name, resolvedDoc]) => ({
|
|
178
|
+
conceptName: name,
|
|
179
|
+
name: resolvedDoc.metadata.name,
|
|
180
|
+
type: resolvedDoc.metadata.type,
|
|
181
|
+
operations: resolvedDoc.operations.map(op => op.name),
|
|
182
|
+
}));
|
|
183
|
+
output = JSON.stringify(flat, null, 2);
|
|
184
|
+
} else {
|
|
185
|
+
// Full resolved documents
|
|
186
|
+
output = JSON.stringify(resolved, null, 2);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (options.output) {
|
|
190
|
+
await writeFile(options.output, output, 'utf-8');
|
|
191
|
+
console.log(`✓ Written to ${options.output}`);
|
|
192
|
+
} else {
|
|
193
|
+
console.log(output);
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.error('Error resolving imports:', err instanceof Error ? err.message : err);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Info command - quick document info
|
|
202
|
+
program
|
|
203
|
+
.command('info')
|
|
204
|
+
.description('Show quick information about a BUSY document')
|
|
205
|
+
.argument('<file>', 'Path to the BUSY markdown file')
|
|
206
|
+
.action(async (file: string) => {
|
|
207
|
+
try {
|
|
208
|
+
const filePath = resolve(file);
|
|
209
|
+
|
|
210
|
+
if (!existsSync(filePath)) {
|
|
211
|
+
console.error(`Error: File not found: ${filePath}`);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const content = await readFile(filePath, 'utf-8');
|
|
216
|
+
const doc = parseDocument(content);
|
|
217
|
+
|
|
218
|
+
console.log(`\n📄 ${doc.metadata.name}`);
|
|
219
|
+
console.log(`${'─'.repeat(40)}`);
|
|
220
|
+
console.log(`Type: ${doc.metadata.type}`);
|
|
221
|
+
console.log(`Description: ${doc.metadata.description}`);
|
|
222
|
+
if (doc.metadata.provider) {
|
|
223
|
+
console.log(`Provider: ${doc.metadata.provider}`);
|
|
224
|
+
}
|
|
225
|
+
console.log(`${'─'.repeat(40)}`);
|
|
226
|
+
console.log(`Imports: ${doc.imports.length}`);
|
|
227
|
+
if (doc.imports.length > 0) {
|
|
228
|
+
for (const imp of doc.imports) {
|
|
229
|
+
console.log(` - [${imp.conceptName}]: ${imp.path}${imp.anchor ? '#' + imp.anchor : ''}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
console.log(`Definitions: ${doc.definitions.length}`);
|
|
233
|
+
if (doc.definitions.length > 0) {
|
|
234
|
+
for (const def of doc.definitions) {
|
|
235
|
+
console.log(` - ${def.name}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
console.log(`Operations: ${doc.operations.length}`);
|
|
239
|
+
if (doc.operations.length > 0) {
|
|
240
|
+
for (const op of doc.operations) {
|
|
241
|
+
console.log(` - ${op.name} (${op.steps.length} steps${op.checklist ? `, ${op.checklist.items.length} checklist items` : ''})`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
console.log(`Triggers: ${doc.triggers.length}`);
|
|
245
|
+
if (doc.triggers.length > 0) {
|
|
246
|
+
for (const trigger of doc.triggers) {
|
|
247
|
+
if (trigger.triggerType === 'alarm') {
|
|
248
|
+
console.log(` - ⏰ ${trigger.schedule} → ${trigger.operation}`);
|
|
249
|
+
} else {
|
|
250
|
+
console.log(` - 📡 ${trigger.eventType}${trigger.filter ? ` (filtered)` : ''} → ${trigger.operation}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if ('tools' in doc && doc.tools.length > 0) {
|
|
255
|
+
console.log(`Tools: ${doc.tools.length}`);
|
|
256
|
+
for (const tool of doc.tools) {
|
|
257
|
+
const providers = tool.providers ? Object.keys(tool.providers).join(', ') : 'none';
|
|
258
|
+
console.log(` - ${tool.name} (providers: ${providers})`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
console.log('');
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error('Error reading document:', err instanceof Error ? err.message : err);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Init command - initialize a BUSY workspace
|
|
269
|
+
program
|
|
270
|
+
.command('init')
|
|
271
|
+
.description('Initialize a new BUSY workspace with package.busy.md')
|
|
272
|
+
.option('-d, --dir <directory>', 'Directory to initialize', '.')
|
|
273
|
+
.action(async (options) => {
|
|
274
|
+
try {
|
|
275
|
+
const workspaceRoot = resolve(options.dir);
|
|
276
|
+
const result = await initWorkspace(workspaceRoot);
|
|
277
|
+
|
|
278
|
+
console.log('\nInitializing BUSY workspace...\n');
|
|
279
|
+
|
|
280
|
+
if (result.created.length > 0) {
|
|
281
|
+
for (const item of result.created) {
|
|
282
|
+
console.log(` ✓ Created ${item}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (result.skipped.length > 0) {
|
|
287
|
+
for (const item of result.skipped) {
|
|
288
|
+
console.log(` - Skipped ${item} (already exists)`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
console.log('\n✓ Workspace initialized');
|
|
293
|
+
} catch (err) {
|
|
294
|
+
console.error('Error initializing workspace:', err instanceof Error ? err.message : err);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// Check command - validate workspace coherence
|
|
300
|
+
program
|
|
301
|
+
.command('check')
|
|
302
|
+
.description('Validate workspace coherence (check packages, links, integrity)')
|
|
303
|
+
.option('-d, --dir <directory>', 'Workspace directory', '.')
|
|
304
|
+
.option('--skip-external', 'Skip validation of external URLs')
|
|
305
|
+
.option('-v, --verbose', 'Show all checks, not just errors')
|
|
306
|
+
.action(async (options) => {
|
|
307
|
+
try {
|
|
308
|
+
const workspaceRoot = resolve(options.dir);
|
|
309
|
+
const result = await checkWorkspace(workspaceRoot, {
|
|
310
|
+
skipExternal: options.skipExternal,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
console.log('\nChecking workspace...\n');
|
|
314
|
+
console.log(` Dependencies: ${result.packages}`);
|
|
315
|
+
|
|
316
|
+
if (result.errors.length > 0) {
|
|
317
|
+
console.log('\nErrors:');
|
|
318
|
+
for (const error of result.errors) {
|
|
319
|
+
console.log(` ✗ ${error}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (result.warnings.length > 0) {
|
|
324
|
+
console.log('\nWarnings:');
|
|
325
|
+
for (const warning of result.warnings) {
|
|
326
|
+
console.log(` ⚠ ${warning}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (result.valid) {
|
|
331
|
+
console.log('\n✓ Workspace is coherent');
|
|
332
|
+
} else {
|
|
333
|
+
console.log('\n✗ Workspace has errors');
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
} catch (err) {
|
|
337
|
+
console.error('✗ Check failed:', err instanceof Error ? err.message : err);
|
|
338
|
+
process.exit(2);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Package command group
|
|
343
|
+
const packageCmd = program
|
|
344
|
+
.command('package')
|
|
345
|
+
.description('Manage packages in the workspace');
|
|
346
|
+
|
|
347
|
+
// Package add
|
|
348
|
+
packageCmd
|
|
349
|
+
.command('add')
|
|
350
|
+
.description('Add a package from URL')
|
|
351
|
+
.argument('<url>', 'URL to the BUSY document')
|
|
352
|
+
.option('-d, --dir <directory>', 'Workspace directory', '.')
|
|
353
|
+
.action(async (url: string, options) => {
|
|
354
|
+
try {
|
|
355
|
+
const workspaceRoot = resolve(options.dir);
|
|
356
|
+
|
|
357
|
+
console.log(`\nAdding package from: ${url}\n`);
|
|
358
|
+
|
|
359
|
+
const result = await addPackage(workspaceRoot, url);
|
|
360
|
+
|
|
361
|
+
console.log(` ID: ${result.id}`);
|
|
362
|
+
console.log(` Provider: ${result.provider}`);
|
|
363
|
+
console.log(` Version: ${result.version}`);
|
|
364
|
+
console.log(` Cached: ${result.cached}`);
|
|
365
|
+
|
|
366
|
+
console.log(`\n✓ Package added: ${result.id}`);
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.error('Error adding package:', err instanceof Error ? err.message : err);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Package remove
|
|
374
|
+
packageCmd
|
|
375
|
+
.command('remove')
|
|
376
|
+
.description('Remove a package')
|
|
377
|
+
.argument('<name>', 'Package ID to remove')
|
|
378
|
+
.option('-d, --dir <directory>', 'Workspace directory', '.')
|
|
379
|
+
.action(async (name: string, options) => {
|
|
380
|
+
try {
|
|
381
|
+
const workspaceRoot = resolve(options.dir);
|
|
382
|
+
|
|
383
|
+
console.log(`\nRemoving package: ${name}\n`);
|
|
384
|
+
|
|
385
|
+
const result = await removePackage(workspaceRoot, name);
|
|
386
|
+
|
|
387
|
+
if (result.removed) {
|
|
388
|
+
console.log(' ✓ Removed from package.busy.md');
|
|
389
|
+
console.log(' ✓ Removed cached file');
|
|
390
|
+
console.log(`\n✓ Package removed: ${name}`);
|
|
391
|
+
} else {
|
|
392
|
+
console.log(` Package not found: ${name}`);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
} catch (err) {
|
|
396
|
+
console.error('Error removing package:', err instanceof Error ? err.message : err);
|
|
397
|
+
process.exit(1);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Package upgrade
|
|
402
|
+
packageCmd
|
|
403
|
+
.command('upgrade')
|
|
404
|
+
.description('Upgrade a package to latest version')
|
|
405
|
+
.argument('[name]', 'Package ID to upgrade (omit for all)')
|
|
406
|
+
.option('-d, --dir <directory>', 'Workspace directory', '.')
|
|
407
|
+
.option('--all', 'Upgrade all packages')
|
|
408
|
+
.action(async (name: string | undefined, options) => {
|
|
409
|
+
try {
|
|
410
|
+
const workspaceRoot = resolve(options.dir);
|
|
411
|
+
|
|
412
|
+
if (options.all || !name) {
|
|
413
|
+
// Upgrade all packages
|
|
414
|
+
console.log('\nChecking for updates...\n');
|
|
415
|
+
|
|
416
|
+
const { packages } = await listPackages(workspaceRoot);
|
|
417
|
+
let upgraded = 0;
|
|
418
|
+
|
|
419
|
+
for (const pkg of packages) {
|
|
420
|
+
try {
|
|
421
|
+
const result = await upgradePackage(workspaceRoot, pkg.id);
|
|
422
|
+
if (result.upgraded) {
|
|
423
|
+
console.log(` ✓ ${pkg.id}: ${result.oldVersion} → ${result.newVersion}`);
|
|
424
|
+
upgraded++;
|
|
425
|
+
} else {
|
|
426
|
+
console.log(` - ${pkg.id}: ${result.oldVersion} (up to date)`);
|
|
427
|
+
}
|
|
428
|
+
} catch (err) {
|
|
429
|
+
console.log(` ✗ ${pkg.id}: ${err instanceof Error ? err.message : err}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
console.log(`\n✓ Upgraded ${upgraded} package(s)`);
|
|
434
|
+
} else {
|
|
435
|
+
// Upgrade single package
|
|
436
|
+
console.log(`\nUpgrading package: ${name}\n`);
|
|
437
|
+
|
|
438
|
+
const result = await upgradePackage(workspaceRoot, name);
|
|
439
|
+
|
|
440
|
+
if (result.upgraded) {
|
|
441
|
+
console.log(` Old version: ${result.oldVersion}`);
|
|
442
|
+
console.log(` New version: ${result.newVersion}`);
|
|
443
|
+
console.log(`\n✓ Package upgraded: ${name}`);
|
|
444
|
+
} else {
|
|
445
|
+
console.log(` Already at latest version: ${result.newVersion}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
} catch (err) {
|
|
449
|
+
console.error('Error upgrading package:', err instanceof Error ? err.message : err);
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// Package list
|
|
455
|
+
packageCmd
|
|
456
|
+
.command('list')
|
|
457
|
+
.description('List installed packages')
|
|
458
|
+
.option('-d, --dir <directory>', 'Workspace directory', '.')
|
|
459
|
+
.action(async (options) => {
|
|
460
|
+
try {
|
|
461
|
+
const workspaceRoot = resolve(options.dir);
|
|
462
|
+
const result = await listPackages(workspaceRoot);
|
|
463
|
+
|
|
464
|
+
console.log('\nDependencies:');
|
|
465
|
+
if (result.packages.length === 0) {
|
|
466
|
+
console.log(' (none)');
|
|
467
|
+
} else {
|
|
468
|
+
for (const pkg of result.packages) {
|
|
469
|
+
console.log(` ${pkg.id.padEnd(20)} ${pkg.version.padEnd(12)} ${pkg.provider.padEnd(10)} ${pkg.cached}`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
console.log(`\nTotal: ${result.packages.length} dependency(s)`);
|
|
474
|
+
} catch (err) {
|
|
475
|
+
console.error('Error listing packages:', err instanceof Error ? err.message : err);
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Package info
|
|
481
|
+
packageCmd
|
|
482
|
+
.command('info')
|
|
483
|
+
.description('Show package details')
|
|
484
|
+
.argument('<name>', 'Package ID')
|
|
485
|
+
.option('-d, --dir <directory>', 'Workspace directory', '.')
|
|
486
|
+
.action(async (name: string, options) => {
|
|
487
|
+
try {
|
|
488
|
+
const workspaceRoot = resolve(options.dir);
|
|
489
|
+
const pkg = await getPackageInfo(workspaceRoot, name);
|
|
490
|
+
|
|
491
|
+
if (!pkg) {
|
|
492
|
+
console.log(`Package not found: ${name}`);
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
console.log(`\nPackage: ${pkg.id}\n`);
|
|
497
|
+
console.log('| Field | Value |');
|
|
498
|
+
console.log('|-----------|-------|');
|
|
499
|
+
console.log(`| Source | ${pkg.source} |`);
|
|
500
|
+
console.log(`| Provider | ${pkg.provider} |`);
|
|
501
|
+
console.log(`| Cached | ${pkg.cached} |`);
|
|
502
|
+
console.log(`| Version | ${pkg.version} |`);
|
|
503
|
+
console.log(`| Fetched | ${pkg.fetched} |`);
|
|
504
|
+
if (pkg.integrity) {
|
|
505
|
+
console.log(`| Integrity | ${pkg.integrity} |`);
|
|
506
|
+
}
|
|
507
|
+
console.log('');
|
|
508
|
+
} catch (err) {
|
|
509
|
+
console.error('Error getting package info:', err instanceof Error ? err.message : err);
|
|
510
|
+
process.exit(1);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
program.parse();
|