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
|
@@ -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"}
|