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.
Files changed (128) hide show
  1. package/README.md +129 -0
  2. package/dist/builders/context.d.ts +50 -0
  3. package/dist/builders/context.d.ts.map +1 -0
  4. package/dist/builders/context.js +190 -0
  5. package/dist/cache/index.d.ts +100 -0
  6. package/dist/cache/index.d.ts.map +1 -0
  7. package/dist/cache/index.js +270 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/cli/index.js +463 -0
  11. package/dist/commands/package.d.ts +96 -0
  12. package/dist/commands/package.d.ts.map +1 -0
  13. package/dist/commands/package.js +285 -0
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +7 -0
  17. package/dist/loader.d.ts +6 -0
  18. package/dist/loader.d.ts.map +1 -0
  19. package/dist/loader.js +361 -0
  20. package/dist/merge.d.ts +16 -0
  21. package/dist/merge.d.ts.map +1 -0
  22. package/dist/merge.js +102 -0
  23. package/dist/package/manifest.d.ts +59 -0
  24. package/dist/package/manifest.d.ts.map +1 -0
  25. package/dist/package/manifest.js +265 -0
  26. package/dist/parser.d.ts +28 -0
  27. package/dist/parser.d.ts.map +1 -0
  28. package/dist/parser.js +220 -0
  29. package/dist/parsers/frontmatter.d.ts +14 -0
  30. package/dist/parsers/frontmatter.d.ts.map +1 -0
  31. package/dist/parsers/frontmatter.js +110 -0
  32. package/dist/parsers/imports.d.ts +48 -0
  33. package/dist/parsers/imports.d.ts.map +1 -0
  34. package/dist/parsers/imports.js +147 -0
  35. package/dist/parsers/links.d.ts +12 -0
  36. package/dist/parsers/links.d.ts.map +1 -0
  37. package/dist/parsers/links.js +79 -0
  38. package/dist/parsers/localdefs.d.ts +6 -0
  39. package/dist/parsers/localdefs.d.ts.map +1 -0
  40. package/dist/parsers/localdefs.js +132 -0
  41. package/dist/parsers/operations.d.ts +32 -0
  42. package/dist/parsers/operations.d.ts.map +1 -0
  43. package/dist/parsers/operations.js +313 -0
  44. package/dist/parsers/sections.d.ts +15 -0
  45. package/dist/parsers/sections.d.ts.map +1 -0
  46. package/dist/parsers/sections.js +173 -0
  47. package/dist/parsers/tools.d.ts +30 -0
  48. package/dist/parsers/tools.d.ts.map +1 -0
  49. package/dist/parsers/tools.js +178 -0
  50. package/dist/parsers/triggers.d.ts +35 -0
  51. package/dist/parsers/triggers.d.ts.map +1 -0
  52. package/dist/parsers/triggers.js +219 -0
  53. package/dist/providers/base.d.ts +60 -0
  54. package/dist/providers/base.d.ts.map +1 -0
  55. package/dist/providers/base.js +34 -0
  56. package/dist/providers/github.d.ts +18 -0
  57. package/dist/providers/github.d.ts.map +1 -0
  58. package/dist/providers/github.js +109 -0
  59. package/dist/providers/gitlab.d.ts +18 -0
  60. package/dist/providers/gitlab.d.ts.map +1 -0
  61. package/dist/providers/gitlab.js +101 -0
  62. package/dist/providers/index.d.ts +13 -0
  63. package/dist/providers/index.d.ts.map +1 -0
  64. package/dist/providers/index.js +17 -0
  65. package/dist/providers/local.d.ts +31 -0
  66. package/dist/providers/local.d.ts.map +1 -0
  67. package/dist/providers/local.js +116 -0
  68. package/dist/providers/url.d.ts +16 -0
  69. package/dist/providers/url.d.ts.map +1 -0
  70. package/dist/providers/url.js +45 -0
  71. package/dist/registry/index.d.ts +99 -0
  72. package/dist/registry/index.d.ts.map +1 -0
  73. package/dist/registry/index.js +320 -0
  74. package/dist/types/schema.d.ts +3259 -0
  75. package/dist/types/schema.d.ts.map +1 -0
  76. package/dist/types/schema.js +258 -0
  77. package/dist/utils/logger.d.ts +19 -0
  78. package/dist/utils/logger.d.ts.map +1 -0
  79. package/dist/utils/logger.js +23 -0
  80. package/dist/utils/slugify.d.ts +14 -0
  81. package/dist/utils/slugify.d.ts.map +1 -0
  82. package/dist/utils/slugify.js +28 -0
  83. package/package.json +61 -0
  84. package/src/__tests__/cache.test.ts +393 -0
  85. package/src/__tests__/cli-package.test.ts +667 -0
  86. package/src/__tests__/fixtures/automated-workflow.busy.md +84 -0
  87. package/src/__tests__/fixtures/concept.busy.md +30 -0
  88. package/src/__tests__/fixtures/document.busy.md +44 -0
  89. package/src/__tests__/fixtures/simple-operation.busy.md +45 -0
  90. package/src/__tests__/fixtures/tool-document.busy.md +71 -0
  91. package/src/__tests__/fixtures/tool.busy.md +54 -0
  92. package/src/__tests__/imports.test.ts +244 -0
  93. package/src/__tests__/integration.test.ts +432 -0
  94. package/src/__tests__/operations.test.ts +408 -0
  95. package/src/__tests__/package-manifest.test.ts +455 -0
  96. package/src/__tests__/providers.test.ts +672 -0
  97. package/src/__tests__/registry.test.ts +402 -0
  98. package/src/__tests__/schema.test.ts +467 -0
  99. package/src/__tests__/tools.test.ts +376 -0
  100. package/src/__tests__/triggers.test.ts +312 -0
  101. package/src/builders/context.ts +294 -0
  102. package/src/cache/index.ts +312 -0
  103. package/src/cli/index.ts +514 -0
  104. package/src/commands/package.ts +392 -0
  105. package/src/index.ts +46 -0
  106. package/src/loader.ts +474 -0
  107. package/src/merge.ts +126 -0
  108. package/src/package/manifest.ts +349 -0
  109. package/src/parser.ts +278 -0
  110. package/src/parsers/frontmatter.ts +135 -0
  111. package/src/parsers/imports.ts +196 -0
  112. package/src/parsers/links.ts +108 -0
  113. package/src/parsers/localdefs.ts +166 -0
  114. package/src/parsers/operations.ts +404 -0
  115. package/src/parsers/sections.ts +230 -0
  116. package/src/parsers/tools.ts +215 -0
  117. package/src/parsers/triggers.ts +252 -0
  118. package/src/providers/base.ts +77 -0
  119. package/src/providers/github.ts +129 -0
  120. package/src/providers/gitlab.ts +121 -0
  121. package/src/providers/index.ts +25 -0
  122. package/src/providers/local.ts +129 -0
  123. package/src/providers/url.ts +56 -0
  124. package/src/registry/index.ts +408 -0
  125. package/src/types/schema.ts +369 -0
  126. package/src/utils/logger.ts +25 -0
  127. package/src/utils/slugify.ts +31 -0
  128. package/tsconfig.json +21 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Package Management Commands
