docorbit 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.
Files changed (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +660 -0
  3. package/apps/cli/bin/docorbit.js +8 -0
  4. package/apps/cli/src/commands/add.ts +44 -0
  5. package/apps/cli/src/commands/api.ts +38 -0
  6. package/apps/cli/src/commands/context.ts +47 -0
  7. package/apps/cli/src/commands/dashboard.ts +55 -0
  8. package/apps/cli/src/commands/diff.ts +30 -0
  9. package/apps/cli/src/commands/evaluate.ts +133 -0
  10. package/apps/cli/src/commands/examples.ts +39 -0
  11. package/apps/cli/src/commands/export.ts +89 -0
  12. package/apps/cli/src/commands/impact.ts +31 -0
  13. package/apps/cli/src/commands/init.ts +69 -0
  14. package/apps/cli/src/commands/inspect.ts +30 -0
  15. package/apps/cli/src/commands/mcp.ts +72 -0
  16. package/apps/cli/src/commands/pitfalls.ts +38 -0
  17. package/apps/cli/src/commands/recipes.ts +35 -0
  18. package/apps/cli/src/commands/search.ts +48 -0
  19. package/apps/cli/src/commands/update.ts +73 -0
  20. package/apps/cli/src/commands/verify.ts +48 -0
  21. package/apps/cli/src/formatters/colors.ts +23 -0
  22. package/apps/cli/src/formatters/inspection.ts +102 -0
  23. package/apps/cli/src/formatters/knowledge.ts +272 -0
  24. package/apps/cli/src/formatters/retrieval.ts +74 -0
  25. package/apps/cli/src/formatters/terminal.ts +6 -0
  26. package/apps/cli/src/formatters/verification.ts +126 -0
  27. package/apps/cli/src/index.ts +409 -0
  28. package/bin/docorbit.js +8 -0
  29. package/package.json +46 -0
  30. package/packages/core/src/dashboard/server.ts +314 -0
  31. package/packages/core/src/dashboard/ui.ts +586 -0
  32. package/packages/core/src/implementation-service.ts +451 -0
  33. package/packages/core/src/index.ts +7 -0
  34. package/packages/core/src/inspector.ts +71 -0
  35. package/packages/core/src/pipeline.ts +331 -0
  36. package/packages/crawler/src/config.ts +12 -0
  37. package/packages/crawler/src/fetcher.ts +185 -0
  38. package/packages/crawler/src/index.ts +2 -0
  39. package/packages/discovery/src/index.ts +31 -0
  40. package/packages/discovery/src/provider.ts +47 -0
  41. package/packages/discovery/src/providers/generic.ts +98 -0
  42. package/packages/discovery/src/providers/github.ts +61 -0
  43. package/packages/discovery/src/providers/llms-txt.ts +73 -0
  44. package/packages/discovery/src/providers/markdown.ts +48 -0
  45. package/packages/discovery/src/providers/openapi.ts +91 -0
  46. package/packages/discovery/src/providers/sitemap.ts +62 -0
  47. package/packages/discovery/src/providers/skill.ts +54 -0
  48. package/packages/discovery/src/ranker.ts +123 -0
  49. package/packages/evaluation/src/dataset.ts +963 -0
  50. package/packages/evaluation/src/index.ts +8 -0
  51. package/packages/evaluation/src/runner.ts +241 -0
  52. package/packages/evaluation/src/strategies/context7-runner.ts +269 -0
  53. package/packages/evaluation/src/strategies/docorbit-runner.ts +228 -0
  54. package/packages/evaluation/src/strategies/firecrawl-runner.ts +172 -0
  55. package/packages/evaluation/src/strategies/web-search-runner.ts +194 -0
  56. package/packages/evaluation/src/types.ts +34 -0
  57. package/packages/evaluation/src/version-matcher.ts +73 -0
  58. package/packages/export/src/agents-md.ts +200 -0
  59. package/packages/export/src/claude-md.ts +141 -0
  60. package/packages/export/src/docs-map.ts +150 -0
  61. package/packages/export/src/index.ts +6 -0
  62. package/packages/export/src/llms-txt.ts +96 -0
  63. package/packages/export/src/service.ts +250 -0
  64. package/packages/export/src/skill-md.ts +128 -0
  65. package/packages/mcp/src/index.ts +46 -0
  66. package/packages/mcp/src/resources/index.ts +189 -0
  67. package/packages/mcp/src/server.ts +278 -0
  68. package/packages/mcp/src/tools/analyze-impact.ts +74 -0
  69. package/packages/mcp/src/tools/check-api.ts +86 -0
  70. package/packages/mcp/src/tools/diff-docs.ts +68 -0
  71. package/packages/mcp/src/tools/export-context.ts +73 -0
  72. package/packages/mcp/src/tools/find-api.ts +99 -0
  73. package/packages/mcp/src/tools/find-example.ts +100 -0
  74. package/packages/mcp/src/tools/find-pitfall.ts +94 -0
  75. package/packages/mcp/src/tools/find-recipe.ts +98 -0
  76. package/packages/mcp/src/tools/get-doc.ts +130 -0
  77. package/packages/mcp/src/tools/get-docs-map.ts +64 -0
  78. package/packages/mcp/src/tools/get-version.ts +118 -0
  79. package/packages/mcp/src/tools/implementation-context.ts +88 -0
  80. package/packages/mcp/src/tools/index.ts +59 -0
  81. package/packages/mcp/src/tools/list-sources.ts +85 -0
  82. package/packages/mcp/src/tools/search-docs.ts +123 -0
  83. package/packages/mcp/src/tools/types.ts +28 -0
  84. package/packages/mcp/src/transports/http.ts +256 -0
  85. package/packages/mcp/src/transports/stdio.ts +105 -0
  86. package/packages/mcp/src/transports/types.ts +6 -0
  87. package/packages/mcp/src/types.ts +102 -0
  88. package/packages/normalizer/src/example-indexer.ts +240 -0
  89. package/packages/normalizer/src/html.ts +253 -0
  90. package/packages/normalizer/src/index.ts +8 -0
  91. package/packages/normalizer/src/llms.ts +83 -0
  92. package/packages/normalizer/src/openapi/endpoint-parser.ts +406 -0
  93. package/packages/normalizer/src/openapi/schema-resolver.ts +111 -0
  94. package/packages/normalizer/src/openapi.ts +2 -0
  95. package/packages/normalizer/src/page.ts +184 -0
  96. package/packages/normalizer/src/pitfall-extractor.ts +190 -0
  97. package/packages/normalizer/src/slicer.ts +455 -0
  98. package/packages/retrieval/src/engine.ts +120 -0
  99. package/packages/retrieval/src/index.ts +7 -0
  100. package/packages/retrieval/src/intent.ts +43 -0
  101. package/packages/retrieval/src/packer.ts +145 -0
  102. package/packages/retrieval/src/recipe-engine.ts +313 -0
  103. package/packages/retrieval/src/scorer.ts +139 -0
  104. package/packages/retrieval/src/weights.ts +31 -0
  105. package/packages/security/src/annotations.ts +112 -0
  106. package/packages/security/src/index.ts +2 -0
  107. package/packages/security/src/ssrf.ts +153 -0
  108. package/packages/shared/src/errors.ts +53 -0
  109. package/packages/shared/src/hashing.ts +23 -0
  110. package/packages/shared/src/index.ts +3 -0
  111. package/packages/shared/src/types.ts +881 -0
  112. package/packages/storage/src/db.ts +72 -0
  113. package/packages/storage/src/index.ts +11 -0
  114. package/packages/storage/src/interfaces.ts +115 -0
  115. package/packages/storage/src/repositories/api-repository.ts +219 -0
  116. package/packages/storage/src/repositories/chunk-repository.ts +316 -0
  117. package/packages/storage/src/repositories/example-repository.ts +206 -0
  118. package/packages/storage/src/repositories/page-repository.ts +205 -0
  119. package/packages/storage/src/repositories/pitfall-repository.ts +188 -0
  120. package/packages/storage/src/repositories/source-repository.ts +205 -0
  121. package/packages/storage/src/repository.ts +256 -0
  122. package/packages/storage/src/schema.ts +269 -0
  123. package/packages/storage/src/search-tokens.ts +28 -0
  124. package/packages/verification/src/diff-engine.ts +258 -0
  125. package/packages/verification/src/extractor.ts +339 -0
  126. package/packages/verification/src/impact-scanner.ts +203 -0
  127. package/packages/verification/src/index.ts +5 -0
  128. package/packages/verification/src/services.ts +238 -0
  129. package/packages/verification/src/verifier.ts +375 -0
  130. package/packages/workspace/src/detector.ts +143 -0
  131. package/packages/workspace/src/ecosystems/cargo.ts +84 -0
  132. package/packages/workspace/src/ecosystems/composer.ts +42 -0
  133. package/packages/workspace/src/ecosystems/go.ts +54 -0
  134. package/packages/workspace/src/ecosystems/index.ts +34 -0
  135. package/packages/workspace/src/ecosystems/maven.ts +34 -0
  136. package/packages/workspace/src/ecosystems/npm.ts +83 -0
  137. package/packages/workspace/src/ecosystems/pub.ts +40 -0
  138. package/packages/workspace/src/ecosystems/pypi.ts +100 -0
  139. package/packages/workspace/src/ecosystems/rubygems.ts +30 -0
  140. package/packages/workspace/src/ecosystems/types.ts +18 -0
  141. package/packages/workspace/src/index.ts +5 -0
  142. package/packages/workspace/src/lockfile.ts +194 -0
  143. package/packages/workspace/src/resolver.ts +234 -0
  144. package/packages/workspace/src/semver.ts +259 -0
@@ -0,0 +1,73 @@
1
+ import type { BenchmarkTaskDef } from './types.ts';
2
+
3
+ /**
4
+ * Semantically evaluates whether retrieved documentation content matches
5
+ * the target version requirements of a benchmark task.
6
+ *
7
+ * Rather than looking for an arbitrary literal version string (e.g. "16.12" in HTTP API docs
8
+ * which only use date versions, or "2.6" in tutorial prose on GitHub main), this inspects
9
+ * whether the retrieved documentation teaches the correct version-specific API signatures,
10
+ * syntax, and conventions — and does not teach fatal breaking-change anti-patterns.
11
+ */
12
+ export function isVersionContentMatch(task: BenchmarkTaskDef, content: string): boolean {
13
+ if (!content || content.trim().length === 0) return false;
14
+ const lower = content.toLowerCase();
15
+
16
+ switch (task.id) {
17
+ case 'train_nextjs_14_route_sync':
18
+ // Next.js 14: route params are synchronous. If doc teaches "await params" or "Promise<{", it's Next.js 15+!
19
+ if (lower.includes('await params') || lower.includes('params: promise') || lower.includes('promise<{')) {
20
+ return false;
21
+ }
22
+ return lower.includes('14') || lower.includes('params.id') || lower.includes('{ params }') || lower.includes('route handler');
23
+
24
+ case 'eval_nextjs_15_async_params':
25
+ // Next.js 15: route params are asynchronous Promises.
26
+ return lower.includes('await params') || lower.includes('params: promise') || lower.includes('promise<{') || lower.includes('next.js 15');
27
+
28
+ case 'train_stripe_v1_charges':
29
+ // Legacy Stripe: direct charges endpoint /v1/charges
30
+ if (lower.includes('payment_intents') && !lower.includes('/v1/charges') && !lower.includes('charges.create')) {
31
+ return false;
32
+ }
33
+ return lower.includes('/v1/charges') || lower.includes('charges.create');
34
+
35
+ case 'eval_stripe_payment_intents_2024':
36
+ // Modern Stripe: PaymentIntents endpoint
37
+ return lower.includes('payment_intents') || lower.includes('paymentintents');
38
+
39
+ case 'train_pydantic_v1_validator':
40
+ // Pydantic v1: uses @validator. If doc teaches @field_validator, it's Pydantic v2!
41
+ if (lower.includes('field_validator')) {
42
+ return false;
43
+ }
44
+ return lower.includes('@validator') || lower.includes('validator(');
45
+
46
+ case 'eval_pydantic_v2_field_validator':
47
+ // Pydantic v2: uses @field_validator
48
+ return lower.includes('field_validator');
49
+
50
+ case 'train_fastapi_095_sync_dep':
51
+ // FastAPI 0.95 dependency injection with Depends
52
+ return lower.includes('depends(') || lower.includes('depends');
53
+
54
+ case 'eval_fastapi_100_lifespan':
55
+ // FastAPI 0.100+: uses lifespan async context manager
56
+ return lower.includes('lifespan');
57
+
58
+ case 'eval_tokio_postgres_07':
59
+ // tokio-postgres 0.7: connect + spawn connection task
60
+ return lower.includes('connect') && (lower.includes('tokio::spawn') || lower.includes('spawn') || lower.includes('connection'));
61
+
62
+ case 'eval_gin_gonic_v19':
63
+ // Gin 1.9+: ShouldBindJSON
64
+ return lower.includes('shouldbindjson');
65
+
66
+ default: {
67
+ // Fallback: check SemVer major/minor string
68
+ const parts = task.targetVersion.split('.');
69
+ const majorMinor = parts.slice(0, 2).join('.');
70
+ return lower.includes(majorMinor) || lower.includes(`v${parts[0]}`);
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,200 @@
1
+ import type {
2
+ ApiEndpoint,
3
+ Pitfall,
4
+ Recipe,
5
+ ProjectDependency,
6
+ VersionResolutionResult,
7
+ } from '../../shared/src/index.ts';
8
+
9
+ export interface AgentsMdExportData {
10
+ projectDir?: string;
11
+ docVersion?: string;
12
+ dependencies: Array<{
13
+ dep: ProjectDependency;
14
+ resolution?: VersionResolutionResult;
15
+ }>;
16
+ endpoints: ApiEndpoint[];
17
+ pitfalls: Pitfall[];
18
+ recipes: Recipe[];
19
+ sources: string[];
20
+ }
21
+
22
+ /**
23
+ * Generates an AGENTS.md document grounded strictly in indexed documentation.
24
+ * Preserves exact versions, endpoint signatures, critical caveats, and provenance.
25
+ * Fully deterministic output.
26
+ */
27
+ export function generateAgentsMd(data: AgentsMdExportData): string {
28
+ const lines: string[] = [];
29
+
30
+ lines.push('# AGENTS.md — Documentation & Version Intelligence Context');
31
+ lines.push('');
32
+ lines.push('> [!NOTE]');
33
+ lines.push('> **Strict Evidence-Grounding**: All API signatures, version constraints, and pitfalls in this document');
34
+ lines.push('> are extracted deterministically from indexed documentation. External documentation content is treated');
35
+ lines.push('> as untrusted input (`untrusted: true`).');
36
+ lines.push('');
37
+
38
+ // 1. Version Matrix & Dependencies
39
+ lines.push('## 1. Project Dependencies & Version Matrix');
40
+ lines.push('');
41
+ if (data.dependencies.length > 0) {
42
+ lines.push('| Dependency | Ecosystem | Requested | Resolved / Doc Version | Match Type | Confidence |');
43
+ lines.push('| :--- | :--- | :--- | :--- | :--- | :--- |');
44
+
45
+ const sortedDeps = [...data.dependencies].sort((a, b) => a.dep.name.localeCompare(b.dep.name));
46
+ for (const item of sortedDeps) {
47
+ const dep = item.dep;
48
+ const res = item.resolution;
49
+ const resolved = res?.targetVersion || dep.resolvedVersion || 'unresolved';
50
+ const matchType = res?.matchedBy || (res?.targetVersion ? 'resolved' : 'unresolved');
51
+ const conf = res ? `${(res.confidence * 100).toFixed(0)}%` : 'N/A';
52
+ lines.push(`| \`${dep.name}\` | ${dep.ecosystem} | \`${dep.requestedVersion}\` | \`${resolved}\` | \`${matchType}\` | ${conf} |`);
53
+ }
54
+ lines.push('');
55
+ } else if (data.docVersion) {
56
+ lines.push(`- **Target Documentation Version Filter**: \`${data.docVersion}\``);
57
+ lines.push('');
58
+ } else {
59
+ lines.push('- *No local package manifests detected; using globally indexed documentation.*');
60
+ lines.push('');
61
+ }
62
+
63
+ // 2. Core API Contracts
64
+ lines.push('## 2. Core API Contracts & Signatures');
65
+ lines.push('');
66
+ if (data.endpoints.length > 0) {
67
+ const sortedEndpoints = [...data.endpoints].sort((a, b) => {
68
+ const cmp = a.path.localeCompare(b.path);
69
+ return cmp !== 0 ? cmp : a.method.localeCompare(b.method);
70
+ });
71
+
72
+ for (const ep of sortedEndpoints) {
73
+ const depBadge = ep.deprecated ? ' `[DEPRECATED]`' : '';
74
+ const versionTag = ep.docVersion ? ` (version: \`${ep.docVersion}\`)` : '';
75
+ lines.push(`### \`${ep.method.toUpperCase()} ${ep.path}\`${depBadge}${versionTag}`);
76
+ if (ep.summary) {
77
+ lines.push(`${ep.summary}`);
78
+ }
79
+ lines.push('');
80
+
81
+ if (ep.parameters && ep.parameters.length > 0) {
82
+ lines.push('**Parameters:**');
83
+ const sortedParams = [...ep.parameters].sort((a, b) => a.name.localeCompare(b.name));
84
+ for (const p of sortedParams) {
85
+ const req = p.required ? '**required**' : 'optional';
86
+ const type = p.type || 'string';
87
+ const desc = p.description ? ` — ${p.description}` : '';
88
+ lines.push(`- \`${p.name}\` (\`${p.in}\`, ${req}, type: \`${type}\`)${desc}`);
89
+ }
90
+ lines.push('');
91
+ }
92
+
93
+ if (ep.requestSchema) {
94
+ lines.push('**Request Body Schema:**');
95
+ lines.push('```json');
96
+ lines.push(JSON.stringify(ep.requestSchema, null, 2));
97
+ lines.push('```');
98
+ lines.push('');
99
+ }
100
+
101
+ if (ep.responseSchema) {
102
+ lines.push('**Response Schema:**');
103
+ lines.push('```json');
104
+ lines.push(JSON.stringify(ep.responseSchema, null, 2));
105
+ lines.push('```');
106
+ lines.push('');
107
+ }
108
+ }
109
+ } else {
110
+ lines.push('- *No structured API endpoints indexed for this version/scope.*');
111
+ lines.push('');
112
+ }
113
+
114
+ // 3. Critical Pitfalls & Breaking Changes
115
+ lines.push('## 3. Critical Pitfalls & Breaking Changes');
116
+ lines.push('');
117
+ if (data.pitfalls.length > 0) {
118
+ const sortedPitfalls = [...data.pitfalls].sort((a, b) => {
119
+ const severityOrder: Record<string, number> = { error: 0, warning: 1, info: 2 };
120
+ const sA = severityOrder[a.severity || 'warning'] ?? 1;
121
+ const sB = severityOrder[b.severity || 'warning'] ?? 1;
122
+ if (sA !== sB) return sA - sB;
123
+ return a.title.localeCompare(b.title);
124
+ });
125
+
126
+ for (const pf of sortedPitfalls) {
127
+ const kindLabel = pf.kind.toUpperCase().replace('_', ' ');
128
+ const versionInfo = pf.affectedVersions?.target || pf.docVersion ? ` (Version: \`${pf.affectedVersions?.target || pf.docVersion}\`)` : '';
129
+ lines.push(`### [${kindLabel}] ${pf.title}${versionInfo}`);
130
+ lines.push(`- **Severity**: \`${pf.severity || 'warning'}\``);
131
+ lines.push(`- **Evidence**: ${pf.message}`);
132
+ if (pf.mitigation) {
133
+ lines.push(`- **Mitigation**: ${pf.mitigation}`);
134
+ }
135
+ if (pf.provenance?.sourceUrl) {
136
+ lines.push(`- **Provenance**: [${pf.provenance.sourceUrl}](${pf.provenance.sourceUrl})`);
137
+ }
138
+ lines.push('');
139
+ }
140
+ } else {
141
+ lines.push('- *No explicit pitfalls or warnings indexed for this version/scope.*');
142
+ lines.push('');
143
+ }
144
+
145
+ // 4. Grounded Recipes
146
+ if (data.recipes.length > 0) {
147
+ lines.push('## 4. Evidence-Grounded Implementation Recipes');
148
+ lines.push('');
149
+ for (const r of data.recipes) {
150
+ lines.push(`### Recipe: ${r.title}`);
151
+ lines.push(`**Goal**: ${r.goal}`);
152
+ lines.push('');
153
+ if (r.prerequisites.length > 0) {
154
+ lines.push('**Prerequisites:**');
155
+ for (const pre of r.prerequisites) {
156
+ const evidenceTag = pre.evidenceLevel === 'documented_fact' ? '`[DOCUMENTED FACT]`' : '`[INFERRED]`';
157
+ lines.push(`- ${evidenceTag} ${pre.description}`);
158
+ }
159
+ lines.push('');
160
+ }
161
+
162
+ if (r.steps.length > 0) {
163
+ lines.push('**Implementation Steps:**');
164
+ for (const step of r.steps) {
165
+ const evidenceTag = step.evidenceLevel === 'documented_fact' ? '`[DOCUMENTED FACT]`' : '`[INFERRED]`';
166
+ lines.push(`${step.stepNumber}. ${evidenceTag} **${step.title}**: ${step.action}`);
167
+ if (step.codeSnippet) {
168
+ lines.push(' ```' + (step.codeSnippet.language || 'typescript'));
169
+ lines.push(step.codeSnippet.code.trim().split('\n').map(l => ' ' + l).join('\n'));
170
+ lines.push(' ```');
171
+ }
172
+ }
173
+ lines.push('');
174
+ }
175
+
176
+ if (r.validationSteps.length > 0) {
177
+ lines.push('**Evidence-Based Validation Steps:**');
178
+ for (const val of r.validationSteps) {
179
+ lines.push(`- **Verify**: ${val.assertion} (Expected: \`${val.expectedOutcome}\`)`);
180
+ }
181
+ lines.push('');
182
+ }
183
+ }
184
+ }
185
+
186
+ // 5. Provenance & Security Notice
187
+ lines.push('## 5. Indexed Sources & Provenance');
188
+ lines.push('');
189
+ if (data.sources.length > 0) {
190
+ const sortedSources = [...data.sources].sort();
191
+ for (const src of sortedSources) {
192
+ lines.push(`- Source: \`${src}\` (Status: \`indexed\`, Security: \`untrusted: true\`)`);
193
+ }
194
+ } else {
195
+ lines.push('- *No sources indexed.*');
196
+ }
197
+ lines.push('');
198
+
199
+ return lines.join('\n');
200
+ }
@@ -0,0 +1,141 @@
1
+ import type {
2
+ ApiEndpoint,
3
+ Pitfall,
4
+ ProjectDependency,
5
+ VersionResolutionResult,
6
+ } from '../../shared/src/index.ts';
7
+
8
+ export interface ClaudeMdExportData {
9
+ projectDir?: string;
10
+ docVersion?: string;
11
+ dependencies: Array<{
12
+ dep: ProjectDependency;
13
+ resolution?: VersionResolutionResult;
14
+ }>;
15
+ endpoints: ApiEndpoint[];
16
+ pitfalls: Pitfall[];
17
+ sources: string[];
18
+ }
19
+
20
+ /**
21
+ * Generates a CLAUDE.md developer and agent guide tailored for Claude Code.
22
+ * Extracts deterministic rules, caveats, verification CLI commands, and API signatures.
23
+ */
24
+ export function generateClaudeMd(data: ClaudeMdExportData): string {
25
+ const lines: string[] = [];
26
+
27
+ lines.push('# CLAUDE.md — Agent Working Rules & Documentation Contracts');
28
+ lines.push('');
29
+ lines.push('> [!IMPORTANT]');
30
+ lines.push('> **Version-Grounded Rules**: This codebase uses DocOrbit for deterministic documentation intelligence.');
31
+ lines.push('> Never hallucinate or assume unpinned library versions. All rules and caveats below are extracted');
32
+ lines.push('> from authoritative indexed documentation (`untrusted: true`).');
33
+ lines.push('');
34
+
35
+ // 1. Architecture & Tech Stack
36
+ lines.push('## 1. Project Architecture & Dependencies');
37
+ lines.push('');
38
+ if (data.dependencies.length > 0) {
39
+ const sortedDeps = [...data.dependencies].sort((a, b) => a.dep.name.localeCompare(b.dep.name));
40
+ for (const item of sortedDeps) {
41
+ const dep = item.dep;
42
+ const res = item.resolution;
43
+ const ver = res?.targetVersion || dep.resolvedVersion || dep.requestedVersion;
44
+ const confidence = res ? ` (doc match confidence: ${(res.confidence * 100).toFixed(0)}%)` : '';
45
+ lines.push(`- **${dep.name}**: \`${ver}\` [${dep.ecosystem}]${confidence}`);
46
+ }
47
+ lines.push('');
48
+ } else if (data.docVersion) {
49
+ lines.push(`- Pinned Documentation Scope: \`${data.docVersion}\``);
50
+ lines.push('');
51
+ } else {
52
+ lines.push('- *No project manifests detected; relying on indexed documentation.*');
53
+ lines.push('');
54
+ }
55
+
56
+ // 2. Deterministic Verification Commands
57
+ lines.push('## 2. DocOrbit Verification Commands');
58
+ lines.push('');
59
+ lines.push('Run these commands before committing generated code:');
60
+ lines.push('```bash');
61
+ lines.push('# Verify generated code snippet or file against indexed schemas');
62
+ lines.push('docorbit verify "<code-or-file-path>"');
63
+ lines.push('');
64
+ lines.push('# Check documentation diff across versions (e.g. v14 to v15)');
65
+ lines.push('docorbit diff --from v14 --to v15');
66
+ lines.push('');
67
+ lines.push('# Scan project repository for code impacted by API breaking changes');
68
+ lines.push('docorbit impact --from v14 --to v15 --project .');
69
+ lines.push('```');
70
+ lines.push('');
71
+
72
+ // 3. Hard Rules & Caveats (From Pitfalls)
73
+ lines.push('## 3. Hard Rules & Caveats');
74
+ lines.push('');
75
+ if (data.pitfalls.length > 0) {
76
+ const sortedPitfalls = [...data.pitfalls].sort((a, b) => {
77
+ const severityOrder: Record<string, number> = { error: 0, warning: 1, info: 2 };
78
+ const sA = severityOrder[a.severity || 'warning'] ?? 1;
79
+ const sB = severityOrder[b.severity || 'warning'] ?? 1;
80
+ if (sA !== sB) return sA - sB;
81
+ return a.title.localeCompare(b.title);
82
+ });
83
+
84
+ for (const pf of sortedPitfalls) {
85
+ const prefix = pf.kind === 'removed' || pf.kind === 'breaking_change' || pf.severity === 'error' ? 'NEVER' : 'ALWAYS';
86
+ const versionNote = pf.affectedVersions?.target || pf.docVersion ? ` (Version: \`${pf.affectedVersions?.target || pf.docVersion}\`)` : '';
87
+ lines.push(`- **${prefix}**: ${pf.title}${versionNote}`);
88
+ lines.push(` - Caveat: ${pf.message}`);
89
+ if (pf.mitigation) {
90
+ lines.push(` - Fix: ${pf.mitigation}`);
91
+ }
92
+ }
93
+ lines.push('');
94
+ } else {
95
+ lines.push('- *No explicit warnings or pitfalls recorded.*');
96
+ lines.push('');
97
+ }
98
+
99
+ // 4. Verified API Endpoints
100
+ lines.push('## 4. Key API Signatures');
101
+ lines.push('');
102
+ if (data.endpoints.length > 0) {
103
+ const sortedEndpoints = [...data.endpoints].sort((a, b) => {
104
+ const cmp = a.path.localeCompare(b.path);
105
+ return cmp !== 0 ? cmp : a.method.localeCompare(b.method);
106
+ });
107
+
108
+ for (const ep of sortedEndpoints) {
109
+ const req = ep.parameters?.filter(p => p.required).map(p => p.name).join(', ') || 'none';
110
+ const opt = ep.parameters?.filter(p => !p.required).map(p => p.name).join(', ') || 'none';
111
+ const depNotice = ep.deprecated ? ' *(deprecated)*' : '';
112
+ lines.push(`- \`${ep.method.toUpperCase()} ${ep.path}\`${depNotice}:`);
113
+ if (ep.summary) {
114
+ lines.push(` - Summary: ${ep.summary}`);
115
+ }
116
+ lines.push(` - Required parameters: \`${req}\``);
117
+ if (opt !== 'none') {
118
+ lines.push(` - Optional parameters: \`${opt}\``);
119
+ }
120
+ }
121
+ lines.push('');
122
+ } else {
123
+ lines.push('- *No indexed endpoints.*');
124
+ lines.push('');
125
+ }
126
+
127
+ // 5. Security & Provenance
128
+ lines.push('## 5. Provenance Notice');
129
+ lines.push('');
130
+ lines.push('All external documentation indexed by DocOrbit is treated as untrusted third-party input.');
131
+ if (data.sources.length > 0) {
132
+ const sortedSources = [...data.sources].sort();
133
+ lines.push('Sources:');
134
+ for (const s of sortedSources) {
135
+ lines.push(`- \`${s}\``);
136
+ }
137
+ }
138
+ lines.push('');
139
+
140
+ return lines.join('\n');
141
+ }
@@ -0,0 +1,150 @@
1
+ import type {
2
+ NormalizedPage,
3
+ DocumentChunk,
4
+ ApiEndpoint,
5
+ Pitfall,
6
+ DiscoveredSource,
7
+ DocumentationMapNode,
8
+ DocumentationMapResult,
9
+ } from '../../shared/src/index.ts';
10
+
11
+ export interface DocsMapInputData {
12
+ sources: DiscoveredSource[];
13
+ pages: NormalizedPage[];
14
+ chunks?: DocumentChunk[];
15
+ endpoints?: ApiEndpoint[];
16
+ pitfalls?: Pitfall[];
17
+ docVersion?: string;
18
+ }
19
+
20
+ /**
21
+ * Builds a comprehensive, hierarchical documentation map with token estimates and provenance.
22
+ * Fully deterministic.
23
+ */
24
+ export function buildDocumentationMap(data: DocsMapInputData): DocumentationMapResult {
25
+ const pages = [...data.pages];
26
+ const sources = [...data.sources];
27
+ const endpoints = data.endpoints || [];
28
+ const pitfalls = data.pitfalls || [];
29
+ const chunks = data.chunks || [];
30
+
31
+ // Group chunks by pageId
32
+ const chunkCountByPage = new Map<string, number>();
33
+ const tokenEstimateByPage = new Map<string, number>();
34
+
35
+ for (const chunk of chunks) {
36
+ chunkCountByPage.set(chunk.pageId, (chunkCountByPage.get(chunk.pageId) || 0) + 1);
37
+ const tokens = chunk.tokenEstimate || 0;
38
+ tokenEstimateByPage.set(chunk.pageId, (tokenEstimateByPage.get(chunk.pageId) || 0) + tokens);
39
+ }
40
+
41
+ // Group endpoints by pageId
42
+ const apisByPage = new Map<string, string[]>();
43
+ for (const ep of endpoints) {
44
+ if (!apisByPage.has(ep.pageId)) {
45
+ apisByPage.set(ep.pageId, []);
46
+ }
47
+ apisByPage.get(ep.pageId)!.push(`${ep.method.toUpperCase()} ${ep.path}`);
48
+ }
49
+
50
+ // Group pitfalls by pageId
51
+ const pitfallsByPage = new Map<string, number>();
52
+ for (const pf of pitfalls) {
53
+ pitfallsByPage.set(pf.pageId, (pitfallsByPage.get(pf.pageId) || 0) + 1);
54
+ }
55
+
56
+ // Build page nodes
57
+ const sortedPages = pages.sort((a, b) => a.url.localeCompare(b.url));
58
+ const pageNodes: DocumentationMapNode[] = sortedPages.map(p => {
59
+ const chunkCount = chunkCountByPage.get(p.id) || (p.headings ? Math.max(1, p.headings.length) : 1);
60
+ const estimatedTokens = tokenEstimateByPage.get(p.id) || p.estimatedTokens || 150;
61
+ const pageApis = apisByPage.get(p.id) || [];
62
+ const pagePitfalls = pitfallsByPage.get(p.id) || 0;
63
+
64
+ return {
65
+ id: p.id,
66
+ title: p.title || p.url,
67
+ url: p.url,
68
+ version: p.provenance?.versionTag || data.docVersion,
69
+ sourceId: p.sourceId,
70
+ chunkCount,
71
+ estimatedTokens,
72
+ headings: (p.headings || []).map(h => typeof h === 'string' ? h : (h as any).text || ''),
73
+ apis: pageApis.length > 0 ? pageApis : undefined,
74
+ pitfallCount: pagePitfalls > 0 ? pagePitfalls : undefined,
75
+ untrusted: true,
76
+ };
77
+ });
78
+
79
+ const totalPages = pageNodes.length;
80
+ const totalChunks = pageNodes.reduce((acc, p) => acc + p.chunkCount, 0);
81
+ const totalEstimatedTokens = pageNodes.reduce((acc, p) => acc + p.estimatedTokens, 0);
82
+
83
+ // Group by source
84
+ const sourceSummaries = sources.map(s => {
85
+ const sourcePages = pageNodes.filter(p => p.sourceId === s.id);
86
+ return {
87
+ id: s.id,
88
+ url: s.url,
89
+ pageCount: sourcePages.length,
90
+ chunkCount: sourcePages.reduce((acc, p) => acc + p.chunkCount, 0),
91
+ };
92
+ });
93
+
94
+ // Generate Markdown representation
95
+ const lines: string[] = [];
96
+ lines.push('# Documentation Map & Token Footprint');
97
+ lines.push('');
98
+ lines.push(`- **Total Indexed Sources**: ${sources.length}`);
99
+ lines.push(`- **Total Pages**: ${totalPages}`);
100
+ lines.push(`- **Total Chunks**: ${totalChunks}`);
101
+ lines.push(`- **Total Estimated Tokens**: ~${totalEstimatedTokens.toLocaleString()}`);
102
+ lines.push('- **Security Boundary**: `untrusted: true`');
103
+ lines.push('');
104
+
105
+ lines.push('## Documentation Tree');
106
+ lines.push('');
107
+
108
+ for (const s of sources) {
109
+ lines.push(`### Source: \`${s.url}\``);
110
+ const sourcePages = pageNodes.filter(p => p.sourceId === s.id);
111
+ if (sourcePages.length === 0) {
112
+ lines.push(' - *(No pages indexed under this source)*');
113
+ } else {
114
+ for (const p of sourcePages) {
115
+ const tokenBadge = `~${p.estimatedTokens} tokens`;
116
+ const chunkBadge = `${p.chunkCount} chunks`;
117
+ const verBadge = p.version ? ` [version: ${p.version}]` : '';
118
+ lines.push(` - **[${p.title}](${p.url})** (${chunkBadge}, ${tokenBadge})${verBadge}`);
119
+
120
+ if (p.headings && p.headings.length > 0) {
121
+ for (const h of p.headings.slice(0, 5)) {
122
+ lines.push(` - section: ${h}`);
123
+ }
124
+ if (p.headings.length > 5) {
125
+ lines.push(` - ... and ${p.headings.length - 5} more sections`);
126
+ }
127
+ }
128
+
129
+ if (p.apis && p.apis.length > 0) {
130
+ lines.push(` - *APIs: ${p.apis.join(', ')}*`);
131
+ }
132
+ if (p.pitfallCount) {
133
+ lines.push(` - *Warnings/Pitfalls: ${p.pitfallCount} recorded*`);
134
+ }
135
+ }
136
+ }
137
+ lines.push('');
138
+ }
139
+
140
+ return {
141
+ totalSources: sources.length,
142
+ totalPages,
143
+ totalChunks,
144
+ totalEstimatedTokens,
145
+ sources: sourceSummaries,
146
+ pages: pageNodes,
147
+ markdownTree: lines.join('\n'),
148
+ untrusted: true,
149
+ };
150
+ }
@@ -0,0 +1,6 @@
1
+ export * from './agents-md.ts';
2
+ export * from './claude-md.ts';
3
+ export * from './skill-md.ts';
4
+ export * from './llms-txt.ts';
5
+ export * from './docs-map.ts';
6
+ export * from './service.ts';
@@ -0,0 +1,96 @@
1
+ import type {
2
+ NormalizedPage,
3
+ ApiEndpoint,
4
+ Pitfall,
5
+ } from '../../shared/src/index.ts';
6
+
7
+ export interface LlmsTxtExportData {
8
+ title?: string;
9
+ summary?: string;
10
+ docVersion?: string;
11
+ pages: NormalizedPage[];
12
+ endpoints: ApiEndpoint[];
13
+ pitfalls: Pitfall[];
14
+ full?: boolean;
15
+ }
16
+
17
+ /**
18
+ * Generates an llms.txt (or llms-full.txt) document according to the llms.txt specification.
19
+ * Fully deterministic output.
20
+ */
21
+ export function generateLlmsTxt(data: LlmsTxtExportData): string {
22
+ const lines: string[] = [];
23
+ const title = data.title || 'Documentation Index';
24
+ const summary = data.summary || 'Curated machine-readable documentation index generated by DocOrbit.';
25
+
26
+ lines.push(`# ${title}`);
27
+ lines.push('');
28
+ lines.push(`> ${summary}`);
29
+ lines.push('');
30
+
31
+ if (data.docVersion) {
32
+ lines.push(`Documentation Version: \`${data.docVersion}\``);
33
+ lines.push('');
34
+ }
35
+
36
+ // 1. Pages / Core Docs
37
+ lines.push('## Documentation Pages');
38
+ lines.push('');
39
+ if (data.pages.length > 0) {
40
+ const sortedPages = [...data.pages].sort((a, b) => a.url.localeCompare(b.url));
41
+ for (const page of sortedPages) {
42
+ const pageTitle = page.title || page.url;
43
+ const tokens = page.estimatedTokens ? ` (~${page.estimatedTokens} tokens)` : '';
44
+ lines.push(`- [${pageTitle}](${page.url})${tokens}`);
45
+ }
46
+ lines.push('');
47
+ } else {
48
+ lines.push('- *No documentation pages indexed.*');
49
+ lines.push('');
50
+ }
51
+
52
+ // 2. API Reference
53
+ if (data.endpoints.length > 0) {
54
+ lines.push('## API Reference');
55
+ lines.push('');
56
+ const sortedEndpoints = [...data.endpoints].sort((a, b) => {
57
+ const cmp = a.path.localeCompare(b.path);
58
+ return cmp !== 0 ? cmp : a.method.localeCompare(b.method);
59
+ });
60
+
61
+ for (const ep of sortedEndpoints) {
62
+ const desc = ep.summary ? `: ${ep.summary}` : '';
63
+ const provUrl = ep.provenance?.sourceUrl || `#`;
64
+ lines.push(`- [${ep.method.toUpperCase()} ${ep.path}](${provUrl})${desc}`);
65
+ }
66
+ lines.push('');
67
+ }
68
+
69
+ // 3. Pitfalls & Warnings
70
+ if (data.pitfalls.length > 0) {
71
+ lines.push('## Critical Caveats & Warnings');
72
+ lines.push('');
73
+ const sortedPitfalls = [...data.pitfalls].sort((a, b) => a.title.localeCompare(b.title));
74
+ for (const pf of sortedPitfalls) {
75
+ const provUrl = pf.provenance?.sourceUrl || `#`;
76
+ lines.push(`- [${pf.title}](${provUrl}): ${pf.message}`);
77
+ }
78
+ lines.push('');
79
+ }
80
+
81
+ // If full mode requested, append full page text
82
+ if (data.full) {
83
+ lines.push('---');
84
+ lines.push('## Full Documentation Content');
85
+ lines.push('');
86
+ const sortedPages = [...data.pages].sort((a, b) => a.url.localeCompare(b.url));
87
+ for (const page of sortedPages) {
88
+ lines.push(`### [${page.title || page.url}](${page.url})`);
89
+ lines.push('');
90
+ lines.push(page.content.trim());
91
+ lines.push('');
92
+ }
93
+ }
94
+
95
+ return lines.join('\n');
96
+ }