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,313 @@
1
+ import { findSection, getSectionExtends } from './sections.js';
2
+ import { debug } from '../utils/logger.js';
3
+ const OPERATIONS_SECTION_ALIASES = [
4
+ 'operations',
5
+ 'operations-section',
6
+ ];
7
+ // =============================================================================
8
+ // NEW PARSER FUNCTIONS - busy-python compatible
9
+ // =============================================================================
10
+ /**
11
+ * Parse numbered steps from markdown content
12
+ * Returns Step objects with stepNumber, instruction, and operationReferences
13
+ *
14
+ * @param content - Markdown content to parse
15
+ * @returns Array of Step objects
16
+ */
17
+ export function parseSteps(content) {
18
+ const steps = [];
19
+ // Find Steps section if present
20
+ const stepsMatch = content.match(/###\s*\[?Steps\]?\s*\n([\s\S]*?)(?=\n###|\n##|$)/i);
21
+ const textToParse = stepsMatch ? stepsMatch[1] : content;
22
+ // Split content by lines and process
23
+ const lines = textToParse.split('\n');
24
+ let currentStepNumber = 0;
25
+ let currentInstruction = '';
26
+ for (const line of lines) {
27
+ const trimmed = line.trim();
28
+ // Check for numbered step start: "1. instruction"
29
+ const stepMatch = trimmed.match(/^(\d+)\.\s+(.+)$/);
30
+ if (stepMatch) {
31
+ // Save previous step if exists
32
+ if (currentStepNumber > 0 && currentInstruction) {
33
+ steps.push(createStepObject(currentStepNumber, currentInstruction));
34
+ }
35
+ currentStepNumber = parseInt(stepMatch[1], 10);
36
+ currentInstruction = stepMatch[2];
37
+ }
38
+ else if (currentStepNumber > 0 && trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('-') && !trimmed.startsWith('*')) {
39
+ // Continuation of current step (indented or regular text)
40
+ currentInstruction += ' ' + trimmed;
41
+ }
42
+ }
43
+ // Add final step
44
+ if (currentStepNumber > 0 && currentInstruction) {
45
+ steps.push(createStepObject(currentStepNumber, currentInstruction));
46
+ }
47
+ return steps;
48
+ }
49
+ /**
50
+ * Helper to create a Step object with operation references extracted
51
+ */
52
+ function createStepObject(stepNumber, instruction) {
53
+ instruction = instruction.trim();
54
+ // Extract operation references: [OperationName]
55
+ const operationReferences = [];
56
+ const refPattern = /\[([^\]]+)\]/g;
57
+ let refMatch;
58
+ while ((refMatch = refPattern.exec(instruction)) !== null) {
59
+ // Skip if it looks like a markdown link [text](url)
60
+ const afterBracket = instruction.slice(refMatch.index + refMatch[0].length);
61
+ if (!afterBracket.startsWith('(')) {
62
+ operationReferences.push(refMatch[1]);
63
+ }
64
+ }
65
+ return {
66
+ stepNumber,
67
+ instruction,
68
+ operationReferences: operationReferences.length > 0 ? operationReferences : undefined,
69
+ };
70
+ }
71
+ /**
72
+ * Parse checklist items from markdown content
73
+ * Returns Checklist object with items array, or null if no checklist found
74
+ *
75
+ * @param content - Markdown content to parse
76
+ * @returns Checklist object or null
77
+ */
78
+ export function parseChecklist(content) {
79
+ // Find Checklist section
80
+ const checklistMatch = content.match(/###\s*\[?Checklist\]?\s*\n([\s\S]*?)(?=\n###|\n##|$)/i);
81
+ if (!checklistMatch) {
82
+ return null;
83
+ }
84
+ const checklistContent = checklistMatch[1];
85
+ const items = [];
86
+ // Split by lines and process bullet items
87
+ const lines = checklistContent.split('\n');
88
+ let currentItem = '';
89
+ for (const line of lines) {
90
+ const trimmed = line.trim();
91
+ // Check for bullet item start (- or *)
92
+ const bulletMatch = trimmed.match(/^[-*]\s+(.+)$/);
93
+ if (bulletMatch) {
94
+ // Save previous item if exists
95
+ if (currentItem) {
96
+ items.push(currentItem.trim());
97
+ }
98
+ currentItem = bulletMatch[1];
99
+ }
100
+ else if (currentItem && trimmed && !trimmed.startsWith('#')) {
101
+ // Continuation of current item
102
+ currentItem += ' ' + trimmed;
103
+ }
104
+ }
105
+ // Add final item
106
+ if (currentItem) {
107
+ items.push(currentItem.trim());
108
+ }
109
+ if (items.length === 0) {
110
+ return null;
111
+ }
112
+ return { items };
113
+ }
114
+ /**
115
+ * Parse inputs/outputs from an operation section
116
+ */
117
+ function parseInputsOutputs(content, sectionName) {
118
+ const pattern = new RegExp(`###\\s*\\[?${sectionName}\\]?\\s*\\n([\\s\\S]*?)(?=\\n###|\\n##|$)`, 'i');
119
+ const match = content.match(pattern);
120
+ if (!match) {
121
+ return [];
122
+ }
123
+ const items = [];
124
+ const lines = match[1].split('\n');
125
+ for (const line of lines) {
126
+ const trimmed = line.trim();
127
+ const bulletMatch = trimmed.match(/^[-*]\s+(.+)$/);
128
+ if (bulletMatch) {
129
+ items.push(bulletMatch[1].trim());
130
+ }
131
+ }
132
+ return items;
133
+ }
134
+ /**
135
+ * Parse operations from markdown content
136
+ * Returns array of Operation objects matching busy-python format
137
+ *
138
+ * @param content - Full markdown document content
139
+ * @returns Array of NewOperation objects
140
+ */
141
+ export function parseOperations(content) {
142
+ const operations = [];
143
+ // Find Operations section - handle both with and without brackets
144
+ // Match # Operations or # [Operations]
145
+ const operationsMatch = content.match(/^#\s*\[?Operations\]?\s*$/im);
146
+ if (!operationsMatch) {
147
+ return [];
148
+ }
149
+ // Get content after Operations heading until next top-level section or end
150
+ const startIndex = operationsMatch.index + operationsMatch[0].length;
151
+ const restContent = content.slice(startIndex);
152
+ // Find next top-level heading (# not ##)
153
+ const nextH1Match = restContent.match(/\n#\s+[^\#]/);
154
+ const operationsContent = nextH1Match
155
+ ? restContent.slice(0, nextH1Match.index)
156
+ : restContent;
157
+ // Split by ## headings to find individual operations
158
+ // Use a simpler approach: split by ## and process each part
159
+ const parts = operationsContent.split(/\n(?=##\s+)/);
160
+ for (const part of parts) {
161
+ if (!part.trim())
162
+ continue;
163
+ // Match operation heading: ## OperationName or ## [OperationName][Type]
164
+ const headingMatch = part.match(/^##\s+(?:\[([^\]]+)\](?:\[[^\]]*\])?|([^\n]+))\s*\n?([\s\S]*)$/);
165
+ if (headingMatch) {
166
+ const name = (headingMatch[1] || headingMatch[2]).trim();
167
+ const opContent = headingMatch[3] || '';
168
+ const steps = parseSteps(opContent);
169
+ const checklist = parseChecklist(opContent);
170
+ const inputs = parseInputsOutputs(opContent, 'Inputs');
171
+ const outputs = parseInputsOutputs(opContent, 'Outputs');
172
+ operations.push({
173
+ name,
174
+ inputs,
175
+ outputs,
176
+ steps,
177
+ checklist: checklist || undefined,
178
+ });
179
+ }
180
+ }
181
+ return operations;
182
+ }
183
+ /**
184
+ * Extract Operations from sections
185
+ */
186
+ export function extractOperations(sections, docId, filePath) {
187
+ debug.localdefs('Extracting operations for %s', docId);
188
+ // Find the Operations section
189
+ const operationsSection = findOperationsSection(sections);
190
+ if (!operationsSection) {
191
+ debug.localdefs('No Operations section found');
192
+ return [];
193
+ }
194
+ debug.localdefs('Found Operations section: %s', operationsSection.title);
195
+ // Extract all direct children as Operations
196
+ const operations = [];
197
+ for (const child of operationsSection.children) {
198
+ const operation = createOperation(child, docId, filePath);
199
+ // Skip empty operations (just reference headers with no content)
200
+ // These are placeholders that should be inherited from parent documents
201
+ if (operation.content.trim().length === 0 &&
202
+ operation.steps.length === 0 &&
203
+ operation.checklist.length === 0 &&
204
+ child.children.length === 0) {
205
+ debug.localdefs('Skipping empty operation: %s (likely a reference header)', operation.name);
206
+ continue;
207
+ }
208
+ operations.push(operation);
209
+ }
210
+ debug.localdefs('Extracted %d operations', operations.length);
211
+ return operations;
212
+ }
213
+ /**
214
+ * Find the Operations section (case-insensitive)
215
+ */
216
+ function findOperationsSection(sections) {
217
+ for (const alias of OPERATIONS_SECTION_ALIASES) {
218
+ const section = findSection(sections, alias);
219
+ if (section) {
220
+ return section;
221
+ }
222
+ }
223
+ return undefined;
224
+ }
225
+ /**
226
+ * Create an Operation from a section
227
+ */
228
+ function createOperation(section, docId, filePath) {
229
+ const slug = section.slug;
230
+ const id = `${docId}::${slug}`; // Use :: for concept IDs
231
+ // Parse steps and checklist from content
232
+ const { steps, checklist } = parseOperationContent(section);
233
+ // Get extends from section heading (e.g., ## [ValidateInput][Operation])
234
+ const extends_ = getSectionExtends(section.id);
235
+ return {
236
+ kind: 'operation',
237
+ id,
238
+ docId,
239
+ slug,
240
+ name: section.title,
241
+ content: section.content,
242
+ types: [],
243
+ extends: extends_,
244
+ sectionRef: section.id, // sectionRef uses # for section references
245
+ steps,
246
+ checklist,
247
+ };
248
+ }
249
+ /**
250
+ * Parse operation content for steps and checklist
251
+ */
252
+ function parseOperationContent(section) {
253
+ const steps = [];
254
+ const checklist = [];
255
+ // Look for Steps subsection
256
+ const stepsSection = section.children.find((child) => child.title.toLowerCase() === 'steps');
257
+ if (stepsSection) {
258
+ steps.push(...extractListItems(stepsSection.content));
259
+ }
260
+ else {
261
+ // Try to find numbered lists in main content
262
+ steps.push(...extractListItems(section.content));
263
+ }
264
+ // Look for Checklist subsection
265
+ const checklistSection = section.children.find((child) => child.title.toLowerCase() === 'checklist');
266
+ if (checklistSection) {
267
+ checklist.push(...extractListItems(checklistSection.content));
268
+ }
269
+ return { steps, checklist };
270
+ }
271
+ /**
272
+ * Extract list items from markdown content
273
+ * Handles both ordered (1. 2. 3.) and unordered (- *) lists
274
+ */
275
+ function extractListItems(content) {
276
+ const items = [];
277
+ const lines = content.split('\n');
278
+ let inList = false;
279
+ let currentItem = '';
280
+ for (const line of lines) {
281
+ const trimmed = line.trim();
282
+ // Check if this is a list item
283
+ const orderedMatch = trimmed.match(/^\d+\.\s+(.+)$/);
284
+ const unorderedMatch = trimmed.match(/^[-*]\s+(.+)$/);
285
+ if (orderedMatch || unorderedMatch) {
286
+ // Save previous item if any
287
+ if (currentItem) {
288
+ items.push(currentItem.trim());
289
+ }
290
+ // Start new item
291
+ currentItem = (orderedMatch?.[1] || unorderedMatch?.[1] || '').trim();
292
+ inList = true;
293
+ }
294
+ else if (inList && trimmed && !trimmed.startsWith('#')) {
295
+ // Continuation of current item
296
+ currentItem += ' ' + trimmed;
297
+ }
298
+ else if (inList && !trimmed) {
299
+ // Empty line might end the list
300
+ if (currentItem) {
301
+ items.push(currentItem.trim());
302
+ currentItem = '';
303
+ }
304
+ inList = false;
305
+ }
306
+ }
307
+ // Add final item
308
+ if (currentItem) {
309
+ items.push(currentItem.trim());
310
+ }
311
+ return items;
312
+ }
313
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1,15 @@
1
+ import { Section, DocId } from '../types/schema.js';
2
+ /**
3
+ * Parse markdown content into a section tree
4
+ */
5
+ export declare function parseSections(content: string, docId: DocId, filePath: string): Section[];
6
+ export declare function getSectionExtends(sectionId: string): string[];
7
+ /**
8
+ * Find a section by name/slug (case-insensitive)
9
+ */
10
+ export declare function findSection(sections: Section[], nameOrSlug: string): Section | undefined;
11
+ /**
12
+ * Get all sections recursively (flatten tree)
13
+ */
14
+ export declare function getAllSections(sections: Section[]): Section[];
15
+ //# sourceMappingURL=sections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../../src/parsers/sections.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAapD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,EAAE,CAoCX;AAQD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAE7D;AAuHD;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,GAAG,SAAS,CAmBrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAS7D"}
@@ -0,0 +1,173 @@
1
+ import { unified } from 'unified';
2
+ import remarkParse from 'remark-parse';
3
+ import remarkFrontmatter from 'remark-frontmatter';
4
+ import { visit } from 'unist-util-visit';
5
+ import { createSlug } from '../utils/slugify.js';
6
+ import { debug } from '../utils/logger.js';
7
+ /**
8
+ * Parse markdown content into a section tree
9
+ */
10
+ export function parseSections(content, docId, filePath) {
11
+ debug.sections('Parsing sections for %s', docId);
12
+ const processor = unified().use(remarkParse).use(remarkFrontmatter, ['yaml']);
13
+ const tree = processor.parse(content);
14
+ // Extract headings with their positions
15
+ const headings = [];
16
+ visit(tree, 'heading', (node) => {
17
+ const { title, extends: extendsArr } = parseHeadingNode(node);
18
+ const slug = createSlug(title);
19
+ const lineStart = node.position?.start.line ?? 0;
20
+ const lineEnd = node.position?.end.line ?? 0;
21
+ if (extendsArr.length > 0) {
22
+ debug.sections('Parsed heading: "%s" extends %o', title, extendsArr);
23
+ }
24
+ headings.push({
25
+ depth: node.depth,
26
+ title,
27
+ slug,
28
+ lineStart,
29
+ lineEnd,
30
+ extends: extendsArr,
31
+ });
32
+ });
33
+ // Build section tree and populate extends map
34
+ const sections = buildSectionTree(headings, content, docId, filePath);
35
+ debug.sections('Found %d top-level sections', sections.length);
36
+ return sections;
37
+ }
38
+ /**
39
+ * Get extends information for a section by ID
40
+ * This is stored separately since Section schema doesn't include extends
41
+ */
42
+ const sectionExtendsMap = new Map();
43
+ export function getSectionExtends(sectionId) {
44
+ return sectionExtendsMap.get(sectionId) ?? [];
45
+ }
46
+ /**
47
+ * Extract plain text from a node
48
+ */
49
+ function extractTextFromNode(node) {
50
+ if ('value' in node && typeof node.value === 'string') {
51
+ return node.value;
52
+ }
53
+ if ('children' in node && Array.isArray(node.children)) {
54
+ return node.children.map(extractTextFromNode).join('');
55
+ }
56
+ return '';
57
+ }
58
+ /**
59
+ * Parse heading node to extract title and extends information
60
+ * Patterns:
61
+ * - [Title][Type] -> title="Title", extends=["Type"]
62
+ * - [Title][Type1][Type2] -> title="Title", extends=["Type1", "Type2"]
63
+ * - Regular Title -> title="Regular Title", extends=[]
64
+ *
65
+ * When [Type] is a defined reference link (like [Operation]:./operation.md),
66
+ * markdown parses [Title][Type] as a linkReference node with:
67
+ * - children: [text node with "Title"]
68
+ * - label: "Type"
69
+ * - referenceType: "full"
70
+ */
71
+ function parseHeadingNode(node) {
72
+ const children = node.children || [];
73
+ // Check for linkReference pattern: [Title][Type]
74
+ if (children.length === 1 && children[0].type === 'linkReference') {
75
+ const linkRef = children[0];
76
+ if (linkRef.referenceType === 'full' && linkRef.label) {
77
+ // This is [Title][Type] where Type is a defined reference
78
+ const title = extractTextFromNode(linkRef);
79
+ const extends_ = [linkRef.label];
80
+ return { title, extends: extends_ };
81
+ }
82
+ }
83
+ // Fallback: extract plain text
84
+ const rawTitle = extractTextFromNode(node);
85
+ return { title: rawTitle, extends: [] };
86
+ }
87
+ /**
88
+ * Build a hierarchical section tree from flat headings
89
+ */
90
+ function buildSectionTree(headings, content, docId, filePath) {
91
+ if (headings.length === 0) {
92
+ return [];
93
+ }
94
+ const lines = content.split('\n');
95
+ const sections = [];
96
+ const stack = [];
97
+ for (let i = 0; i < headings.length; i++) {
98
+ const heading = headings[i];
99
+ const nextHeading = headings[i + 1];
100
+ // Determine content boundaries
101
+ const contentStart = heading.lineEnd;
102
+ const contentEnd = nextHeading
103
+ ? nextHeading.lineStart - 1
104
+ : lines.length;
105
+ // Extract content for this section
106
+ const sectionContent = lines
107
+ .slice(contentStart, contentEnd)
108
+ .join('\n')
109
+ .trim();
110
+ // Create section
111
+ const section = {
112
+ kind: 'section',
113
+ id: `${docId}#${heading.slug}`,
114
+ docId,
115
+ slug: heading.slug,
116
+ title: heading.title,
117
+ depth: heading.depth,
118
+ path: filePath,
119
+ lineStart: heading.lineStart,
120
+ lineEnd: contentEnd,
121
+ content: sectionContent,
122
+ children: [],
123
+ };
124
+ // Store extends metadata separately (not in Section schema)
125
+ if (heading.extends.length > 0) {
126
+ sectionExtendsMap.set(section.id, heading.extends);
127
+ }
128
+ // Find parent and add to tree
129
+ while (stack.length > 0 && stack[stack.length - 1].depth >= heading.depth) {
130
+ stack.pop();
131
+ }
132
+ if (stack.length === 0) {
133
+ // Top-level section
134
+ sections.push(section);
135
+ }
136
+ else {
137
+ // Child section
138
+ stack[stack.length - 1].children.push(section);
139
+ }
140
+ stack.push(section);
141
+ }
142
+ return sections;
143
+ }
144
+ /**
145
+ * Find a section by name/slug (case-insensitive)
146
+ */
147
+ export function findSection(sections, nameOrSlug) {
148
+ const target = nameOrSlug.toLowerCase();
149
+ for (const section of sections) {
150
+ if (section.title.toLowerCase() === target ||
151
+ section.slug.toLowerCase() === target) {
152
+ return section;
153
+ }
154
+ // Search recursively
155
+ const found = findSection(section.children, nameOrSlug);
156
+ if (found) {
157
+ return found;
158
+ }
159
+ }
160
+ return undefined;
161
+ }
162
+ /**
163
+ * Get all sections recursively (flatten tree)
164
+ */
165
+ export function getAllSections(sections) {
166
+ const result = [];
167
+ for (const section of sections) {
168
+ result.push(section);
169
+ result.push(...getAllSections(section.children));
170
+ }
171
+ return result;
172
+ }
173
+ //# sourceMappingURL=sections.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Tool Parser - Matches busy-python Tool and ToolDocument models
3
+ *
4
+ * Tools have:
5
+ * - name: string
6
+ * - description: string
7
+ * - inputs: list[str]
8
+ * - outputs: list[str]
9
+ * - examples: Optional[list[str]]
10
+ * - providers: Optional[dict[str, dict[str, Any]]] (provider_name -> {action, parameters})
11
+ */
12
+ import { Tool } from '../types/schema.js';
13
+ /**
14
+ * Parse provider mappings from tool content
15
+ *
16
+ * @param content - Content of the tool section
17
+ * @returns Record of provider name to {action, parameters}
18
+ */
19
+ export declare function parseToolProviders(content: string): Record<string, {
20
+ action: string;
21
+ parameters?: Record<string, any>;
22
+ }>;
23
+ /**
24
+ * Parse tools from markdown content
25
+ *
26
+ * @param content - Full markdown document content
27
+ * @returns Array of Tool objects
28
+ */
29
+ export declare function parseTools(content: string): Tool[];
30
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/parsers/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CA2DxH;AA0ED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,CAuDlD"}