busy-cli 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/builders/context.d.ts +50 -0
- package/dist/builders/context.d.ts.map +1 -0
- package/dist/builders/context.js +190 -0
- package/dist/cache/index.d.ts +100 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +270 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +463 -0
- package/dist/commands/package.d.ts +96 -0
- package/dist/commands/package.d.ts.map +1 -0
- package/dist/commands/package.js +285 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/loader.d.ts +6 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +361 -0
- package/dist/merge.d.ts +16 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +102 -0
- package/dist/package/manifest.d.ts +59 -0
- package/dist/package/manifest.d.ts.map +1 -0
- package/dist/package/manifest.js +265 -0
- package/dist/parser.d.ts +28 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +220 -0
- package/dist/parsers/frontmatter.d.ts +14 -0
- package/dist/parsers/frontmatter.d.ts.map +1 -0
- package/dist/parsers/frontmatter.js +110 -0
- package/dist/parsers/imports.d.ts +48 -0
- package/dist/parsers/imports.d.ts.map +1 -0
- package/dist/parsers/imports.js +147 -0
- package/dist/parsers/links.d.ts +12 -0
- package/dist/parsers/links.d.ts.map +1 -0
- package/dist/parsers/links.js +79 -0
- package/dist/parsers/localdefs.d.ts +6 -0
- package/dist/parsers/localdefs.d.ts.map +1 -0
- package/dist/parsers/localdefs.js +132 -0
- package/dist/parsers/operations.d.ts +32 -0
- package/dist/parsers/operations.d.ts.map +1 -0
- package/dist/parsers/operations.js +313 -0
- package/dist/parsers/sections.d.ts +15 -0
- package/dist/parsers/sections.d.ts.map +1 -0
- package/dist/parsers/sections.js +173 -0
- package/dist/parsers/tools.d.ts +30 -0
- package/dist/parsers/tools.d.ts.map +1 -0
- package/dist/parsers/tools.js +178 -0
- package/dist/parsers/triggers.d.ts +35 -0
- package/dist/parsers/triggers.d.ts.map +1 -0
- package/dist/parsers/triggers.js +219 -0
- package/dist/providers/base.d.ts +60 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +34 -0
- package/dist/providers/github.d.ts +18 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +109 -0
- package/dist/providers/gitlab.d.ts +18 -0
- package/dist/providers/gitlab.d.ts.map +1 -0
- package/dist/providers/gitlab.js +101 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +17 -0
- package/dist/providers/local.d.ts +31 -0
- package/dist/providers/local.d.ts.map +1 -0
- package/dist/providers/local.js +116 -0
- package/dist/providers/url.d.ts +16 -0
- package/dist/providers/url.d.ts.map +1 -0
- package/dist/providers/url.js +45 -0
- package/dist/registry/index.d.ts +99 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +320 -0
- package/dist/types/schema.d.ts +3259 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +258 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +23 -0
- package/dist/utils/slugify.d.ts +14 -0
- package/dist/utils/slugify.d.ts.map +1 -0
- package/dist/utils/slugify.js +28 -0
- package/package.json +61 -0
- package/src/__tests__/cache.test.ts +393 -0
- package/src/__tests__/cli-package.test.ts +667 -0
- package/src/__tests__/fixtures/automated-workflow.busy.md +84 -0
- package/src/__tests__/fixtures/concept.busy.md +30 -0
- package/src/__tests__/fixtures/document.busy.md +44 -0
- package/src/__tests__/fixtures/simple-operation.busy.md +45 -0
- package/src/__tests__/fixtures/tool-document.busy.md +71 -0
- package/src/__tests__/fixtures/tool.busy.md +54 -0
- package/src/__tests__/imports.test.ts +244 -0
- package/src/__tests__/integration.test.ts +432 -0
- package/src/__tests__/operations.test.ts +408 -0
- package/src/__tests__/package-manifest.test.ts +455 -0
- package/src/__tests__/providers.test.ts +672 -0
- package/src/__tests__/registry.test.ts +402 -0
- package/src/__tests__/schema.test.ts +467 -0
- package/src/__tests__/tools.test.ts +376 -0
- package/src/__tests__/triggers.test.ts +312 -0
- package/src/builders/context.ts +294 -0
- package/src/cache/index.ts +312 -0
- package/src/cli/index.ts +514 -0
- package/src/commands/package.ts +392 -0
- package/src/index.ts +46 -0
- package/src/loader.ts +474 -0
- package/src/merge.ts +126 -0
- package/src/package/manifest.ts +349 -0
- package/src/parser.ts +278 -0
- package/src/parsers/frontmatter.ts +135 -0
- package/src/parsers/imports.ts +196 -0
- package/src/parsers/links.ts +108 -0
- package/src/parsers/localdefs.ts +166 -0
- package/src/parsers/operations.ts +404 -0
- package/src/parsers/sections.ts +230 -0
- package/src/parsers/tools.ts +215 -0
- package/src/parsers/triggers.ts +252 -0
- package/src/providers/base.ts +77 -0
- package/src/providers/github.ts +129 -0
- package/src/providers/gitlab.ts +121 -0
- package/src/providers/index.ts +25 -0
- package/src/providers/local.ts +129 -0
- package/src/providers/url.ts +56 -0
- package/src/registry/index.ts +408 -0
- package/src/types/schema.ts +369 -0
- package/src/utils/logger.ts +25 -0
- package/src/utils/slugify.ts +31 -0
- package/tsconfig.json +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @busy/parser
|
|
2
|
+
|
|
3
|
+
TypeScript library + CLI that parses Busy markdown workspaces into a typed graph.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ Parse Busy markdown files with YAML frontmatter
|
|
8
|
+
- ✅ Build hierarchical section trees
|
|
9
|
+
- ✅ Extract Local Definitions with inheritance
|
|
10
|
+
- ✅ Resolve reference-style imports
|
|
11
|
+
- ✅ Create typed edge graph (ref, calls, extends, imports)
|
|
12
|
+
- ✅ Build minimal execution contexts for operations
|
|
13
|
+
- ✅ Export to JSON and DOT graph formats
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @busy/parser
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## CLI Usage
|
|
22
|
+
|
|
23
|
+
### Load and validate workspace
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
busyctx load "busy-v2/**/*.busy.md" --dump repo.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Build operation context
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
busyctx context "Document#evaluatedocument" -o context.json --maxDefChars 2000
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Export graph
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
busyctx graph --format dot > repo.dot
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API Usage
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { loadRepo, buildContext } from '@busy/parser';
|
|
45
|
+
|
|
46
|
+
// Load workspace
|
|
47
|
+
const repo = await loadRepo(['busy-v2/**/*.busy.md']);
|
|
48
|
+
|
|
49
|
+
console.log(`Loaded ${repo.docs.length} documents`);
|
|
50
|
+
console.log(`Found ${Object.keys(repo.localdefs).length} local definitions`);
|
|
51
|
+
console.log(`Created ${repo.edges.length} edges`);
|
|
52
|
+
|
|
53
|
+
// Build execution context for an operation
|
|
54
|
+
const context = buildContext(repo, 'Document#evaluatedocument', {
|
|
55
|
+
maxDefChars: 2000,
|
|
56
|
+
includeChildren: true
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
console.log(`Operation: ${context.operation.title}`);
|
|
60
|
+
console.log(`Definitions: ${context.defs.length}`);
|
|
61
|
+
console.log(`Calls: ${context.calls.length}`);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Data Model
|
|
65
|
+
|
|
66
|
+
### Documents & Sections
|
|
67
|
+
|
|
68
|
+
- **BusyDocument**: Top-level markdown file with frontmatter
|
|
69
|
+
- **Section**: Hierarchical heading structure (`#` - `######`)
|
|
70
|
+
- **LocalDef**: Definitions under "Local Definitions" section
|
|
71
|
+
|
|
72
|
+
### Edges
|
|
73
|
+
|
|
74
|
+
- **imports**: Document imports another document
|
|
75
|
+
- **calls**: Section/operation calls another operation
|
|
76
|
+
- **extends**: LocalDef extends another definition
|
|
77
|
+
- **ref**: General reference link
|
|
78
|
+
|
|
79
|
+
### Context Payload
|
|
80
|
+
|
|
81
|
+
Minimal execution context for an operation:
|
|
82
|
+
- Operation section (ref, title, content)
|
|
83
|
+
- Required local definitions (with transitive closure)
|
|
84
|
+
- Callable sub-operations
|
|
85
|
+
- Symbol table for imports
|
|
86
|
+
|
|
87
|
+
## Frontmatter Support
|
|
88
|
+
|
|
89
|
+
Handles both plain strings and markdown link syntax:
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
---
|
|
93
|
+
Name: Document
|
|
94
|
+
Type: [Concept]
|
|
95
|
+
Description: The core document type
|
|
96
|
+
Tags: [core, foundation]
|
|
97
|
+
Extends: [ConceptBase]
|
|
98
|
+
---
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Automatically strips brackets: `[Concept]` → `Concept`
|
|
102
|
+
|
|
103
|
+
## File Extensions
|
|
104
|
+
|
|
105
|
+
Supports both `.md` and `.busy.md` extensions. Import references to `.md` files automatically resolve to `.busy.md` files.
|
|
106
|
+
|
|
107
|
+
## Example Output
|
|
108
|
+
|
|
109
|
+
For `busy-v2/**/*.busy.md`:
|
|
110
|
+
- ✓ 15 documents loaded
|
|
111
|
+
- ✓ 45 local definitions
|
|
112
|
+
- ✓ 137 imports resolved
|
|
113
|
+
- ✓ 173 edges created
|
|
114
|
+
- 137 import edges
|
|
115
|
+
- 26 call edges
|
|
116
|
+
- 10 ref edges
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm install
|
|
122
|
+
npm run build
|
|
123
|
+
npm run dev # watch mode
|
|
124
|
+
npm test
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
ISC
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Repo, ContextPayload, Section, LocalDef, Operation, ConceptBase } from '../types/schema.js';
|
|
2
|
+
export interface BuildOpts {
|
|
3
|
+
includeChildren?: boolean;
|
|
4
|
+
maxDefChars?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Build minimal execution context for an operation
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildContext(repo: Repo, opRef: string, opts?: BuildOpts): ContextPayload;
|
|
10
|
+
/**
|
|
11
|
+
* Lookup helpers
|
|
12
|
+
*/
|
|
13
|
+
export declare function get(repo: Repo, ref: string): Section | LocalDef | Operation | ConceptBase | undefined;
|
|
14
|
+
export declare function parentsOf(repo: Repo, nameOrRef: string): string[];
|
|
15
|
+
export declare function childrenOf(repo: Repo, nameOrRef: string): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Context for a specific concept
|
|
18
|
+
*/
|
|
19
|
+
export interface ConceptContext {
|
|
20
|
+
concept: Section | LocalDef | Operation | ConceptBase;
|
|
21
|
+
calls: string[];
|
|
22
|
+
extends: string[];
|
|
23
|
+
imports: string[];
|
|
24
|
+
refs: string[];
|
|
25
|
+
calledBy: string[];
|
|
26
|
+
extendedBy: string[];
|
|
27
|
+
importedBy: string[];
|
|
28
|
+
referencedBy: string[];
|
|
29
|
+
allEdges: {
|
|
30
|
+
outgoing: Array<{
|
|
31
|
+
to: string;
|
|
32
|
+
role: string;
|
|
33
|
+
}>;
|
|
34
|
+
incoming: Array<{
|
|
35
|
+
from: string;
|
|
36
|
+
role: string;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
contentMap: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get comprehensive context for any concept by ID
|
|
43
|
+
* Returns all relationships (calls, extends, imports, refs) both outgoing and incoming
|
|
44
|
+
*/
|
|
45
|
+
export declare function getConceptContext(repo: Repo, conceptId: string): ConceptContext;
|
|
46
|
+
/**
|
|
47
|
+
* Write context to JSON file
|
|
48
|
+
*/
|
|
49
|
+
export declare function writeContext(file: string, ctx: ContextPayload): Promise<void>;
|
|
50
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/builders/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGrG,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAc,GACnB,cAAc,CA2EhB;AA4BD;;GAEG;AACH,wBAAgB,GAAG,CACjB,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,GACV,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAE1D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAKlE;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IAEtD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IAEvB,QAAQ,EAAE;QACR,QAAQ,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC9C,QAAQ,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACjD,CAAC;IAEF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,GAChB,cAAc,CA4GhB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { debug } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build minimal execution context for an operation
|
|
4
|
+
*/
|
|
5
|
+
export function buildContext(repo, opRef, opts = {}) {
|
|
6
|
+
debug.context('Building context for operation: %s', opRef);
|
|
7
|
+
const { includeChildren = false, maxDefChars } = opts;
|
|
8
|
+
// 1. Seed: resolve opRef to a Section or Operation
|
|
9
|
+
const opNode = repo.byId[opRef];
|
|
10
|
+
if (!opNode || (opNode.kind !== 'section' && opNode.kind !== 'operation')) {
|
|
11
|
+
throw new Error(`Operation not found: ${opRef}`);
|
|
12
|
+
}
|
|
13
|
+
// Get the full Operation object
|
|
14
|
+
let operation;
|
|
15
|
+
if (opNode.kind === 'operation') {
|
|
16
|
+
operation = opNode;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
// If it's a section, we need to find the operation or throw
|
|
20
|
+
throw new Error(`Expected operation but got section: ${opRef}`);
|
|
21
|
+
}
|
|
22
|
+
// 2. Collect outgoing edges from the operation section
|
|
23
|
+
const edges = repo.edges.filter((edge) => {
|
|
24
|
+
if (edge.from === opNode.id) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
// Optionally include edges from child sections
|
|
28
|
+
if (includeChildren) {
|
|
29
|
+
return edge.from.startsWith(`${opNode.docId}#`);
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
});
|
|
33
|
+
debug.context('Found %d outgoing edges', edges.length);
|
|
34
|
+
// 3. Calls (just array of concept IDs)
|
|
35
|
+
const calls = [];
|
|
36
|
+
for (const edge of edges) {
|
|
37
|
+
if (edge.role === 'calls') {
|
|
38
|
+
calls.push(edge.to);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
debug.context('Found %d calls', calls.length);
|
|
42
|
+
// 4. Symbols
|
|
43
|
+
// Get the document's import symbol table
|
|
44
|
+
const fileInfo = repo.byFile[opNode.docId];
|
|
45
|
+
const docImports = repo.imports.filter((imp) => imp.docId === opNode.docId);
|
|
46
|
+
const symbols = {};
|
|
47
|
+
for (const imp of docImports) {
|
|
48
|
+
if (imp.resolved) {
|
|
49
|
+
// Parse ConceptId string back to { docId, slug }
|
|
50
|
+
const parts = imp.resolved.split('#');
|
|
51
|
+
symbols[imp.label] = {
|
|
52
|
+
docId: parts[0],
|
|
53
|
+
slug: parts[1],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const payload = {
|
|
58
|
+
operation,
|
|
59
|
+
calls,
|
|
60
|
+
symbols,
|
|
61
|
+
};
|
|
62
|
+
debug.context('Context built successfully');
|
|
63
|
+
return payload;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Trim content to max characters, preserving structure
|
|
67
|
+
*/
|
|
68
|
+
function trimContent(content, maxChars) {
|
|
69
|
+
if (content.length <= maxChars) {
|
|
70
|
+
return content;
|
|
71
|
+
}
|
|
72
|
+
// Try to preserve headings and code fences
|
|
73
|
+
const lines = content.split('\n');
|
|
74
|
+
let result = '';
|
|
75
|
+
let charCount = 0;
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
if (charCount + line.length > maxChars) {
|
|
78
|
+
result += '\n\n[... trimmed ...]';
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
result += line + '\n';
|
|
82
|
+
charCount += line.length + 1;
|
|
83
|
+
}
|
|
84
|
+
return result.trim();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Lookup helpers
|
|
88
|
+
*/
|
|
89
|
+
export function get(repo, ref) {
|
|
90
|
+
return repo.byId[ref];
|
|
91
|
+
}
|
|
92
|
+
export function parentsOf(repo, nameOrRef) {
|
|
93
|
+
const edges = repo.edges.filter((edge) => edge.from === nameOrRef && edge.role === 'extends');
|
|
94
|
+
return edges.map((edge) => edge.to);
|
|
95
|
+
}
|
|
96
|
+
export function childrenOf(repo, nameOrRef) {
|
|
97
|
+
const edges = repo.edges.filter((edge) => edge.to === nameOrRef && edge.role === 'extends');
|
|
98
|
+
return edges.map((edge) => edge.from);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get comprehensive context for any concept by ID
|
|
102
|
+
* Returns all relationships (calls, extends, imports, refs) both outgoing and incoming
|
|
103
|
+
*/
|
|
104
|
+
export function getConceptContext(repo, conceptId) {
|
|
105
|
+
debug.context('Building concept context for: %s', conceptId);
|
|
106
|
+
// 1. Get the concept
|
|
107
|
+
const concept = repo.byId[conceptId];
|
|
108
|
+
if (!concept) {
|
|
109
|
+
throw new Error(`Concept not found: ${conceptId}`);
|
|
110
|
+
}
|
|
111
|
+
// 2. Get all outgoing edges
|
|
112
|
+
// For operations and localdefs, also include edges from their parent section and document
|
|
113
|
+
let edgeSources = [conceptId];
|
|
114
|
+
if (concept.kind === 'operation' || concept.kind === 'localdef') {
|
|
115
|
+
// Add the section
|
|
116
|
+
if ('sectionRef' in concept && concept.sectionRef) {
|
|
117
|
+
edgeSources.push(concept.sectionRef);
|
|
118
|
+
}
|
|
119
|
+
// Add the document
|
|
120
|
+
if ('docId' in concept && concept.docId) {
|
|
121
|
+
edgeSources.push(concept.docId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const outgoingEdges = repo.edges.filter((edge) => edgeSources.includes(edge.from));
|
|
125
|
+
// 3. Get all incoming edges
|
|
126
|
+
const incomingEdges = repo.edges.filter((edge) => edge.to === conceptId);
|
|
127
|
+
// 4. Group outgoing edges by role
|
|
128
|
+
const calls = outgoingEdges
|
|
129
|
+
.filter((e) => e.role === 'calls')
|
|
130
|
+
.map((e) => e.to);
|
|
131
|
+
const extendsEdges = outgoingEdges
|
|
132
|
+
.filter((e) => e.role === 'extends')
|
|
133
|
+
.map((e) => e.to);
|
|
134
|
+
const importsEdges = outgoingEdges
|
|
135
|
+
.filter((e) => e.role === 'imports')
|
|
136
|
+
.map((e) => e.to);
|
|
137
|
+
const refs = outgoingEdges
|
|
138
|
+
.filter((e) => e.role === 'ref')
|
|
139
|
+
.map((e) => e.to);
|
|
140
|
+
// 5. Group incoming edges by role
|
|
141
|
+
const calledBy = incomingEdges
|
|
142
|
+
.filter((e) => e.role === 'calls')
|
|
143
|
+
.map((e) => e.from);
|
|
144
|
+
const extendedBy = incomingEdges
|
|
145
|
+
.filter((e) => e.role === 'extends')
|
|
146
|
+
.map((e) => e.from);
|
|
147
|
+
const importedBy = incomingEdges
|
|
148
|
+
.filter((e) => e.role === 'imports')
|
|
149
|
+
.map((e) => e.from);
|
|
150
|
+
const referencedBy = incomingEdges
|
|
151
|
+
.filter((e) => e.role === 'ref')
|
|
152
|
+
.map((e) => e.from);
|
|
153
|
+
debug.context('Found: %d calls, %d extends, %d imports, %d refs (outgoing)', calls.length, extendsEdges.length, importsEdges.length, refs.length);
|
|
154
|
+
debug.context('Found: %d calledBy, %d extendedBy, %d importedBy, %d referencedBy (incoming)', calledBy.length, extendedBy.length, importedBy.length, referencedBy.length);
|
|
155
|
+
// 6. Build content map for imports and refs
|
|
156
|
+
const contentMap = {};
|
|
157
|
+
const contentConceptIds = new Set([...importsEdges, ...refs]);
|
|
158
|
+
for (const id of contentConceptIds) {
|
|
159
|
+
const relatedConcept = repo.byId[id];
|
|
160
|
+
if (relatedConcept && 'content' in relatedConcept) {
|
|
161
|
+
contentMap[id] = relatedConcept.content;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
debug.context('Built content map with %d entries', Object.keys(contentMap).length);
|
|
165
|
+
return {
|
|
166
|
+
concept,
|
|
167
|
+
calls,
|
|
168
|
+
extends: extendsEdges,
|
|
169
|
+
imports: importsEdges,
|
|
170
|
+
refs,
|
|
171
|
+
calledBy,
|
|
172
|
+
extendedBy,
|
|
173
|
+
importedBy,
|
|
174
|
+
referencedBy,
|
|
175
|
+
allEdges: {
|
|
176
|
+
outgoing: outgoingEdges.map((e) => ({ to: e.to, role: e.role })),
|
|
177
|
+
incoming: incomingEdges.map((e) => ({ from: e.from, role: e.role })),
|
|
178
|
+
},
|
|
179
|
+
contentMap,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Write context to JSON file
|
|
184
|
+
*/
|
|
185
|
+
export async function writeContext(file, ctx) {
|
|
186
|
+
const { writeFile } = await import('fs/promises');
|
|
187
|
+
await writeFile(file, JSON.stringify(ctx, null, 2), 'utf-8');
|
|
188
|
+
debug.context('Context written to %s', file);
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Manager for .libraries/
|
|
3
|
+
*
|
|
4
|
+
* Manages local cache of fetched packages.
|
|
5
|
+
*/
|
|
6
|
+
import type { ParsedURL } from '../providers/base.js';
|
|
7
|
+
/**
|
|
8
|
+
* Result of saving a file to cache
|
|
9
|
+
*/
|
|
10
|
+
export interface CachedFile {
|
|
11
|
+
path: string;
|
|
12
|
+
fullPath: string;
|
|
13
|
+
integrity: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Calculate SHA256 integrity hash of content
|
|
17
|
+
*/
|
|
18
|
+
export declare function calculateIntegrity(content: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Verify content matches integrity hash
|
|
21
|
+
*/
|
|
22
|
+
export declare function verifyIntegrity(content: string, integrity: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Derive cache path from parsed URL
|
|
25
|
+
*
|
|
26
|
+
* For GitHub/GitLab: {repo}/{path-from-blob}
|
|
27
|
+
* For generic URLs: {domain}/{path}
|
|
28
|
+
*/
|
|
29
|
+
export declare function deriveCachePath(parsed: ParsedURL): string;
|
|
30
|
+
/**
|
|
31
|
+
* Cache Manager
|
|
32
|
+
*
|
|
33
|
+
* Manages the .libraries/ cache directory.
|
|
34
|
+
*/
|
|
35
|
+
export declare class CacheManager {
|
|
36
|
+
private _workspaceRoot;
|
|
37
|
+
private _librariesPath;
|
|
38
|
+
constructor(workspaceRoot: string);
|
|
39
|
+
/**
|
|
40
|
+
* Get workspace root path
|
|
41
|
+
*/
|
|
42
|
+
get workspaceRoot(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Get .libraries path
|
|
45
|
+
*/
|
|
46
|
+
get librariesPath(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Initialize cache directory
|
|
49
|
+
*/
|
|
50
|
+
init(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Save content to cache
|
|
53
|
+
*/
|
|
54
|
+
save(cachePath: string, content: string): Promise<CachedFile>;
|
|
55
|
+
/**
|
|
56
|
+
* Read content from cache
|
|
57
|
+
*/
|
|
58
|
+
read(cachePath: string): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Check if file exists in cache
|
|
61
|
+
*/
|
|
62
|
+
exists(cachePath: string): Promise<boolean>;
|
|
63
|
+
/**
|
|
64
|
+
* Delete file from cache
|
|
65
|
+
*/
|
|
66
|
+
delete(cachePath: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* List all cached files
|
|
69
|
+
*/
|
|
70
|
+
list(): Promise<string[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Remove all cached files
|
|
73
|
+
*/
|
|
74
|
+
clean(): Promise<number>;
|
|
75
|
+
/**
|
|
76
|
+
* Verify integrity of cached file
|
|
77
|
+
*/
|
|
78
|
+
verifyIntegrity(cachePath: string, integrity: string): Promise<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Get full filesystem path from cache path
|
|
81
|
+
*/
|
|
82
|
+
getFullPath(cachePath: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Get cache path from full filesystem path
|
|
85
|
+
*/
|
|
86
|
+
getCachePath(fullPath: string): string | null;
|
|
87
|
+
/**
|
|
88
|
+
* Walk directory recursively to collect files
|
|
89
|
+
*/
|
|
90
|
+
private walkDir;
|
|
91
|
+
/**
|
|
92
|
+
* Clean up empty directories from a path up to .libraries
|
|
93
|
+
*/
|
|
94
|
+
private cleanEmptyDirs;
|
|
95
|
+
/**
|
|
96
|
+
* Clean all empty directories under .libraries
|
|
97
|
+
*/
|
|
98
|
+
private cleanAllEmptyDirs;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAO3E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAoBzD;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,cAAc,CAAS;gBAEnB,aAAa,EAAE,MAAM;IAKjC;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAmBnE;;OAEG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C;;OAEG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUjD;;OAEG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB9C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAgB/B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAkB9B;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS7E;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAItC;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAe7C;;OAEG;YACW,OAAO;IAiBrB;;OAEG;YACW,cAAc;IAkB5B;;OAEG;YACW,iBAAiB;CAwBhC"}
|