@fragments-sdk/context 0.1.3 → 0.2.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.
@@ -0,0 +1,103 @@
1
+ // src/citations/document-builder.ts
2
+ function buildCitationDocuments(chunks, options) {
3
+ const documents = [];
4
+ const documentMap = [];
5
+ const byFile = /* @__PURE__ */ new Map();
6
+ for (const chunk of chunks) {
7
+ const existing = byFile.get(chunk.filePath);
8
+ if (existing) {
9
+ existing.push(chunk);
10
+ } else {
11
+ byFile.set(chunk.filePath, [chunk]);
12
+ }
13
+ }
14
+ for (const [filePath, fileChunks] of byFile) {
15
+ const docIndex = documents.length;
16
+ const contentBlocks = fileChunks.map((chunk) => ({
17
+ type: "text",
18
+ text: chunk.content
19
+ }));
20
+ const contextLines = fileChunks.map((chunk, i) => {
21
+ const parts = [`Block ${i}: lines ${chunk.startLine}-${chunk.endLine} (${chunk.language})`];
22
+ if (chunk.symbolName) parts.push(`symbol: ${chunk.symbolName}`);
23
+ return parts.join(", ");
24
+ });
25
+ documents.push({
26
+ type: "document",
27
+ source: { type: "content", content: contentBlocks },
28
+ title: filePath,
29
+ context: contextLines.join("\n"),
30
+ citations: { enabled: true }
31
+ });
32
+ documentMap.push({
33
+ documentIndex: docIndex,
34
+ filePath,
35
+ sourceType: "code",
36
+ chunks: fileChunks.map((chunk, i) => ({
37
+ blockIndex: i,
38
+ startLine: chunk.startLine,
39
+ endLine: chunk.endLine,
40
+ language: chunk.language,
41
+ symbolName: chunk.symbolName,
42
+ score: chunk.score
43
+ }))
44
+ });
45
+ }
46
+ if (options?.additionalDocuments) {
47
+ for (const doc of options.additionalDocuments) {
48
+ if (!doc.content.trim()) continue;
49
+ const docIndex = documents.length;
50
+ documents.push({
51
+ type: "document",
52
+ source: {
53
+ type: "text",
54
+ media_type: "text/plain",
55
+ data: doc.content
56
+ },
57
+ title: doc.title,
58
+ citations: { enabled: true }
59
+ });
60
+ documentMap.push({
61
+ documentIndex: docIndex,
62
+ filePath: doc.title,
63
+ sourceType: "plaintext",
64
+ chunks: []
65
+ });
66
+ }
67
+ }
68
+ return { documents, documentMap };
69
+ }
70
+
71
+ // src/citations/resolver.ts
72
+ function resolveCitation(raw, documentMap, citationIndex, textOffset) {
73
+ const mapping = documentMap[raw.document_index];
74
+ const base = {
75
+ index: citationIndex,
76
+ citedText: raw.cited_text,
77
+ documentTitle: raw.document_title,
78
+ filePath: mapping?.filePath ?? raw.document_title ?? "unknown",
79
+ sourceType: mapping?.sourceType ?? "plaintext",
80
+ textOffset
81
+ };
82
+ if (raw.type === "content_block_location" && mapping?.sourceType === "code" && raw.start_block_index != null) {
83
+ const chunk = mapping.chunks[raw.start_block_index];
84
+ if (chunk) {
85
+ base.startLine = chunk.startLine;
86
+ base.endLine = chunk.endLine;
87
+ base.language = chunk.language;
88
+ base.symbolName = chunk.symbolName;
89
+ }
90
+ }
91
+ return base;
92
+ }
93
+ function resolveCitations(raws, documentMap, textOffsets) {
94
+ return raws.map(
95
+ (raw, i) => resolveCitation(raw, documentMap, i + 1, textOffsets[i] ?? 0)
96
+ );
97
+ }
98
+
99
+ export {
100
+ buildCitationDocuments,
101
+ resolveCitation,
102
+ resolveCitations
103
+ };
@@ -0,0 +1,76 @@
1
+ // src/embeddings/voyage.ts
2
+ var VOYAGE_EMBEDDINGS_URL = "https://api.voyageai.com/v1/embeddings";
3
+ var VOYAGE_RERANK_URL = "https://api.voyageai.com/v1/rerank";
4
+ var DEFAULT_MODEL = "voyage-code-3";
5
+ var DEFAULT_RERANK_MODEL = "rerank-2.5";
6
+ var DEFAULT_DIMENSIONS = 1024;
7
+ var DEFAULT_MAX_CHARS = 32e3;
8
+ var DEFAULT_BATCH_SIZE = 128;
9
+ async function generateEmbeddings(texts, options) {
10
+ if (texts.length === 0) return [];
11
+ const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
12
+ const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
13
+ const dimensions = options.dimensions ?? DEFAULT_DIMENSIONS;
14
+ const truncated = texts.map(
15
+ (t) => t.length > maxChars ? t.slice(0, maxChars) : t
16
+ );
17
+ const results = [];
18
+ for (let i = 0; i < truncated.length; i += batchSize) {
19
+ const batch = truncated.slice(i, i + batchSize);
20
+ const response = await fetch(VOYAGE_EMBEDDINGS_URL, {
21
+ method: "POST",
22
+ headers: {
23
+ "Content-Type": "application/json",
24
+ Authorization: `Bearer ${options.apiKey}`
25
+ },
26
+ body: JSON.stringify({
27
+ model: DEFAULT_MODEL,
28
+ input: batch,
29
+ input_type: options.inputType,
30
+ output_dimension: dimensions
31
+ })
32
+ });
33
+ if (!response.ok) {
34
+ const body = await response.text();
35
+ throw new Error(`Voyage API error ${response.status}: ${body}`);
36
+ }
37
+ const data = await response.json();
38
+ const sorted = data.data.sort((a, b) => a.index - b.index);
39
+ for (const item of sorted) {
40
+ results.push(item.embedding);
41
+ }
42
+ }
43
+ return results;
44
+ }
45
+ async function rerankResults(query, documents, options) {
46
+ if (documents.length === 0) return [];
47
+ const topK = options.topK ?? 15;
48
+ const model = options.model ?? DEFAULT_RERANK_MODEL;
49
+ const response = await fetch(VOYAGE_RERANK_URL, {
50
+ method: "POST",
51
+ headers: {
52
+ "Content-Type": "application/json",
53
+ Authorization: `Bearer ${options.apiKey}`
54
+ },
55
+ body: JSON.stringify({
56
+ model,
57
+ query,
58
+ documents,
59
+ top_k: topK
60
+ })
61
+ });
62
+ if (!response.ok) {
63
+ const body = await response.text();
64
+ throw new Error(`Voyage Rerank API error ${response.status}: ${body}`);
65
+ }
66
+ const data = await response.json();
67
+ return data.data.map((item) => ({
68
+ index: item.index,
69
+ relevanceScore: item.relevance_score
70
+ }));
71
+ }
72
+
73
+ export {
74
+ generateEmbeddings,
75
+ rerankResults
76
+ };
@@ -0,0 +1,42 @@
1
+ // src/indexing/hasher.ts
2
+ import { createHash } from "crypto";
3
+ function hashContent(content) {
4
+ return createHash("sha256").update(content, "utf-8").digest("hex");
5
+ }
6
+
7
+ // src/indexing/diff-resolver.ts
8
+ function resolveChanges(oldEntries, newEntries) {
9
+ const oldMap = /* @__PURE__ */ new Map();
10
+ for (const entry of oldEntries) {
11
+ oldMap.set(entry.filePath, entry);
12
+ }
13
+ const newMap = /* @__PURE__ */ new Map();
14
+ for (const entry of newEntries) {
15
+ newMap.set(entry.filePath, entry);
16
+ }
17
+ const added = [];
18
+ const modified = [];
19
+ const unchanged = [];
20
+ const deleted = [];
21
+ for (const [filePath, newEntry] of newMap) {
22
+ const oldEntry = oldMap.get(filePath);
23
+ if (!oldEntry) {
24
+ added.push(filePath);
25
+ } else if (oldEntry.contentHash !== newEntry.contentHash) {
26
+ modified.push(filePath);
27
+ } else {
28
+ unchanged.push(filePath);
29
+ }
30
+ }
31
+ for (const filePath of oldMap.keys()) {
32
+ if (!newMap.has(filePath)) {
33
+ deleted.push(filePath);
34
+ }
35
+ }
36
+ return { added, modified, deleted, unchanged };
37
+ }
38
+
39
+ export {
40
+ hashContent,
41
+ resolveChanges
42
+ };
@@ -0,0 +1,168 @@
1
+ // src/indexing/file-filter.ts
2
+ var INDEXABLE_EXTENSIONS = /* @__PURE__ */ new Set([
3
+ ".ts",
4
+ ".tsx",
5
+ ".js",
6
+ ".jsx",
7
+ ".mjs",
8
+ ".cjs",
9
+ ".py",
10
+ ".go",
11
+ ".rs",
12
+ ".java",
13
+ ".kt",
14
+ ".swift",
15
+ ".c",
16
+ ".cpp",
17
+ ".h",
18
+ ".hpp",
19
+ ".cs",
20
+ ".rb",
21
+ ".php",
22
+ ".scala",
23
+ ".sh",
24
+ ".bash",
25
+ ".zsh",
26
+ ".sql",
27
+ ".graphql",
28
+ ".gql",
29
+ ".css",
30
+ ".scss",
31
+ ".less",
32
+ ".html",
33
+ ".svelte",
34
+ ".vue",
35
+ ".md",
36
+ ".mdx",
37
+ ".json",
38
+ ".yaml",
39
+ ".yml",
40
+ ".toml",
41
+ ".xml",
42
+ ".proto",
43
+ ".tf",
44
+ ".hcl",
45
+ ".dockerfile",
46
+ ".prisma"
47
+ ]);
48
+ var IGNORED_DIRS = [
49
+ "node_modules/",
50
+ "dist/",
51
+ ".git/",
52
+ ".next/",
53
+ "__pycache__/",
54
+ ".cache/",
55
+ "coverage/",
56
+ "build/",
57
+ ".turbo/",
58
+ "vendor/",
59
+ ".venv/"
60
+ ];
61
+ var IGNORED_FILES = [
62
+ "pnpm-lock.yaml",
63
+ "package-lock.json",
64
+ "yarn.lock",
65
+ "bun.lockb",
66
+ "Cargo.lock",
67
+ "go.sum",
68
+ "poetry.lock",
69
+ "Pipfile.lock",
70
+ "composer.lock",
71
+ "Gemfile.lock"
72
+ ];
73
+ var MAX_FILE_SIZE = 100 * 1024;
74
+ function shouldIndexFile(path, size) {
75
+ if (size > MAX_FILE_SIZE) return false;
76
+ if (IGNORED_FILES.includes(path.split("/").pop() ?? "")) return false;
77
+ for (const dir of IGNORED_DIRS) {
78
+ if (path.includes(dir)) return false;
79
+ }
80
+ const ext = getExtension(path);
81
+ if (!ext) {
82
+ const basename = path.split("/").pop() ?? "";
83
+ return basename === "Dockerfile" || basename === "Makefile";
84
+ }
85
+ return INDEXABLE_EXTENSIONS.has(ext);
86
+ }
87
+ var LANGUAGE_MAP = {
88
+ ".ts": { language: "typescript", grammar: "typescript" },
89
+ ".tsx": { language: "typescript", grammar: "tsx" },
90
+ ".js": { language: "javascript", grammar: "javascript" },
91
+ ".jsx": { language: "javascript", grammar: "javascript" },
92
+ ".mjs": { language: "javascript", grammar: "javascript" },
93
+ ".cjs": { language: "javascript", grammar: "javascript" },
94
+ ".py": { language: "python", grammar: "python" },
95
+ ".go": { language: "go", grammar: "go" },
96
+ ".rs": { language: "rust", grammar: "rust" },
97
+ ".java": { language: "java", grammar: "java" },
98
+ ".kt": { language: "kotlin", grammar: "" },
99
+ ".swift": { language: "swift", grammar: "" },
100
+ ".c": { language: "c", grammar: "" },
101
+ ".cpp": { language: "cpp", grammar: "" },
102
+ ".h": { language: "c", grammar: "" },
103
+ ".hpp": { language: "cpp", grammar: "" },
104
+ ".cs": { language: "csharp", grammar: "" },
105
+ ".rb": { language: "ruby", grammar: "" },
106
+ ".php": { language: "php", grammar: "" },
107
+ ".scala": { language: "scala", grammar: "" },
108
+ ".sh": { language: "shell", grammar: "" },
109
+ ".bash": { language: "shell", grammar: "" },
110
+ ".zsh": { language: "shell", grammar: "" },
111
+ ".sql": { language: "sql", grammar: "" },
112
+ ".graphql": { language: "graphql", grammar: "" },
113
+ ".gql": { language: "graphql", grammar: "" },
114
+ ".css": { language: "css", grammar: "" },
115
+ ".scss": { language: "scss", grammar: "" },
116
+ ".less": { language: "less", grammar: "" },
117
+ ".html": { language: "html", grammar: "" },
118
+ ".svelte": { language: "svelte", grammar: "" },
119
+ ".vue": { language: "vue", grammar: "" },
120
+ ".md": { language: "markdown", grammar: "" },
121
+ ".mdx": { language: "markdown", grammar: "" },
122
+ ".json": { language: "json", grammar: "" },
123
+ ".yaml": { language: "yaml", grammar: "" },
124
+ ".yml": { language: "yaml", grammar: "" },
125
+ ".toml": { language: "toml", grammar: "" },
126
+ ".xml": { language: "xml", grammar: "" },
127
+ ".proto": { language: "protobuf", grammar: "" },
128
+ ".tf": { language: "terraform", grammar: "" },
129
+ ".hcl": { language: "hcl", grammar: "" },
130
+ ".dockerfile": { language: "dockerfile", grammar: "" },
131
+ ".prisma": { language: "prisma", grammar: "" }
132
+ };
133
+ var AST_SUPPORTED_LANGUAGES = /* @__PURE__ */ new Set([
134
+ "typescript",
135
+ "tsx",
136
+ "javascript",
137
+ "python",
138
+ "go",
139
+ "rust",
140
+ "java"
141
+ ]);
142
+ function detectLanguage(path) {
143
+ const ext = getExtension(path);
144
+ if (ext && ext in LANGUAGE_MAP) return LANGUAGE_MAP[ext].language;
145
+ const basename = path.split("/").pop() ?? "";
146
+ if (basename === "Dockerfile") return "dockerfile";
147
+ if (basename === "Makefile") return "makefile";
148
+ return "unknown";
149
+ }
150
+ function getTreeSitterGrammar(path) {
151
+ const ext = getExtension(path);
152
+ if (ext && ext in LANGUAGE_MAP) return LANGUAGE_MAP[ext].grammar;
153
+ return "";
154
+ }
155
+ function getExtension(path) {
156
+ const basename = path.split("/").pop() ?? "";
157
+ const dotIdx = basename.lastIndexOf(".");
158
+ if (dotIdx <= 0) return "";
159
+ return basename.slice(dotIdx).toLowerCase();
160
+ }
161
+
162
+ export {
163
+ INDEXABLE_EXTENSIONS,
164
+ shouldIndexFile,
165
+ AST_SUPPORTED_LANGUAGES,
166
+ detectLanguage,
167
+ getTreeSitterGrammar
168
+ };
@@ -0,0 +1,228 @@
1
+ // src/generate/index.ts
2
+ var PLACEHOLDER_PATTERNS = [
3
+ /^\w+ component is needed$/i,
4
+ /^Alternative component is more appropriate$/i,
5
+ /^Use \w+ when you need/i
6
+ ];
7
+ function filterPlaceholders(items) {
8
+ if (!items) return [];
9
+ return items.filter(
10
+ (item) => !PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(item.trim()))
11
+ );
12
+ }
13
+ function generateContext(segments, options = {}, blocks) {
14
+ const format = options.format ?? "markdown";
15
+ const compact = options.compact ?? false;
16
+ const include = {
17
+ props: options.include?.props ?? true,
18
+ variants: options.include?.variants ?? true,
19
+ usage: options.include?.usage ?? true,
20
+ relations: options.include?.relations ?? false,
21
+ code: options.include?.code ?? false
22
+ };
23
+ const sorted = [...segments].sort((a, b) => {
24
+ const catCompare = a.meta.category.localeCompare(b.meta.category);
25
+ if (catCompare !== 0) return catCompare;
26
+ return a.meta.name.localeCompare(b.meta.name);
27
+ });
28
+ if (format === "json") {
29
+ return generateJsonContext(sorted, include, compact, blocks);
30
+ }
31
+ return generateMarkdownContext(sorted, include, compact, blocks);
32
+ }
33
+ function generateMarkdownContext(segments, include, compact, blocks) {
34
+ const lines = [];
35
+ lines.push("# Design System Reference");
36
+ lines.push("");
37
+ lines.push("## Quick Reference");
38
+ lines.push("");
39
+ lines.push("| Component | Category | Use For |");
40
+ lines.push("|-----------|----------|---------|");
41
+ for (const segment of segments) {
42
+ const filteredWhen = filterPlaceholders(segment.usage.when);
43
+ const useFor = filteredWhen.slice(0, 2).join(", ") || segment.meta.description;
44
+ lines.push(`| ${segment.meta.name} | ${segment.meta.category} | ${truncate(useFor, 50)} |`);
45
+ }
46
+ lines.push("");
47
+ if (compact) {
48
+ const content2 = lines.join("\n");
49
+ return { content: content2, tokenEstimate: estimateTokens(content2) };
50
+ }
51
+ lines.push("## Components");
52
+ lines.push("");
53
+ for (const segment of segments) {
54
+ lines.push(`### ${segment.meta.name}`);
55
+ lines.push("");
56
+ const statusParts = [`**Category:** ${segment.meta.category}`];
57
+ if (segment.meta.status) {
58
+ statusParts.push(`**Status:** ${segment.meta.status}`);
59
+ }
60
+ lines.push(statusParts.join(" | "));
61
+ lines.push("");
62
+ if (segment.meta.description) {
63
+ lines.push(segment.meta.description);
64
+ lines.push("");
65
+ }
66
+ const whenFiltered = filterPlaceholders(segment.usage.when);
67
+ const whenNotFiltered = filterPlaceholders(segment.usage.whenNot);
68
+ if (include.usage && (whenFiltered.length > 0 || whenNotFiltered.length > 0)) {
69
+ if (whenFiltered.length > 0) {
70
+ lines.push("**When to use:**");
71
+ for (const when of whenFiltered) {
72
+ lines.push(`- ${when}`);
73
+ }
74
+ lines.push("");
75
+ }
76
+ if (whenNotFiltered.length > 0) {
77
+ lines.push("**When NOT to use:**");
78
+ for (const whenNot of whenNotFiltered) {
79
+ lines.push(`- ${whenNot}`);
80
+ }
81
+ lines.push("");
82
+ }
83
+ }
84
+ if (include.props && Object.keys(segment.props).length > 0) {
85
+ lines.push("**Props:**");
86
+ for (const [name, prop] of Object.entries(segment.props)) {
87
+ lines.push(`- \`${name}\`: ${formatPropType(prop)}${prop.required ? " (required)" : ""}`);
88
+ }
89
+ lines.push("");
90
+ }
91
+ if (include.variants && segment.variants.length > 0) {
92
+ const variantNames = segment.variants.map((v) => v.name).join(", ");
93
+ lines.push(`**Variants:** ${variantNames}`);
94
+ lines.push("");
95
+ if (include.code) {
96
+ for (const variant of segment.variants) {
97
+ if (variant.code) {
98
+ lines.push(`*${variant.name}:*`);
99
+ lines.push("```tsx");
100
+ lines.push(variant.code);
101
+ lines.push("```");
102
+ lines.push("");
103
+ }
104
+ }
105
+ }
106
+ }
107
+ if (include.relations && segment.relations && segment.relations.length > 0) {
108
+ lines.push("**Related:**");
109
+ for (const relation of segment.relations) {
110
+ lines.push(`- ${relation.component} (${relation.relationship}): ${relation.note}`);
111
+ }
112
+ lines.push("");
113
+ }
114
+ lines.push("---");
115
+ lines.push("");
116
+ }
117
+ if (blocks && blocks.length > 0) {
118
+ lines.push("## Blocks");
119
+ lines.push("");
120
+ lines.push("Composition patterns showing how components wire together.");
121
+ lines.push("");
122
+ for (const block of blocks) {
123
+ lines.push(`### ${block.name}`);
124
+ lines.push("");
125
+ lines.push(block.description);
126
+ lines.push("");
127
+ lines.push(`**Category:** ${block.category}`);
128
+ lines.push(`**Components:** ${block.components.join(", ")}`);
129
+ if (block.tags && block.tags.length > 0) {
130
+ lines.push(`**Tags:** ${block.tags.join(", ")}`);
131
+ }
132
+ lines.push("");
133
+ lines.push("```tsx");
134
+ lines.push(block.code);
135
+ lines.push("```");
136
+ lines.push("");
137
+ lines.push("---");
138
+ lines.push("");
139
+ }
140
+ }
141
+ const content = lines.join("\n");
142
+ return { content, tokenEstimate: estimateTokens(content) };
143
+ }
144
+ function generateJsonContext(segments, include, compact, blocks) {
145
+ const categories = [...new Set(segments.map((s) => s.meta.category))].sort();
146
+ const components = {};
147
+ for (const segment of segments) {
148
+ const component = {
149
+ category: segment.meta.category,
150
+ description: segment.meta.description
151
+ };
152
+ if (segment.meta.status) {
153
+ component.status = segment.meta.status;
154
+ }
155
+ if (!compact) {
156
+ if (include.usage) {
157
+ const whenFiltered = filterPlaceholders(segment.usage.when);
158
+ const whenNotFiltered = filterPlaceholders(segment.usage.whenNot);
159
+ if (whenFiltered.length > 0) component.whenToUse = whenFiltered;
160
+ if (whenNotFiltered.length > 0) component.whenNotToUse = whenNotFiltered;
161
+ }
162
+ if (include.props && Object.keys(segment.props).length > 0) {
163
+ component.props = {};
164
+ for (const [name, prop] of Object.entries(segment.props)) {
165
+ component.props[name] = {
166
+ type: formatPropType(prop),
167
+ description: prop.description
168
+ };
169
+ if (prop.required) component.props[name].required = true;
170
+ if (prop.default !== void 0) component.props[name].default = prop.default;
171
+ }
172
+ }
173
+ if (include.variants && segment.variants.length > 0) {
174
+ component.variants = segment.variants.map((v) => v.name);
175
+ }
176
+ if (include.relations && segment.relations && segment.relations.length > 0) {
177
+ component.relations = segment.relations.map((r) => ({
178
+ component: r.component,
179
+ relationship: r.relationship,
180
+ note: r.note
181
+ }));
182
+ }
183
+ }
184
+ components[segment.meta.name] = component;
185
+ }
186
+ const blocksMap = blocks && blocks.length > 0 ? Object.fromEntries(blocks.map((b) => [b.name, {
187
+ description: b.description,
188
+ category: b.category,
189
+ components: b.components,
190
+ code: b.code,
191
+ tags: b.tags
192
+ }])) : void 0;
193
+ const output = {
194
+ version: "1.0",
195
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
196
+ summary: {
197
+ totalComponents: segments.length,
198
+ categories,
199
+ ...blocksMap && { totalBlocks: blocks.length }
200
+ },
201
+ components,
202
+ ...blocksMap && { blocks: blocksMap }
203
+ };
204
+ const content = JSON.stringify(output, null, 2);
205
+ return { content, tokenEstimate: estimateTokens(content) };
206
+ }
207
+ function formatPropType(prop) {
208
+ if (prop.type === "enum" && prop.values) {
209
+ return prop.values.map((v) => `"${v}"`).join(" | ");
210
+ }
211
+ if (prop.default !== void 0) {
212
+ return `${prop.type} (default: ${JSON.stringify(prop.default)})`;
213
+ }
214
+ return prop.type;
215
+ }
216
+ function truncate(str, maxLength) {
217
+ if (str.length <= maxLength) return str;
218
+ return str.slice(0, maxLength - 3) + "...";
219
+ }
220
+ function estimateTokens(text) {
221
+ return Math.ceil(text.length / 4);
222
+ }
223
+
224
+ export {
225
+ PLACEHOLDER_PATTERNS,
226
+ filterPlaceholders,
227
+ generateContext
228
+ };