busy-cli 0.2.0 → 0.3.0

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/cli/index.js CHANGED
@@ -310,14 +310,15 @@ const packageCmd = program
310
310
  // Package add
311
311
  packageCmd
312
312
  .command('add')
313
- .description('Add a package from URL')
314
- .argument('<url>', 'URL to the BUSY document')
313
+ .description('Add a package from URL or local folder')
314
+ .argument('<url>', 'URL or local path to the BUSY document or folder')
315
315
  .option('-d, --dir <directory>', 'Workspace directory', '.')
316
+ .option('-r, --recursive', 'Recursively add all files from a local folder')
316
317
  .action(async (url, options) => {
317
318
  try {
318
319
  const workspaceRoot = resolve(options.dir);
319
320
  console.log(`\nAdding package from: ${url}\n`);
320
- const result = await addPackage(workspaceRoot, url);
321
+ const result = await addPackage(workspaceRoot, url, { recursive: options.recursive });
321
322
  console.log(` ID: ${result.id}`);
322
323
  console.log(` Provider: ${result.provider}`);
323
324
  console.log(` Version: ${result.version}`);
@@ -71,12 +71,15 @@ export declare function checkWorkspace(workspaceRoot: string, options?: {
71
71
  skipExternal?: boolean;
72
72
  }): Promise<CheckResult>;
73
73
  /**
74
- * Add a package from URL
74
+ * Add a package from URL or local folder
75
75
  *
76
76
  * If the URL points to a package.busy.md manifest, fetches the entire package.
77
+ * If it's a local directory with --recursive (or without a manifest), copies all files.
77
78
  * Otherwise, fetches a single file.
78
79
  */
79
- export declare function addPackage(workspaceRoot: string, url: string): Promise<AddResult>;
80
+ export declare function addPackage(workspaceRoot: string, url: string, options?: {
81
+ recursive?: boolean;
82
+ }): Promise<AddResult>;
80
83
  /**
81
84
  * Remove a package
82
85
  */
@@ -1 +1 @@
1
- {"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../src/commands/package.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAmB,YAAY,EAAiC,MAAM,sBAAsB,CAAC;AAKpG,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,wBAAwB,CAAC;AAChC,OAAO,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkC9E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CA8CtH;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAoFvF;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwBnG;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAQ7E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAM3G;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA4ErG"}
1
+ {"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../src/commands/package.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAmB,YAAY,EAAiC,MAAM,sBAAsB,CAAC;AAKpG,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,wBAAwB,CAAC;AAChC,OAAO,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkC9E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CA8CtH;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAgH1H;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwBnG;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAQ7E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAM3G;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA4ErG"}
@@ -8,7 +8,7 @@ import * as path from 'node:path';
8
8
  import { CacheManager, deriveCachePath } from '../cache/index.js';
9
9
  import { PackageRegistry, deriveEntryId, deriveCategory } from '../registry/index.js';
10
10
  import { providerRegistry } from '../providers/index.js';
11
- import { isPackageManifestUrl, fetchPackageFromManifest } from '../package/manifest.js';
11
+ import { isPackageManifestUrl, fetchPackageFromManifest, fetchPackageFromLocalFolder } from '../package/manifest.js';
12
12
  // Ensure providers are registered
13
13
  import '../providers/local.js';
14
14
  import '../providers/github.js';
@@ -94,12 +94,40 @@ export async function checkWorkspace(workspaceRoot, options) {
94
94
  };
95
95
  }
96
96
  /**
97
- * Add a package from URL
97
+ * Add a package from URL or local folder
98
98
  *
99
99
  * If the URL points to a package.busy.md manifest, fetches the entire package.
100
+ * If it's a local directory with --recursive (or without a manifest), copies all files.
100
101
  * Otherwise, fetches a single file.
101
102
  */
102
- export async function addPackage(workspaceRoot, url) {
103
+ export async function addPackage(workspaceRoot, url, options) {
104
+ // Check if this is a local directory that should use folder-based discovery
105
+ if (url.startsWith('./') || url.startsWith('../') || url.startsWith('/') || (!url.includes('://') && !url.startsWith('http'))) {
106
+ const resolvedPath = path.isAbsolute(url) ? url : path.resolve(process.cwd(), url);
107
+ try {
108
+ const stat = await fs.stat(resolvedPath);
109
+ if (stat.isDirectory()) {
110
+ const manifestExists = await fs.stat(path.join(resolvedPath, 'package.busy.md'))
111
+ .then(() => true).catch(() => false);
112
+ if (options?.recursive || !manifestExists) {
113
+ // Use folder-based discovery: --recursive flag or no manifest available
114
+ const result = await fetchPackageFromLocalFolder(workspaceRoot, resolvedPath);
115
+ return {
116
+ id: result.name,
117
+ source: resolvedPath,
118
+ provider: 'local',
119
+ cached: result.cached,
120
+ version: result.version,
121
+ integrity: result.integrity,
122
+ };
123
+ }
124
+ // Has manifest and not --recursive: fall through to manifest-based flow
125
+ }
126
+ }
127
+ catch {
128
+ // Not a directory or doesn't exist - fall through to normal handling
129
+ }
130
+ }
103
131
  // Check if this is a package manifest URL
104
132
  if (isPackageManifestUrl(url)) {
105
133
  // Use manifest-based package installation
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { loadRepo } from './loader.js';
2
2
  export { buildContext, writeContext, get, parentsOf, childrenOf, getConceptContext } from './builders/context.js';
3
3
  export { mergeRepos, extendRepo, loadRepoFromJSON } from './merge.js';
4
- export type { DocId, Slug, Section, ConceptBase, BusyDocument, LocalDef, Operation, ImportDef, EdgeRole, Edge, File, Repo, ContextPayload, FrontMatter, } from './types/schema.js';
4
+ export type { DocId, Slug, Section, ConceptBase, BusyDocument, Playbook, View, Config, LocalDef, Operation, ImportDef, EdgeRole, Edge, File, Repo, ContextPayload, FrontMatter, } from './types/schema.js';
5
5
  export type { BuildOpts, ConceptContext } from './builders/context.js';
6
6
  export { DocIdSchema, SlugSchema, SectionIdSchema, ConceptIdSchema, SectionSchema, ConceptBaseSchema, LocalDefSchema, SetupSchema, OperationSchema, ImportDefSchema, BusyDocumentSchema, PlaybookSchema, EdgeRoleSchema, EdgeSchema, FileSchema, RepoSchema, ContextPayloadSchema, FrontMatterSchema, } from './types/schema.js';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGtE,YAAY,EACV,KAAK,EACL,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,cAAc,EACd,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EACL,WAAW,EACX,UAAU,EACV,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGtE,YAAY,EACV,KAAK,EACL,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,cAAc,EACd,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EACL,WAAW,EACX,UAAU,EACV,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,IAAI,EAUL,MAAM,mBAAmB,CAAC;AAS3B;;GAEG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+T7D"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,IAAI,EAYL,MAAM,mBAAmB,CAAC;AAgB3B;;GAEG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqU7D"}
package/dist/loader.js CHANGED
@@ -155,44 +155,52 @@ export async function loadRepo(globs) {
155
155
  }
156
156
  // Build final document structures with inline arrays
157
157
  for (const [docId, parts] of docParts) {
158
- const isPlaybook = parts.types.some((t) => t.toLowerCase() === 'playbook');
158
+ const typesLower = parts.types.map((t) => t.toLowerCase());
159
+ const isPlaybook = typesLower.includes('playbook');
160
+ const isView = typesLower.includes('view');
161
+ const isConfig = typesLower.includes('config');
162
+ // Base fields shared across all document kinds
163
+ const baseFields = {
164
+ id: parts.docId,
165
+ docId: parts.docId,
166
+ slug: parts.docId.toLowerCase(),
167
+ name: parts.frontmatter.Name,
168
+ content: parts.content,
169
+ types: parts.types,
170
+ extends: parts.extends,
171
+ sectionRef: `${parts.docId}#`, // Root reference
172
+ imports: parts.imports,
173
+ localdefs: parts.localdefs,
174
+ setup: parts.setup,
175
+ operations: parts.operations,
176
+ };
159
177
  if (isPlaybook) {
160
178
  // Extract sequence from ExecutePlaybook operation
161
179
  const sequence = extractPlaybookSequence(parts.sections);
180
+ const doc = { ...baseFields, kind: 'playbook', sequence };
181
+ docs.push(doc);
182
+ }
183
+ else if (isView) {
184
+ // Extract template section with full content (including children)
185
+ const displaySection = findSection(parts.sections, 'display');
186
+ let displayContent;
187
+ if (displaySection) {
188
+ // Reconstruct full template from section + children content
189
+ displayContent = getSectionFullContent(displaySection);
190
+ }
162
191
  const doc = {
163
- kind: 'playbook',
164
- id: parts.docId,
165
- docId: parts.docId,
166
- slug: parts.docId.toLowerCase(),
167
- name: parts.frontmatter.Name,
168
- content: parts.content,
169
- types: parts.types,
170
- extends: parts.extends,
171
- sectionRef: `${parts.docId}#`, // Root reference
172
- imports: parts.imports,
173
- localdefs: parts.localdefs,
174
- setup: parts.setup,
175
- operations: parts.operations,
176
- sequence,
192
+ ...baseFields,
193
+ kind: 'view',
194
+ display: displayContent,
177
195
  };
178
196
  docs.push(doc);
179
197
  }
198
+ else if (isConfig) {
199
+ const doc = { ...baseFields, kind: 'config' };
200
+ docs.push(doc);
201
+ }
180
202
  else {
181
- const doc = {
182
- kind: 'document',
183
- id: parts.docId,
184
- docId: parts.docId,
185
- slug: parts.docId.toLowerCase(),
186
- name: parts.frontmatter.Name,
187
- content: parts.content,
188
- types: parts.types,
189
- extends: parts.extends,
190
- sectionRef: `${parts.docId}#`, // Root reference
191
- imports: parts.imports,
192
- localdefs: parts.localdefs,
193
- setup: parts.setup,
194
- operations: parts.operations,
195
- };
203
+ const doc = { ...baseFields, kind: 'document' };
196
204
  docs.push(doc);
197
205
  }
198
206
  }
@@ -332,6 +340,18 @@ function resolveSymbol(nameOrLabel, currentDocId, localdefs, docs, symbols) {
332
340
  }
333
341
  return undefined;
334
342
  }
343
+ /**
344
+ * Get the full content of a section including all nested children.
345
+ * Reconstructs the original markdown by walking the section tree.
346
+ */
347
+ function getSectionFullContent(section) {
348
+ let content = section.content;
349
+ for (const child of section.children) {
350
+ const prefix = '#'.repeat(child.depth);
351
+ content += `\n${prefix} ${child.title}\n${getSectionFullContent(child)}`;
352
+ }
353
+ return content.trim();
354
+ }
335
355
  /**
336
356
  * Extract sequence of operations from a playbook's ExecutePlaybook operation
337
357
  * Looks for sections with "Step" in the title and extracts Target metadata
@@ -56,4 +56,16 @@ export declare function parsePackageManifest(content: string): PackageManifest;
56
56
  * Fetch a package from its manifest URL or local path
57
57
  */
58
58
  export declare function fetchPackageFromManifest(workspaceRoot: string, manifestUrl: string): Promise<FetchPackageResult>;
59
+ /**
60
+ * Recursively discover all files in a directory.
61
+ * Skips hidden directories (e.g. .git, .libraries) and node_modules.
62
+ */
63
+ export declare function discoverFiles(dirPath: string): Promise<string[]>;
64
+ /**
65
+ * Fetch a package from a local folder, copying all files.
66
+ *
67
+ * If a package.busy.md exists, its metadata (name, version, description) is used.
68
+ * Otherwise, metadata is derived from the folder name.
69
+ */
70
+ export declare function fetchPackageFromLocalFolder(workspaceRoot: string, folderPath: string): Promise<FetchPackageResult>;
59
71
  //# sourceMappingURL=manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/package/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,wBAAwB,CAAC;AAChC,OAAO,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAazD;AAyBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAiHrE;AAkBD;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC,CA4G7B"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/package/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,wBAAwB,CAAC;AAChC,OAAO,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAazD;AAyBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAiHrE;AAkBD;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC,CA4G7B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBtE;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAqG7B"}
@@ -262,4 +262,122 @@ export async function fetchPackageFromManifest(workspaceRoot, manifestUrl) {
262
262
  integrity,
263
263
  };
264
264
  }
265
+ /**
266
+ * Recursively discover all files in a directory.
267
+ * Skips hidden directories (e.g. .git, .libraries) and node_modules.
268
+ */
269
+ export async function discoverFiles(dirPath) {
270
+ const results = [];
271
+ async function walk(dir) {
272
+ const entries = await fs.readdir(dir, { withFileTypes: true });
273
+ for (const entry of entries) {
274
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') {
275
+ continue;
276
+ }
277
+ const fullPath = path.join(dir, entry.name);
278
+ if (entry.isDirectory()) {
279
+ await walk(fullPath);
280
+ }
281
+ else if (entry.isFile()) {
282
+ results.push(fullPath);
283
+ }
284
+ }
285
+ }
286
+ await walk(dirPath);
287
+ return results;
288
+ }
289
+ /**
290
+ * Fetch a package from a local folder, copying all files.
291
+ *
292
+ * If a package.busy.md exists, its metadata (name, version, description) is used.
293
+ * Otherwise, metadata is derived from the folder name.
294
+ */
295
+ export async function fetchPackageFromLocalFolder(workspaceRoot, folderPath) {
296
+ const absolutePath = path.isAbsolute(folderPath)
297
+ ? folderPath
298
+ : path.resolve(process.cwd(), folderPath);
299
+ // Verify it's a directory
300
+ const stat = await fs.stat(absolutePath);
301
+ if (!stat.isDirectory()) {
302
+ throw new Error(`Not a directory: ${absolutePath}`);
303
+ }
304
+ // Try to read manifest for metadata
305
+ const manifestPath = path.join(absolutePath, 'package.busy.md');
306
+ let manifest = null;
307
+ let manifestContent = null;
308
+ try {
309
+ manifestContent = await fs.readFile(manifestPath, 'utf-8');
310
+ manifest = parsePackageManifest(manifestContent);
311
+ }
312
+ catch {
313
+ // No manifest - that's fine
314
+ }
315
+ const packageName = manifest?.name || path.basename(absolutePath);
316
+ // Discover all files in the folder
317
+ const discoveredFiles = await discoverFiles(absolutePath);
318
+ // Filter out package.busy.md itself (handled separately)
319
+ const filesToCopy = discoveredFiles
320
+ .filter(f => path.basename(f) !== 'package.busy.md')
321
+ .map(f => ({
322
+ relativePath: path.relative(absolutePath, f),
323
+ absolutePath: f,
324
+ }));
325
+ // Initialize cache
326
+ const cache = new CacheManager(workspaceRoot);
327
+ await cache.init();
328
+ // Copy files
329
+ let combinedContent = '';
330
+ const documents = [];
331
+ for (const file of filesToCopy) {
332
+ try {
333
+ const content = await fs.readFile(file.absolutePath, 'utf-8');
334
+ combinedContent += content;
335
+ const cachePath = path.join(packageName, file.relativePath);
336
+ await cache.save(cachePath, content);
337
+ documents.push({
338
+ name: path.basename(file.relativePath, path.extname(file.relativePath)),
339
+ relativePath: './' + file.relativePath,
340
+ });
341
+ }
342
+ catch (error) {
343
+ console.warn(`Warning: Failed to read ${file.absolutePath}: ${error}`);
344
+ }
345
+ }
346
+ // Save manifest if it exists
347
+ if (manifestContent) {
348
+ await cache.save(path.join(packageName, 'package.busy.md'), manifestContent);
349
+ }
350
+ // Calculate integrity
351
+ const integrity = calculateIntegrity(combinedContent);
352
+ // Add to registry
353
+ const registry = new PackageRegistry(workspaceRoot);
354
+ try {
355
+ await registry.load();
356
+ }
357
+ catch {
358
+ await registry.init();
359
+ await registry.load();
360
+ }
361
+ const entry = {
362
+ id: packageName,
363
+ description: manifest?.description || '',
364
+ source: absolutePath,
365
+ provider: 'local',
366
+ cached: `.libraries/${packageName}`,
367
+ version: manifest?.version || 'latest',
368
+ fetched: new Date().toISOString(),
369
+ integrity,
370
+ category: 'Packages',
371
+ };
372
+ registry.addPackage(entry);
373
+ await registry.save();
374
+ return {
375
+ name: packageName,
376
+ version: manifest?.version || 'latest',
377
+ description: manifest?.description || '',
378
+ documents,
379
+ cached: `.libraries/${packageName}`,
380
+ integrity,
381
+ };
382
+ }
265
383
  //# sourceMappingURL=manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EACL,eAAe,IAAI,YAAY,EAAG,8CAA8C;AAChF,YAAY,EAGb,MAAM,mBAAmB,CAAC;AA6G3B;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,YAAY,CAiE1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,GAAG,YAAY,EACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,GAC/B,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAAC,CAuD7C"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EACL,eAAe,IAAI,YAAY,EAAG,8CAA8C;AAChF,YAAY,EAGb,MAAM,mBAAmB,CAAC;AA6G3B;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,YAAY,CAiE1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,GAAG,YAAY,EACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,GAC/B,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAAC,CA2D7C"}
package/dist/parser.js CHANGED
@@ -170,13 +170,15 @@ export function resolveImports(document, basePath, visited = new Set()) {
170
170
  for (const imp of document.imports) {
171
171
  // Resolve the import path
172
172
  const importPath = resolve(dirname(basePath), imp.path);
173
- // Check for circular imports
173
+ // Check for circular imports — warn and skip instead of crashing
174
174
  if (visited.has(importPath)) {
175
- throw new Error(`Circular import detected: ${importPath}`);
175
+ console.warn(`⚠ Circular import skipped: ${imp.path} (from ${basePath})`);
176
+ continue;
176
177
  }
177
- // Check if file exists
178
+ // Check if file exists — warn and skip instead of crashing
178
179
  if (!existsSync(importPath)) {
179
- throw new Error(`Import not found: ${imp.path} (resolved to ${importPath})`);
180
+ console.warn(`⚠ Import not found: ${imp.path} (resolved to ${importPath})`);
181
+ continue;
180
182
  }
181
183
  // Mark as visited
182
184
  visited.add(importPath);
@@ -192,7 +194,7 @@ export function resolveImports(document, basePath, visited = new Set()) {
192
194
  const hasDefinition = importedDoc.definitions.some((def) => def.name.toLowerCase() === imp.anchor.toLowerCase() ||
193
195
  slugify(def.name) === imp.anchor.toLowerCase());
194
196
  if (!hasOperation && !hasDefinition) {
195
- throw new Error(`Anchor '${imp.anchor}' not found in ${imp.path}`);
197
+ console.warn(`⚠ Anchor '${imp.anchor}' not found in ${imp.path}`);
196
198
  }
197
199
  }
198
200
  // Store resolved document
@@ -201,10 +203,12 @@ export function resolveImports(document, basePath, visited = new Set()) {
201
203
  const nestedResolved = resolveImports(importedDoc, importPath, visited);
202
204
  Object.assign(resolved, nestedResolved);
203
205
  }
204
- finally {
205
- // Remove from visited after processing (allow same doc in different branches)
206
- visited.delete(importPath);
206
+ catch (e) {
207
+ // Don't crash on nested resolution failures
208
+ console.warn(`⚠ Failed to resolve nested imports in ${imp.path}: ${e}`);
207
209
  }
210
+ // NOTE: we intentionally keep importPath in `visited` — once resolved,
211
+ // don't re-resolve in other branches (prevents exponential recursion)
208
212
  }
209
213
  return resolved;
210
214
  }