@doccov/api 0.3.5 → 0.3.6

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/functions/plan.ts DELETED
@@ -1,104 +0,0 @@
1
- /**
2
- * POST /plan - Generate a build plan for a GitHub repository
3
- *
4
- * Request body:
5
- * - url: GitHub repository URL (required)
6
- * - ref: Git ref (branch/tag), defaults to default branch
7
- * - package: Target package name (for monorepos)
8
- *
9
- * Response:
10
- * - BuildPlan object with steps, environment, and reasoning
11
- */
12
-
13
- import { fetchGitHubContext, parseScanGitHubUrl } from '@doccov/sdk';
14
- import type { VercelRequest, VercelResponse } from '@vercel/node';
15
- import { generateBuildPlan } from '../lib/plan-agent';
16
-
17
- export const config = {
18
- runtime: 'nodejs',
19
- maxDuration: 30,
20
- };
21
-
22
- interface PlanRequestBody {
23
- url: string;
24
- ref?: string;
25
- package?: string;
26
- }
27
-
28
- export default async function handler(req: VercelRequest, res: VercelResponse) {
29
- // CORS
30
- res.setHeader('Access-Control-Allow-Origin', '*');
31
- res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
32
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
33
-
34
- if (req.method === 'OPTIONS') {
35
- return res.status(200).end();
36
- }
37
-
38
- if (req.method !== 'POST') {
39
- return res.status(405).json({ error: 'Method not allowed' });
40
- }
41
-
42
- const body = req.body as PlanRequestBody;
43
-
44
- if (!body.url) {
45
- return res.status(400).json({ error: 'url is required' });
46
- }
47
-
48
- // Validate URL format
49
- let repoUrl: string;
50
- try {
51
- const parsed = parseScanGitHubUrl(body.url);
52
- if (!parsed) {
53
- return res.status(400).json({ error: 'Invalid GitHub URL' });
54
- }
55
- repoUrl = `https://github.com/${parsed.owner}/${parsed.repo}`;
56
- } catch {
57
- return res.status(400).json({ error: 'Invalid GitHub URL' });
58
- }
59
-
60
- try {
61
- // Fetch project context from GitHub
62
- const context = await fetchGitHubContext(repoUrl, body.ref);
63
-
64
- // Check for private repos (we can't access them without auth)
65
- if (context.metadata.isPrivate) {
66
- return res.status(403).json({
67
- error: 'Private repositories are not supported',
68
- hint: 'Use a public repository or run doccov locally',
69
- });
70
- }
71
-
72
- // Generate build plan using AI
73
- const plan = await generateBuildPlan(context, {
74
- targetPackage: body.package,
75
- });
76
-
77
- return res.status(200).json({
78
- plan,
79
- context: {
80
- owner: context.metadata.owner,
81
- repo: context.metadata.repo,
82
- ref: context.ref,
83
- packageManager: context.packageManager,
84
- isMonorepo: context.workspace.isMonorepo,
85
- },
86
- });
87
- } catch (error) {
88
- console.error('Plan generation error:', error);
89
-
90
- if (error instanceof Error) {
91
- if (error.message.includes('404') || error.message.includes('not found')) {
92
- return res.status(404).json({ error: 'Repository not found' });
93
- }
94
- if (error.message.includes('rate limit')) {
95
- return res.status(429).json({ error: 'GitHub API rate limit exceeded' });
96
- }
97
- }
98
-
99
- return res.status(500).json({
100
- error: 'Failed to generate build plan',
101
- message: error instanceof Error ? error.message : 'Unknown error',
102
- });
103
- }
104
- }
package/lib/plan-agent.ts DELETED
@@ -1,252 +0,0 @@
1
- /**
2
- * AI-powered build plan generation agent.
3
- * Uses Claude to analyze repository context and generate execution plans.
4
- */
5
-
6
- import { createAnthropic } from '@ai-sdk/anthropic';
7
- import { generateObject } from 'ai';
8
- import { z } from 'zod';
9
- import type {
10
- BuildPlan,
11
- BuildPlanEnvironment,
12
- BuildPlanStep,
13
- BuildPlanTarget,
14
- GitHubProjectContext,
15
- } from '@doccov/sdk';
16
-
17
- /**
18
- * Zod schema for build plan validation.
19
- */
20
- const BuildPlanStepSchema = z.object({
21
- id: z.string().describe('Unique identifier (e.g., "install", "build-types")'),
22
- name: z.string().describe('Human-readable step name'),
23
- command: z.string().describe('Command to execute'),
24
- args: z.array(z.string()).describe('Command arguments'),
25
- cwd: z.string().optional().describe('Working directory relative to repo root'),
26
- timeout: z.number().optional().describe('Timeout in milliseconds'),
27
- optional: z.boolean().optional().describe('If true, failure does not stop execution'),
28
- });
29
-
30
- const BuildPlanEnvironmentSchema = z.object({
31
- runtime: z.enum(['node22', 'node24']).describe('Runtime to use (node22 or node24)'),
32
- packageManager: z.enum(['npm', 'yarn', 'pnpm', 'bun']).describe('Package manager'),
33
- requiredTools: z.array(z.string()).optional().describe('Additional required tools'),
34
- });
35
-
36
- const BuildPlanReasoningSchema = z.object({
37
- summary: z.string().describe('Brief summary of the approach (1-2 sentences)'),
38
- rationale: z.string().describe('Why this approach was chosen'),
39
- concerns: z.array(z.string()).describe('Potential issues or concerns'),
40
- });
41
-
42
- const AIBuildPlanSchema = z.object({
43
- environment: BuildPlanEnvironmentSchema,
44
- steps: z.array(BuildPlanStepSchema).describe('Steps to execute in order'),
45
- entryPoints: z.array(z.string()).describe('Entry point files to analyze'),
46
- reasoning: BuildPlanReasoningSchema,
47
- confidence: z.enum(['high', 'medium', 'low']).describe('Confidence in this plan'),
48
- });
49
-
50
- type AIBuildPlanOutput = z.infer<typeof AIBuildPlanSchema>;
51
-
52
- /**
53
- * Options for build plan generation.
54
- */
55
- export interface GenerateBuildPlanOptions {
56
- /** Target a specific package in a monorepo */
57
- targetPackage?: string;
58
- /** Override the default model */
59
- model?: string;
60
- }
61
-
62
- /**
63
- * Format project context for the AI prompt.
64
- */
65
- function formatContext(context: GitHubProjectContext): string {
66
- const sections: string[] = [];
67
-
68
- // Repository metadata
69
- sections.push(`=== Repository ===
70
- Owner: ${context.metadata.owner}
71
- Repo: ${context.metadata.repo}
72
- Language: ${context.metadata.language ?? 'unknown'}
73
- Topics: ${context.metadata.topics.join(', ') || 'none'}
74
- Description: ${context.metadata.description ?? 'none'}`);
75
-
76
- // Environment detection
77
- sections.push(`=== Environment ===
78
- Package Manager: ${context.packageManager}
79
- Is Monorepo: ${context.workspace.isMonorepo}
80
- Workspace Tool: ${context.workspace.tool ?? 'none'}
81
- Workspace Packages: ${context.workspace.packages?.join(', ') ?? 'none'}`);
82
-
83
- // Build hints
84
- sections.push(`=== Build Hints ===
85
- Has TypeScript: ${context.buildHints.hasTypeScript}
86
- Has WASM: ${context.buildHints.hasWasm}
87
- Has Native Modules: ${context.buildHints.hasNativeModules}
88
- Has Build Script: ${context.buildHints.hasBuildScript}
89
- Build Script: ${context.buildHints.buildScript ?? 'none'}
90
- Frameworks: ${context.buildHints.frameworks.join(', ') || 'none'}`);
91
-
92
- // File contents
93
- if (context.files.packageJson) {
94
- const truncated =
95
- context.files.packageJson.length > 3000
96
- ? `${context.files.packageJson.slice(0, 3000)}\n... (truncated)`
97
- : context.files.packageJson;
98
- sections.push(`=== package.json ===\n${truncated}`);
99
- }
100
-
101
- if (context.files.tsconfigJson) {
102
- sections.push(`=== tsconfig.json ===\n${context.files.tsconfigJson}`);
103
- }
104
-
105
- if (context.files.lockfile) {
106
- // Only include first 500 chars of lockfile
107
- const preview = context.files.lockfile.content.slice(0, 500);
108
- sections.push(`=== ${context.files.lockfile.name} (preview) ===\n${preview}\n...`);
109
- }
110
-
111
- return sections.join('\n\n');
112
- }
113
-
114
- /**
115
- * System prompt for build plan generation.
116
- */
117
- const SYSTEM_PROMPT = `You are a build system expert. Your task is to analyze a GitHub repository and generate a build plan to analyze its TypeScript/JavaScript API.
118
-
119
- The goal is to:
120
- 1. Install dependencies
121
- 2. Build the project (if needed) to generate TypeScript declarations
122
- 3. Identify entry points for API documentation analysis
123
-
124
- Package Manager Selection:
125
- - If a lockfile is detected (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb), use that package manager
126
- - If Package Manager is "unknown" (no lockfile), default to npm with "npm install" (NOT "npm ci")
127
- - IMPORTANT: "npm ci" and "--frozen-lockfile" flags ONLY work when a lockfile exists
128
- - When no lockfile: use "npm install", "yarn install", "pnpm install", or "bun install" (without frozen flags)
129
-
130
- Install Commands by Package Manager:
131
- - npm with lockfile: ["npm", "ci"]
132
- - npm without lockfile: ["npm", "install"]
133
- - yarn with lockfile: ["yarn", "install", "--frozen-lockfile"]
134
- - yarn without lockfile: ["yarn", "install"]
135
- - pnpm with lockfile: ["pnpm", "install", "--frozen-lockfile"]
136
- - pnpm without lockfile: ["pnpm", "install"]
137
- - bun with lockfile: ["bun", "install", "--frozen-lockfile"]
138
- - bun without lockfile: ["bun", "install"]
139
-
140
- General Guidelines:
141
- - For TypeScript projects, look for "types" or "exports" fields in package.json
142
- - For monorepos, focus on the target package if specified
143
- - Common entry points: src/index.ts, dist/index.d.ts, lib/index.ts, distribution/index.d.ts
144
- - WASM projects may need build steps before .d.ts files exist
145
- - Be conservative with timeouts (default 60000ms, increase for builds)
146
- - Installation is usually required first
147
- - Build step is needed if package.json has a "build" script and the types are in dist/distribution folder
148
-
149
- Step ID conventions:
150
- - "install" - install dependencies
151
- - "build" - main build step
152
- - "build-types" - generate type declarations
153
- - "analyze" - run doccov spec (added automatically)`;
154
-
155
- /**
156
- * Generate a build plan prompt.
157
- */
158
- function generatePrompt(
159
- context: GitHubProjectContext,
160
- options: GenerateBuildPlanOptions,
161
- ): string {
162
- let prompt = `Analyze this repository and generate a build plan:\n\n${formatContext(context)}`;
163
-
164
- if (options.targetPackage) {
165
- prompt += `\n\nTarget Package: ${options.targetPackage}
166
- Focus the plan on building and analyzing only this package within the monorepo.`;
167
- }
168
-
169
- prompt += `\n\nGenerate a build plan with:
170
- - environment: Runtime and package manager configuration
171
- - steps: Ordered build steps (install, build, etc.)
172
- - entryPoints: TypeScript entry files to analyze (relative paths)
173
- - reasoning: Explain your approach
174
- - confidence: How confident you are in this plan`;
175
-
176
- return prompt;
177
- }
178
-
179
- /**
180
- * Get the Anthropic model for plan generation.
181
- */
182
- function getModel() {
183
- const anthropic = createAnthropic();
184
- return anthropic('claude-sonnet-4-20250514');
185
- }
186
-
187
- /**
188
- * Generate a build plan for a GitHub repository.
189
- */
190
- export async function generateBuildPlan(
191
- context: GitHubProjectContext,
192
- options: GenerateBuildPlanOptions = {},
193
- ): Promise<BuildPlan> {
194
- const model = getModel();
195
- const prompt = generatePrompt(context, options);
196
-
197
- const { object } = await generateObject({
198
- model,
199
- schema: AIBuildPlanSchema,
200
- system: SYSTEM_PROMPT,
201
- prompt,
202
- });
203
-
204
- return transformToBuildPlan(object, context, options);
205
- }
206
-
207
- /**
208
- * Transform AI output to full BuildPlan.
209
- */
210
- function transformToBuildPlan(
211
- output: AIBuildPlanOutput,
212
- context: GitHubProjectContext,
213
- options: GenerateBuildPlanOptions,
214
- ): BuildPlan {
215
- const target: BuildPlanTarget = {
216
- type: 'github',
217
- repoUrl: `https://github.com/${context.metadata.owner}/${context.metadata.repo}`,
218
- ref: context.ref,
219
- rootPath: options.targetPackage,
220
- entryPoints: output.entryPoints,
221
- };
222
-
223
- const environment: BuildPlanEnvironment = {
224
- runtime: output.environment.runtime,
225
- packageManager: output.environment.packageManager,
226
- requiredTools: output.environment.requiredTools,
227
- };
228
-
229
- const steps: BuildPlanStep[] = output.steps.map((step) => ({
230
- id: step.id,
231
- name: step.name,
232
- command: step.command,
233
- args: step.args,
234
- cwd: step.cwd,
235
- timeout: step.timeout,
236
- optional: step.optional,
237
- }));
238
-
239
- return {
240
- version: '1.0.0',
241
- generatedAt: new Date().toISOString(),
242
- target,
243
- environment,
244
- steps,
245
- reasoning: {
246
- summary: output.reasoning.summary,
247
- rationale: output.reasoning.rationale,
248
- concerns: output.reasoning.concerns,
249
- },
250
- confidence: output.confidence,
251
- };
252
- }