@doccov/api 0.3.5 → 0.3.7
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/.vercelignore +0 -3
- package/CHANGELOG.md +27 -0
- package/api/index.ts +894 -83
- package/package.json +5 -8
- package/src/routes/plan.ts +14 -8
- package/vercel.json +2 -6
- package/api/[...path].ts +0 -35
- package/bunup.config.ts +0 -16
- package/functions/execute-stream.ts +0 -273
- package/functions/execute.ts +0 -204
- package/functions/plan.ts +0 -104
- package/lib/plan-agent.ts +0 -252
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
|
-
}
|