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,250 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import type {
4
+ DocOrbitRepository,
5
+ } from '../../storage/src/index.ts';
6
+ import { WorkspaceResolver } from '../../workspace/src/index.ts';
7
+ import { RecipeEngine } from '../../retrieval/src/index.ts';
8
+ import type {
9
+ ExportFormat,
10
+ ExportOptions,
11
+ ExportResult,
12
+ DocumentationMapResult,
13
+ ProjectDependency,
14
+ VersionResolutionResult,
15
+ ApiEndpoint,
16
+ Pitfall,
17
+ Recipe,
18
+ NormalizedPage,
19
+ DiscoveredSource,
20
+ } from '../../shared/src/index.ts';
21
+ import { generateAgentsMd } from './agents-md.ts';
22
+ import { generateClaudeMd } from './claude-md.ts';
23
+ import { generateSkillMd } from './skill-md.ts';
24
+ import { generateLlmsTxt } from './llms-txt.ts';
25
+ import { buildDocumentationMap } from './docs-map.ts';
26
+
27
+ export class ExportService {
28
+ private repo: DocOrbitRepository;
29
+ private workspaceResolver: WorkspaceResolver;
30
+ private recipeEngine: RecipeEngine;
31
+
32
+ constructor(repo: DocOrbitRepository) {
33
+ this.repo = repo;
34
+ this.workspaceResolver = new WorkspaceResolver(repo);
35
+ this.recipeEngine = new RecipeEngine(repo);
36
+ }
37
+
38
+ /**
39
+ * Generates agent documentation export deterministically based on indexed evidence.
40
+ */
41
+ async generateExport(options: ExportOptions): Promise<ExportResult> {
42
+ const projectDir = options.projectDir ? path.resolve(options.projectDir) : undefined;
43
+ const docVersion = options.docVersion;
44
+
45
+ // 1. Resolve workspace dependencies if project directory exists
46
+ const dependencies: Array<{
47
+ dep: ProjectDependency;
48
+ resolution?: VersionResolutionResult;
49
+ }> = [];
50
+
51
+ if (projectDir && fs.existsSync(projectDir)) {
52
+ try {
53
+ const resolvedMap = await this.workspaceResolver.resolveAllDependencies(projectDir);
54
+ for (const [, item] of resolvedMap) {
55
+ dependencies.push({
56
+ dep: item.dependency,
57
+ resolution: item.resolution,
58
+ });
59
+ }
60
+ } catch {
61
+ // Workspace detection is best-effort
62
+ }
63
+ }
64
+
65
+ // 2. Fetch indexed sources & pages
66
+ const sources = this.repo.sources.listSources();
67
+ const sourceUrls = sources.map(s => s.url);
68
+ const pages = this.repo.pages.listPages(1000);
69
+
70
+ // 3. Fetch endpoints (filtered by docVersion if provided)
71
+ const endpoints = this.repo.apis.searchApiEndpoints('', {
72
+ docVersion,
73
+ limit: 200,
74
+ });
75
+
76
+ // 4. Fetch pitfalls (filtered by docVersion if provided)
77
+ const pitfalls = this.repo.pitfalls.searchPitfalls('', {
78
+ docVersion,
79
+ limit: 100,
80
+ });
81
+
82
+ // 5. Fetch code examples
83
+ const examples = this.repo.examples.searchIndexedExamples('', {
84
+ docVersion,
85
+ limit: 100,
86
+ });
87
+
88
+ // 6. Build recipes for common goals
89
+ const recipes: Recipe[] = [];
90
+ const candidateGoals = [
91
+ 'Stripe webhook signature verification',
92
+ 'Handle webhook events',
93
+ 'Create customer subscription',
94
+ 'Dynamic route parameter migration',
95
+ 'Authenticate API request',
96
+ ];
97
+
98
+ for (const goal of candidateGoals) {
99
+ try {
100
+ const recipe = this.recipeEngine.assembleRecipe(goal, { docVersion });
101
+ if (recipe.prerequisites.length > 0 || recipe.steps.length > 0) {
102
+ recipes.push(recipe);
103
+ }
104
+ } catch {
105
+ // Goal not covered by current index
106
+ }
107
+ }
108
+
109
+ // 7. Format generation
110
+ let content = '';
111
+ const format = options.format;
112
+
113
+ switch (format) {
114
+ case 'agents.md': {
115
+ content = generateAgentsMd({
116
+ projectDir,
117
+ docVersion,
118
+ dependencies,
119
+ endpoints,
120
+ pitfalls,
121
+ recipes,
122
+ sources: sourceUrls,
123
+ });
124
+ break;
125
+ }
126
+ case 'claude.md': {
127
+ content = generateClaudeMd({
128
+ projectDir,
129
+ docVersion,
130
+ dependencies,
131
+ endpoints,
132
+ pitfalls,
133
+ sources: sourceUrls,
134
+ });
135
+ break;
136
+ }
137
+ case 'skill.md': {
138
+ content = generateSkillMd({
139
+ skillName: options.targetSource || 'docorbit-agent-skill',
140
+ docVersion,
141
+ endpoints,
142
+ pitfalls,
143
+ recipes,
144
+ sources: sourceUrls,
145
+ });
146
+ break;
147
+ }
148
+ case 'llms.txt': {
149
+ content = generateLlmsTxt({
150
+ title: options.targetSource || 'Indexed Documentation',
151
+ docVersion,
152
+ pages,
153
+ endpoints,
154
+ pitfalls,
155
+ });
156
+ break;
157
+ }
158
+ case 'docs-map.md': {
159
+ const map = buildDocumentationMap({
160
+ sources,
161
+ pages,
162
+ endpoints,
163
+ pitfalls,
164
+ docVersion,
165
+ });
166
+ content = map.markdownTree;
167
+ break;
168
+ }
169
+ default:
170
+ throw new Error(`Unsupported export format: ${format}`);
171
+ }
172
+
173
+ const tokenEstimate = Math.ceil(content.length / 4);
174
+
175
+ return {
176
+ format,
177
+ content,
178
+ outputPath: options.outputPath,
179
+ metadata: {
180
+ generatedAt: '2026-09-06T00:00:00.000Z', // Deterministic baseline timestamp
181
+ sources: sourceUrls,
182
+ docVersion,
183
+ projectDependencies: dependencies.map(d => ({
184
+ name: d.dep.name,
185
+ version: d.resolution?.targetVersion || d.dep.requestedVersion,
186
+ matchType: d.resolution?.matchedBy || 'unresolved',
187
+ })),
188
+ totalApis: endpoints.length,
189
+ totalPitfalls: pitfalls.length,
190
+ totalExamples: examples.length,
191
+ tokenEstimate,
192
+ untrusted: true,
193
+ },
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Retrieves the structured documentation map.
199
+ */
200
+ getDocumentationMap(options: { sourceId?: string; docVersion?: string } = {}): DocumentationMapResult {
201
+ let sources = this.repo.sources.listSources();
202
+ if (options.sourceId) {
203
+ sources = sources.filter(s => s.id === options.sourceId || s.url.includes(options.sourceId!));
204
+ }
205
+
206
+ const pages = this.repo.pages.listPages(1000);
207
+ const endpoints = this.repo.apis.searchApiEndpoints('', { docVersion: options.docVersion, limit: 500 });
208
+ const pitfalls = this.repo.pitfalls.searchPitfalls('', { docVersion: options.docVersion, limit: 200 });
209
+
210
+ return buildDocumentationMap({
211
+ sources,
212
+ pages,
213
+ endpoints,
214
+ pitfalls,
215
+ docVersion: options.docVersion,
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Generates export and writes directly to workspace file.
221
+ */
222
+ async writeExport(options: ExportOptions): Promise<ExportResult> {
223
+ const result = await this.generateExport(options);
224
+
225
+ if (!options.stdout) {
226
+ let targetFile = options.outputPath;
227
+ if (!targetFile) {
228
+ const baseDir = options.projectDir || process.cwd();
229
+ const defaultNames: Record<ExportFormat, string> = {
230
+ 'agents.md': 'AGENTS.md',
231
+ 'claude.md': 'CLAUDE.md',
232
+ 'skill.md': 'skill.md',
233
+ 'llms.txt': 'llms.txt',
234
+ 'docs-map.md': 'docs-map.md',
235
+ };
236
+ targetFile = path.join(baseDir, defaultNames[options.format]);
237
+ }
238
+
239
+ const dir = path.dirname(targetFile);
240
+ if (!fs.existsSync(dir)) {
241
+ fs.mkdirSync(dir, { recursive: true });
242
+ }
243
+
244
+ fs.writeFileSync(targetFile, result.content, 'utf-8');
245
+ result.outputPath = targetFile;
246
+ }
247
+
248
+ return result;
249
+ }
250
+ }
@@ -0,0 +1,128 @@
1
+ import type {
2
+ ApiEndpoint,
3
+ Pitfall,
4
+ Recipe,
5
+ } from '../../shared/src/index.ts';
6
+
7
+ export interface SkillMdExportData {
8
+ skillName?: string;
9
+ docVersion?: string;
10
+ endpoints: ApiEndpoint[];
11
+ pitfalls: Pitfall[];
12
+ recipes: Recipe[];
13
+ sources: string[];
14
+ }
15
+
16
+ /**
17
+ * Generates a skill.md file compatible with AI agent skill systems.
18
+ * Embeds YAML frontmatter, deterministic recipes, and verified schemas.
19
+ */
20
+ export function generateSkillMd(data: SkillMdExportData): string {
21
+ const lines: string[] = [];
22
+ const skillName = data.skillName || 'docorbit-implementation-skill';
23
+ const version = data.docVersion || '0.1.0';
24
+
25
+ // Frontmatter
26
+ lines.push('---');
27
+ lines.push(`name: ${skillName}`);
28
+ lines.push('description: Evidence-grounded documentation intelligence and API implementation recipes.');
29
+ lines.push(`version: ${version}`);
30
+ lines.push('untrusted_documentation: true');
31
+ lines.push('grounding: strict_evidence');
32
+ lines.push('---');
33
+ lines.push('');
34
+
35
+ lines.push(`# Skill: ${skillName} (${version})`);
36
+ lines.push('');
37
+ lines.push('This agent skill contains deterministic recipes and verified API signatures generated from authoritative');
38
+ lines.push('documentation. Never fabricate undocumented steps or validation rules.');
39
+ lines.push('');
40
+
41
+ // 1. Recipes
42
+ lines.push('## 1. Actionable Implementation Recipes');
43
+ lines.push('');
44
+ if (data.recipes.length > 0) {
45
+ for (const r of data.recipes) {
46
+ lines.push(`### Recipe: ${r.title}`);
47
+ lines.push(`- **Goal**: ${r.goal}`);
48
+ lines.push(`- **Evidence Status**: ${r.evidenceLevels.strictGrounded ? 'Grounded' : 'Partial'}`);
49
+ lines.push('');
50
+
51
+ if (r.prerequisites.length > 0) {
52
+ lines.push('**Prerequisites:**');
53
+ for (const pre of r.prerequisites) {
54
+ lines.push(`- [${pre.evidenceLevel}] ${pre.description}`);
55
+ }
56
+ lines.push('');
57
+ }
58
+
59
+ if (r.steps.length > 0) {
60
+ lines.push('**Steps:**');
61
+ for (const step of r.steps) {
62
+ lines.push(`${step.stepNumber}. [${step.evidenceLevel}] **${step.title}**: ${step.action}`);
63
+ if (step.codeSnippet) {
64
+ lines.push(' ```' + (step.codeSnippet.language || 'typescript'));
65
+ lines.push(step.codeSnippet.code.trim().split('\n').map(l => ' ' + l).join('\n'));
66
+ lines.push(' ```');
67
+ }
68
+ }
69
+ lines.push('');
70
+ }
71
+
72
+ if (r.validationSteps.length > 0) {
73
+ lines.push('**Evidence-Based Validation:**');
74
+ for (const v of r.validationSteps) {
75
+ lines.push(`- Verify: ${v.assertion} (Expected: \`${v.expectedOutcome}\`)`);
76
+ }
77
+ lines.push('');
78
+ }
79
+ }
80
+ } else {
81
+ lines.push('- *No compiled recipes available for this scope.*');
82
+ lines.push('');
83
+ }
84
+
85
+ // 2. API Reference
86
+ lines.push('## 2. API Reference & Schemas');
87
+ lines.push('');
88
+ if (data.endpoints.length > 0) {
89
+ const sortedEndpoints = [...data.endpoints].sort((a, b) => {
90
+ const cmp = a.path.localeCompare(b.path);
91
+ return cmp !== 0 ? cmp : a.method.localeCompare(b.method);
92
+ });
93
+
94
+ for (const ep of sortedEndpoints) {
95
+ lines.push(`### \`${ep.method.toUpperCase()} ${ep.path}\``);
96
+ if (ep.summary) lines.push(ep.summary);
97
+ if (ep.parameters && ep.parameters.length > 0) {
98
+ lines.push('Parameters:');
99
+ for (const p of ep.parameters) {
100
+ lines.push(`- \`${p.name}\` (${p.in}, ${p.required ? 'required' : 'optional'})`);
101
+ }
102
+ }
103
+ lines.push('');
104
+ }
105
+ } else {
106
+ lines.push('- *No endpoints indexed.*');
107
+ lines.push('');
108
+ }
109
+
110
+ // 3. Known Pitfalls
111
+ lines.push('## 3. Known Pitfalls & Defenses');
112
+ lines.push('');
113
+ if (data.pitfalls.length > 0) {
114
+ const sortedPitfalls = [...data.pitfalls].sort((a, b) => a.title.localeCompare(b.title));
115
+ for (const pf of sortedPitfalls) {
116
+ lines.push(`- **[${pf.kind}] ${pf.title}**: ${pf.message}`);
117
+ if (pf.mitigation) {
118
+ lines.push(` - Defense: ${pf.mitigation}`);
119
+ }
120
+ }
121
+ lines.push('');
122
+ } else {
123
+ lines.push('- *No pitfalls recorded.*');
124
+ lines.push('');
125
+ }
126
+
127
+ return lines.join('\n');
128
+ }
@@ -0,0 +1,46 @@
1
+ export type {
2
+ JsonRpcRequest,
3
+ JsonRpcResponse,
4
+ JsonRpcNotification,
5
+ JsonRpcErrorObject,
6
+ ToolInputSchema,
7
+ McpTool,
8
+ ToolContentItem,
9
+ CallToolResult,
10
+ McpResource,
11
+ ResourceContent,
12
+ ReadResourceResult,
13
+ ServerCapabilities,
14
+ InitializeResult,
15
+ } from './types.ts';
16
+
17
+ export { JSONRPC_ERRORS } from './types.ts';
18
+
19
+ export type { McpContext, McpToolHandler } from './tools/types.ts';
20
+ export {
21
+ createDefaultTools,
22
+ SearchDocsTool,
23
+ GetDocTool,
24
+ FindApiTool,
25
+ FindExampleTool,
26
+ FindPitfallTool,
27
+ FindRecipeTool,
28
+ GetVersionTool,
29
+ ListSourcesTool,
30
+ GetImplementationContextTool,
31
+ CheckApiTool,
32
+ DiffDocsTool,
33
+ AnalyzeImpactTool,
34
+ } from './tools/index.ts';
35
+
36
+ export type { ResourceManagerOptions } from './resources/index.ts';
37
+ export { McpResourceManager } from './resources/index.ts';
38
+
39
+ export type { McpServerOptions } from './server.ts';
40
+ export { McpServer } from './server.ts';
41
+
42
+ export type { McpTransport } from './transports/types.ts';
43
+ export type { StdioTransportOptions } from './transports/stdio.ts';
44
+ export { StdioServerTransport } from './transports/stdio.ts';
45
+ export type { HttpTransportOptions } from './transports/http.ts';
46
+ export { StreamableHttpTransport } from './transports/http.ts';
@@ -0,0 +1,189 @@
1
+ import type { DocOrbitRepository } from '../../../storage/src/index.ts';
2
+ import type { McpResource, ReadResourceResult, ResourceContent } from '../types.ts';
3
+
4
+ export interface ResourceManagerOptions {
5
+ maxResourceBytes?: number;
6
+ }
7
+
8
+ export class McpResourceManager {
9
+ private repo: DocOrbitRepository;
10
+ private maxChars: number;
11
+
12
+ constructor(repo: DocOrbitRepository, options: ResourceManagerOptions = {}) {
13
+ this.repo = repo;
14
+ // Default 64KB bounded limit
15
+ this.maxChars = options.maxResourceBytes || 64 * 1024;
16
+ }
17
+
18
+ listResources(): McpResource[] {
19
+ const resources: McpResource[] = [
20
+ {
21
+ uri: 'docorbit://sources',
22
+ name: 'Indexed Documentation Sources',
23
+ description: 'Collection of all indexed documentation sources, version status, and snapshots.',
24
+ mimeType: 'application/json',
25
+ },
26
+ ];
27
+
28
+ try {
29
+ const sources = this.repo.listSources();
30
+ for (const source of sources) {
31
+ resources.push({
32
+ uri: `docorbit://sources/${source.id}`,
33
+ name: `Source: ${source.url}`,
34
+ description: `Indexed documentation source (${source.type}, authority: ${source.authority})`,
35
+ mimeType: 'application/json',
36
+ });
37
+ }
38
+ } catch {
39
+ // Continue if database is empty or error occurs
40
+ }
41
+
42
+ try {
43
+ const pages = this.repo.listPages(100);
44
+ for (const page of pages) {
45
+ resources.push({
46
+ uri: `docorbit://pages/${page.id}`,
47
+ name: `Page: ${page.title || page.url}`,
48
+ description: `Documentation page at ${page.url} (~${page.estimatedTokens} tokens)`,
49
+ mimeType: 'text/markdown',
50
+ });
51
+ }
52
+ } catch {
53
+ // Continue
54
+ }
55
+
56
+ return resources;
57
+ }
58
+
59
+ readResource(uri: string): ReadResourceResult {
60
+ if (uri === 'docorbit://sources') {
61
+ const sources = this.repo.listSources();
62
+ const snapshots = this.repo.listSnapshots();
63
+ const payload = sources.map(s => ({
64
+ id: s.id,
65
+ url: s.url,
66
+ type: s.type,
67
+ authority: s.authority,
68
+ confidence: s.confidence,
69
+ status: s.status,
70
+ machineReadable: s.machineReadable,
71
+ snapshots: snapshots.filter(sn => sn.sourceId === s.id).map(sn => ({
72
+ id: sn.id,
73
+ pageCount: sn.pageCount,
74
+ docVersion: sn.docVersion,
75
+ capturedAt: sn.capturedAt,
76
+ })),
77
+ }));
78
+
79
+ return {
80
+ contents: [
81
+ {
82
+ uri,
83
+ mimeType: 'application/json',
84
+ text: JSON.stringify(payload, null, 2),
85
+ },
86
+ ],
87
+ };
88
+ }
89
+
90
+ if (uri.startsWith('docorbit://sources/')) {
91
+ const sourceId = uri.slice('docorbit://sources/'.length);
92
+ const source = this.repo.getSource(sourceId);
93
+ if (!source) {
94
+ throw new Error(`Resource not found: ${uri}`);
95
+ }
96
+
97
+ const snapshots = this.repo.listSnapshots().filter(sn => sn.sourceId === source.id);
98
+ const pageCount = this.repo.countPages(source.id);
99
+
100
+ return {
101
+ contents: [
102
+ {
103
+ uri,
104
+ mimeType: 'application/json',
105
+ text: JSON.stringify({
106
+ source,
107
+ pageCount,
108
+ snapshots,
109
+ untrusted: true,
110
+ }, null, 2),
111
+ },
112
+ ],
113
+ };
114
+ }
115
+
116
+ if (uri.startsWith('docorbit://pages/')) {
117
+ const pageId = uri.slice('docorbit://pages/'.length);
118
+ const page = this.repo.getPage(pageId);
119
+ if (!page) {
120
+ throw new Error(`Resource not found: ${uri}`);
121
+ }
122
+
123
+ let content = page.content;
124
+ let truncated = false;
125
+ if (content.length > this.maxChars) {
126
+ content = content.slice(0, this.maxChars) + '\n\n[Content truncated at 64KB bounded resource limit...]';
127
+ truncated = true;
128
+ }
129
+
130
+ const header = [
131
+ `# ${page.title}`,
132
+ `**URL**: ${page.url}`,
133
+ `**Tokens**: ~${page.estimatedTokens} | **Content Hash**: \`${page.contentHash.slice(0, 8)}\``,
134
+ page.securityAnnotations.length > 0
135
+ ? `> [!WARNING] Security alerts: ${page.securityAnnotations.map(a => a.patternName).join(', ')}`
136
+ : `> [!NOTE] External content is untrusted.`,
137
+ truncated ? `> [!IMPORTANT] Output was truncated to meet resource limits.` : '',
138
+ '---\n',
139
+ ].filter(Boolean).join('\n');
140
+
141
+ return {
142
+ contents: [
143
+ {
144
+ uri,
145
+ mimeType: 'text/markdown',
146
+ text: `${header}\n${content}`,
147
+ },
148
+ ],
149
+ };
150
+ }
151
+
152
+ if (uri.startsWith('docorbit://chunks/')) {
153
+ const chunkId = uri.slice('docorbit://chunks/'.length);
154
+ const chunk = this.repo.getChunk(chunkId);
155
+ if (!chunk) {
156
+ throw new Error(`Resource not found: ${uri}`);
157
+ }
158
+
159
+ let content = chunk.content;
160
+ let truncated = false;
161
+ if (content.length > this.maxChars) {
162
+ content = content.slice(0, this.maxChars) + '\n\n[Content truncated at bounded limit...]';
163
+ truncated = true;
164
+ }
165
+
166
+ const breadcrumb = chunk.sectionPath.length > 0 ? chunk.sectionPath.join(' > ') : (chunk.title || 'General');
167
+ const header = [
168
+ `### Chunk: ${breadcrumb}`,
169
+ `**ID**: \`${chunk.id}\` | **Type**: \`${chunk.chunkType}\` | **Est. Tokens**: ~${chunk.tokenEstimate}`,
170
+ chunk.docVersion ? `**Doc Version**: \`${chunk.docVersion}\`` : '',
171
+ `> [!NOTE] External content is untrusted.\n`,
172
+ truncated ? `> [!IMPORTANT] Output was truncated to meet resource limits.\n` : '',
173
+ '---\n',
174
+ ].filter(Boolean).join('\n');
175
+
176
+ return {
177
+ contents: [
178
+ {
179
+ uri,
180
+ mimeType: 'text/markdown',
181
+ text: `${header}\n${content}`,
182
+ },
183
+ ],
184
+ };
185
+ }
186
+
187
+ throw new Error(`Unsupported resource URI: ${uri}`);
188
+ }
189
+ }