3
+ *
4
+ * Implementation of busy init, check, and package commands.
5
+ */
6
+
7
+ import { promises as fs } from 'node:fs';
8
+ import * as path from 'node:path';
9
+ import { CacheManager, calculateIntegrity, deriveCachePath } from '../cache/index.js';
10
+ import { PackageRegistry, PackageEntry, deriveEntryId, deriveCategory } from '../registry/index.js';
11
+ import { providerRegistry } from '../providers/index.js';
12
+ import { isPackageManifestUrl, fetchPackageFromManifest } from '../package/manifest.js';
13
+
14
+ // Ensure providers are registered
15
+ import '../providers/local.js';
16
+ import '../providers/github.js';
17
+ import '../providers/gitlab.js';
18
+ import '../providers/url.js';
19
+
20
+ /**
21
+ * Result of initWorkspace
22
+ */
23
+ export interface InitResult {
24
+ workspaceRoot: string;
25
+ initialized: boolean;
26
+ created: string[];
27
+ skipped: string[];
28
+ }
29
+
30
+ /**
31
+ * Result of checkWorkspace
32
+ */
33
+ export interface CheckResult {
34
+ workspaceRoot: string;
35
+ valid: boolean;
36
+ errors: string[];
37
+ warnings: string[];
38
+ packages: number;
39
+ }
40
+
41
+ /**
42
+ * Result of addPackage
43
+ */
44
+ export interface AddResult {
45
+ id: string;
46
+ source: string;
47
+ provider: string;
48
+ cached: string;
49
+ version: string;
50
+ integrity: string;
51
+ }
52
+
53
+ /**
54
+ * Result of removePackage
55
+ */
56
+ export interface RemoveResult {
57
+ id: string;
58
+ removed: boolean;
59
+ }
60
+
61
+ /**
62
+ * Result of listPackages
63
+ */
64
+ export interface ListResult {
65
+ packages: PackageEntry[];
66
+ }
67
+
68
+ /**
69
+ * Result of upgradePackage
70
+ */
71
+ export interface UpgradeResult {
72
+ id: string;
73
+ upgraded: boolean;
74
+ oldVersion: string;
75
+ newVersion: string;
76
+ }
77
+
78
+ /**
79
+ * Initialize a BUSY workspace
80
+ */
81
+ export async function initWorkspace(workspaceRoot: string): Promise<InitResult> {
82
+ const created: string[] = [];
83
+ const skipped: string[] = [];
84
+
85
+ // Initialize package registry
86
+ const registry = new PackageRegistry(workspaceRoot);
87
+ const packagePath = path.join(workspaceRoot, 'package.busy.md');
88
+
89
+ const packageExists = await fs.stat(packagePath).then(() => true).catch(() => false);
90
+ if (packageExists) {
91
+ skipped.push('package.busy.md');
92
+ } else {
93
+ await registry.init();
94
+ created.push('package.busy.md');
95
+ }
96
+
97
+ // Initialize cache manager
98
+ const cache = new CacheManager(workspaceRoot);
99
+ const librariesPath = path.join(workspaceRoot, '.libraries');
100
+
101
+ const librariesExists = await fs.stat(librariesPath).then(() => true).catch(() => false);
102
+ if (librariesExists) {
103
+ skipped.push('.libraries');
104
+ } else {
105
+ await cache.init();
106
+ created.push('.libraries');
107
+ }
108
+
109
+ return {
110
+ workspaceRoot,
111
+ initialized: true,
112
+ created,
113
+ skipped,
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Check workspace coherence
119
+ */
120
+ export async function checkWorkspace(workspaceRoot: string, options?: { skipExternal?: boolean }): Promise<CheckResult> {
121
+ const errors: string[] = [];
122
+ const warnings: string[] = [];
123
+
124
+ // Load registry
125
+ const registry = new PackageRegistry(workspaceRoot);
126
+ try {
127
+ await registry.load();
128
+ } catch (error) {
129
+ throw new Error(`Workspace not initialized: ${error instanceof Error ? error.message : error}`);
130
+ }
131
+
132
+ const cache = new CacheManager(workspaceRoot);
133
+ const packages = registry.getPackages();
134
+
135
+ // Check each package
136
+ for (const pkg of packages) {
137
+ // Check if cached file exists
138
+ const cachePath = pkg.cached.startsWith('.libraries/')
139
+ ? pkg.cached.slice('.libraries/'.length)
140
+ : pkg.cached;
141
+
142
+ const fullPath = path.join(workspaceRoot, pkg.cached);
143
+ const exists = await fs.stat(fullPath).then(() => true).catch(() => false);
144
+
145
+ if (!exists) {
146
+ errors.push(`Package "${pkg.id}": cached file not found at ${pkg.cached}`);
147
+ continue;
148
+ }
149
+
150
+ // Check integrity if specified
151
+ if (pkg.integrity) {
152
+ const isValid = await cache.verifyIntegrity(cachePath, pkg.integrity);
153
+ if (!isValid) {
154
+ warnings.push(`Package "${pkg.id}": integrity mismatch`);
155
+ }
156
+ }
157
+ }
158
+
159
+ return {
160
+ workspaceRoot,
161
+ valid: errors.length === 0,
162
+ errors,
163
+ warnings,
164
+ packages: packages.length,
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Add a package from URL
170
+ *
171
+ * If the URL points to a package.busy.md manifest, fetches the entire package.
172
+ * Otherwise, fetches a single file.
173
+ */
174
+ export async function addPackage(workspaceRoot: string, url: string): Promise<AddResult> {
175
+ // Check if this is a package manifest URL
176
+ if (isPackageManifestUrl(url)) {
177
+ // Use manifest-based package installation
178
+ const result = await fetchPackageFromManifest(workspaceRoot, url);
179
+
180
+ // Find provider for URL to get provider name and resolve source path
181
+ const provider = providerRegistry.findProvider(url);
182
+ const parsed = provider?.parse(url);
183
+
184
+ // Use absolute path for local sources, original URL for remote
185
+ const resolvedSource = provider?.name === 'local' && parsed
186
+ ? parsed.path
187
+ : url;
188
+
189
+ return {
190
+ id: result.name,
191
+ source: resolvedSource,
192
+ provider: provider?.name || 'url',
193
+ cached: result.cached,
194
+ version: result.version,
195
+ integrity: result.integrity,
196
+ };
197
+ }
198
+
199
+ // Single file installation (existing behavior)
200
+ const provider = providerRegistry.findProvider(url);
201
+ if (!provider) {
202
+ throw new Error(`No provider found for URL: ${url}`);
203
+ }
204
+
205
+ // Parse URL
206
+ const parsed = provider.parse(url);
207
+
208
+ // Derive entry ID
209
+ const entryId = deriveEntryId(url);
210
+
211
+ // Derive cache path
212
+ const cachePath = deriveCachePath(parsed);
213
+
214
+ // Fetch content
215
+ const content = await provider.fetch(url);
216
+
217
+ // Save to cache
218
+ const cache = new CacheManager(workspaceRoot);
219
+ await cache.init();
220
+ const saved = await cache.save(cachePath, content);
221
+
222
+ // Derive category
223
+ const category = deriveCategory(url);
224
+
225
+ // Create package entry
226
+ const entry: PackageEntry = {
227
+ id: entryId,
228
+ description: '',
229
+ source: url,
230
+ provider: provider.name,
231
+ cached: `.libraries/${cachePath}`,
232
+ version: parsed.ref || 'latest',
233
+ fetched: new Date().toISOString(),
234
+ integrity: saved.integrity,
235
+ category,
236
+ };
237
+
238
+ // Load registry and add package
239
+ const registry = new PackageRegistry(workspaceRoot);
240
+ try {
241
+ await registry.load();
242
+ } catch {
243
+ await registry.init();
244
+ await registry.load();
245
+ }
246
+
247
+ registry.addPackage(entry);
248
+ await registry.save();
249
+
250
+ return {
251
+ id: entryId,
252
+ source: url,
253
+ provider: provider.name,
254
+ cached: entry.cached,
255
+ version: entry.version,
256
+ integrity: saved.integrity,
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Remove a package
262
+ */
263
+ export async function removePackage(workspaceRoot: string, packageId: string): Promise<RemoveResult> {
264
+ // Load registry
265
+ const registry = new PackageRegistry(workspaceRoot);
266
+ await registry.load();
267
+
268
+ // Get package info before removing
269
+ const pkg = registry.getPackage(packageId);
270
+ if (!pkg) {
271
+ return { id: packageId, removed: false };
272
+ }
273
+
274
+ // Remove from registry
275
+ registry.removePackage(packageId);
276
+ await registry.save();
277
+
278
+ // Remove cached file
279
+ const cache = new CacheManager(workspaceRoot);
280
+ const cachePath = pkg.cached.startsWith('.libraries/')
281
+ ? pkg.cached.slice('.libraries/'.length)
282
+ : pkg.cached;
283
+
284
+ await cache.delete(cachePath);
285
+
286
+ return { id: packageId, removed: true };
287
+ }
288
+
289
+ /**
290
+ * List all packages
291
+ */
292
+ export async function listPackages(workspaceRoot: string): Promise<ListResult> {
293
+ // Load registry
294
+ const registry = new PackageRegistry(workspaceRoot);
295
+ await registry.load();
296
+
297
+ const packages = registry.getPackages();
298
+
299
+ return { packages };
300
+ }
301
+
302
+ /**
303
+ * Get package info
304
+ */
305
+ export async function getPackageInfo(workspaceRoot: string, packageId: string): Promise<PackageEntry | null> {
306
+ // Load registry
307
+ const registry = new PackageRegistry(workspaceRoot);
308
+ await registry.load();
309
+
310
+ return registry.getPackage(packageId) || null;
311
+ }
312
+
313
+ /**
314
+ * Upgrade a package to latest version
315
+ */
316
+ export async function upgradePackage(workspaceRoot: string, packageId: string): Promise<UpgradeResult> {
317
+ // Load registry
318
+ const registry = new PackageRegistry(workspaceRoot);
319
+ await registry.load();
320
+
321
+ // Get current package
322
+ const pkg = registry.getPackage(packageId);
323
+ if (!pkg) {
324
+ throw new Error(`Package not found: ${packageId}`);
325
+ }
326
+
327
+ // Find provider
328
+ const provider = providerRegistry.findProvider(pkg.source);
329
+ if (!provider) {
330
+ throw new Error(`No provider found for package source: ${pkg.source}`);
331
+ }
332
+
333
+ // Parse current URL
334
+ const parsed = provider.parse(pkg.source);
335
+
336
+ // Get latest version
337
+ let latestVersion: string;
338
+ try {
339
+ if (provider.getLatestVersion) {
340
+ latestVersion = await provider.getLatestVersion(parsed);
341
+ } else {
342
+ throw new Error('Provider does not support version resolution');
343
+ }
344
+ } catch (error) {
345
+ throw new Error(`Failed to get latest version: ${error instanceof Error ? error.message : error}`);
346
+ }
347
+
348
+ const oldVersion = pkg.version;
349
+
350
+ // Check if already at latest
351
+ if (latestVersion === oldVersion) {
352
+ return {
353
+ id: packageId,
354
+ upgraded: false,
355
+ oldVersion,
356
+ newVersion: latestVersion,
357
+ };
358
+ }
359
+
360
+ // Build new URL with latest version
361
+ const newUrl = pkg.source.replace(oldVersion, latestVersion);
362
+
363
+ // Fetch new content
364
+ const content = await provider.fetch(newUrl);
365
+
366
+ // Save to cache
367
+ const cache = new CacheManager(workspaceRoot);
368
+ const cachePath = pkg.cached.startsWith('.libraries/')
369
+ ? pkg.cached.slice('.libraries/'.length)
370
+ : pkg.cached;
371
+
372
+ const saved = await cache.save(cachePath, content);
373
+
374
+ // Update package entry
375
+ const updatedEntry: PackageEntry = {
376
+ ...pkg,
377
+ source: newUrl,
378
+ version: latestVersion,
379
+ fetched: new Date().toISOString(),
380
+ integrity: saved.integrity,
381
+ };
382
+
383
+ registry.addPackage(updatedEntry);
384
+ await registry.save();
385
+
386
+ return {
387
+ id: packageId,
388
+ upgraded: true,
389
+ oldVersion,
390
+ newVersion: latestVersion,
391
+ };
392
+ }
package/src/index.ts ADDED
@@ -0,0 +1,46 @@
1
+ // Main exports
2
+ export { loadRepo } from './loader.js';
3
+ export { buildContext, writeContext, get, parentsOf, childrenOf, getConceptContext } from './builders/context.js';
4
+ export { mergeRepos, extendRepo, loadRepoFromJSON } from './merge.js';
5
+
6
+ // Type exports
7
+ export type {
8
+ DocId,
9
+ Slug,
10
+ Section,
11
+ ConceptBase,
12
+ BusyDocument,
13
+ LocalDef,
14
+ Operation,
15
+ ImportDef,
16
+ EdgeRole,
17
+ Edge,
18
+ File,
19
+ Repo,
20
+ ContextPayload,
21
+ FrontMatter,
22
+ } from './types/schema.js';
23
+
24
+ export type { BuildOpts, ConceptContext } from './builders/context.js';
25
+
26
+ // Zod Schema exports for validation in other repos
27
+ export {
28
+ DocIdSchema,
29
+ SlugSchema,
30
+ SectionIdSchema,
31
+ ConceptIdSchema,
32
+ SectionSchema,
33
+ ConceptBaseSchema,
34
+ LocalDefSchema,
35
+ SetupSchema,
36
+ OperationSchema,
37
+ ImportDefSchema,
38
+ BusyDocumentSchema,
39
+ PlaybookSchema,
40
+ EdgeRoleSchema,
41
+ EdgeSchema,
42
+ FileSchema,
43
+ RepoSchema,
44
+ ContextPayloadSchema,
45
+ FrontMatterSchema,
46
+ } from './types/schema.js';