@oa-sdk/spec-bundler 0.1.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,115 @@
1
+ import { sliceMarkdownBySections } from "./section-slicer.js";
2
+ export const STANDARD_CONTRACT_HEADINGS = [
3
+ "Contract*",
4
+ "Interface*",
5
+ "Overview*",
6
+ "Preconditions*",
7
+ "Guarantees*",
8
+ "API*",
9
+ "Specification*",
10
+ "Rules*",
11
+ ];
12
+ /**
13
+ * Parses the document category from YAML frontmatter or infers it from path heuristics.
14
+ */
15
+ export function parseDocumentCategory(content, filePath) {
16
+ const normPath = filePath.replace(/\\/g, "/").toLowerCase();
17
+ // 1. Try YAML frontmatter parsing
18
+ const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
19
+ if (frontmatterMatch) {
20
+ const fm = frontmatterMatch[1];
21
+ const catMatch = fm.match(/^(?:category|type|kind)\s*:\s*["']?([a-zA-Z_-]+)["']?/m);
22
+ if (catMatch) {
23
+ const rawCat = catMatch[1].toLowerCase().trim();
24
+ if (rawCat === "feature" || rawCat === "features")
25
+ return "feature";
26
+ if (rawCat === "mechanism" || rawCat === "mechanisms" || rawCat === "tech" || rawCat === "strategy")
27
+ return "mechanism";
28
+ if (rawCat === "error" || rawCat === "errors")
29
+ return "error";
30
+ if (rawCat === "issue" || rawCat === "issues" || rawCat === "adr" || rawCat === "rfc")
31
+ return "issue";
32
+ if (rawCat === "convention" || rawCat === "conventions" || rawCat === "rule" || rawCat === "rules")
33
+ return "convention";
34
+ }
35
+ }
36
+ // 2. Path-based heuristics
37
+ if (normPath.includes("/features/") || normPath.includes("/feature/"))
38
+ return "feature";
39
+ if (normPath.includes("/mechanisms/") || normPath.includes("/mechanism/") || normPath.includes("/tech/") || normPath.includes("/strategies/"))
40
+ return "mechanism";
41
+ if (normPath.includes("/errors/") || normPath.includes("/error/"))
42
+ return "error";
43
+ if (normPath.includes("/issues/") || normPath.includes("/issue/") || normPath.includes("/adr/") || normPath.includes("/rfc/"))
44
+ return "issue";
45
+ if (normPath.includes("/conventions/") || normPath.includes("/convention/") || normPath.includes("/rules/"))
46
+ return "convention";
47
+ // 3. Extension fallback
48
+ if (normPath.endsWith(".tsp"))
49
+ return "mechanism";
50
+ return "feature";
51
+ }
52
+ /**
53
+ * Extracts document content according to requested fidelity level (full, contract-only, summary).
54
+ * Prevents bundle bloat by stripping non-contract details for background/upstream documents.
55
+ */
56
+ export function extractDocumentFidelity(content, category, fidelity = "full", customSections) {
57
+ if (fidelity === "full") {
58
+ if (customSections && customSections.length > 0) {
59
+ const sliceResult = sliceMarkdownBySections(content, customSections);
60
+ return { content: sliceResult.sliced, isModified: sliceResult.isSliced };
61
+ }
62
+ return { content, isModified: false };
63
+ }
64
+ if (fidelity === "contract-only") {
65
+ const headingsToExtract = customSections && customSections.length > 0
66
+ ? customSections
67
+ : STANDARD_CONTRACT_HEADINGS;
68
+ const sliceResult = sliceMarkdownBySections(content, headingsToExtract);
69
+ if (sliceResult.isSliced) {
70
+ return { content: sliceResult.sliced, isModified: true };
71
+ }
72
+ // If no contract headings explicitly matched, fallback to preamble + frontmatter
73
+ const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
74
+ const frontmatter = frontmatterMatch ? frontmatterMatch[0] : "";
75
+ const body = content.slice(frontmatter.length);
76
+ const bodyLines = body.split(/\r?\n/);
77
+ const preambleLines = [];
78
+ for (const line of bodyLines) {
79
+ if (/^#{2,6}\s+/.test(line))
80
+ break; // Stop at first H2+ section
81
+ preambleLines.push(line);
82
+ }
83
+ const fallbackContent = `${frontmatter}${preambleLines.join("\n").trim()}\n`;
84
+ return { content: fallbackContent, isModified: true };
85
+ }
86
+ if (fidelity === "summary") {
87
+ // 1. Separate frontmatter
88
+ const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
89
+ const frontmatter = frontmatterMatch ? frontmatterMatch[0] : "";
90
+ // 2. Extract summary field if declared in frontmatter
91
+ let summaryText = "";
92
+ if (frontmatterMatch) {
93
+ const sumMatch = frontmatterMatch[1].match(/^summary\s*:\s*["']?([^"'\r\n]+)["']?/m);
94
+ if (sumMatch) {
95
+ summaryText = sumMatch[1].trim();
96
+ }
97
+ }
98
+ // 3. Extract title and first body paragraph
99
+ const body = content.slice(frontmatter.length);
100
+ const lines = body.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
101
+ const titleLine = lines.find((l) => l.startsWith("# ")) || `# Document (${category})`;
102
+ const firstParagraph = lines.find((l) => !l.startsWith("#") && !l.startsWith("-")) || "";
103
+ const summaryContent = `${frontmatter}
104
+ ${titleLine}
105
+
106
+ *(Category: \`${category}\` | Fidelity: \`summary\`)*
107
+
108
+ > [!NOTE]
109
+ > ${summaryText || firstParagraph || "No detailed summary declared."}
110
+ `;
111
+ return { content: summaryContent, isModified: true };
112
+ }
113
+ return { content, isModified: false };
114
+ }
115
+ //# sourceMappingURL=category-parser.js.map
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map