-
Notifications
You must be signed in to change notification settings - Fork 3
Open
Labels
enhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers
Description
What
Detect API specification files in the repo. When a project has OpenAPI specs, GraphQL schemas, or protobuf definitions, report them in the scan results.
Why
API specs define the contract surface of the application. Knowing "this project has an OpenAPI spec with 15 endpoints" or "GraphQL schema with 8 types" helps Claude write accurate skills about the API layer.
How
Edit src/lib/scanner.js. Add a detectAPISpecs(repoPath) function:
| Type | Detection Signal |
|---|---|
| OpenAPI/Swagger | Files named openapi.yaml, openapi.json, swagger.yaml, swagger.json at root or in api/, docs/ |
| GraphQL | Files matching *.graphql, schema.graphql, *.gql, or codegen.yml/codegen.ts in dependencies |
| Protocol Buffers | Files matching *.proto anywhere in the repo (use a shallow walk, max depth 4) |
| tRPC | trpc in package.json dependencies + files matching *router* or *trpc* |
| AsyncAPI | asyncapi.yaml or asyncapi.json |
Implementation steps
-
Add
detectAPISpecs(repoPath)tosrc/lib/scanner.js:function detectAPISpecs(repoPath) { const detected = []; // OpenAPI / Swagger const openapiNames = ['openapi.yaml', 'openapi.json', 'swagger.yaml', 'swagger.json']; const openapiDirs = ['', 'api', 'docs']; for (const dir of openapiDirs) { for (const name of openapiNames) { if (existsSync(join(repoPath, dir, name))) { if (!detected.includes('openapi')) detected.push('openapi'); } } } // GraphQL — check for .graphql files // Use a simple recursive check (depth 3) if (hasFileWithExtension(repoPath, '.graphql', 3) || hasFileWithExtension(repoPath, '.gql', 3)) { detected.push('graphql'); } // Protobuf if (hasFileWithExtension(repoPath, '.proto', 4)) { detected.push('protobuf'); } // AsyncAPI if (existsSync(join(repoPath, 'asyncapi.yaml')) || existsSync(join(repoPath, 'asyncapi.json'))) { detected.push('asyncapi'); } return detected; } // Helper: check if any file with given extension exists (shallow walk) function hasFileWithExtension(rootPath, ext, maxDepth, depth = 0) { if (depth >= maxDepth) return false; try { const entries = readdirSync(rootPath); for (const entry of entries) { if (entry.startsWith('.') || entry === 'node_modules') continue; if (entry.endsWith(ext)) return true; const full = join(rootPath, entry); if (isDir(full) && hasFileWithExtension(full, ext, maxDepth, depth + 1)) return true; } } catch {} return false; }
-
Call from
scanRepo():result.apiSpecs = detectAPISpecs(repoPath); -
Display in
src/commands/scan.js:if (result.apiSpecs && result.apiSpecs.length > 0) { console.log(pc.cyan(' API specs: ') + result.apiSpecs.join(', ')); }
-
Add tests in
tests/scanner.test.js:describe('API spec detection', () => { it('detects OpenAPI spec', () => { const dir = createFixture('api-openapi', { 'openapi.yaml': 'openapi: "3.0.0"', }); const scan = scanRepo(dir); expect(scan.apiSpecs).toContain('openapi'); }); it('detects GraphQL schema', () => { const dir = createFixture('api-graphql', { 'src/schema.graphql': 'type Query { hello: String }', }); const scan = scanRepo(dir); expect(scan.apiSpecs).toContain('graphql'); }); it('detects protobuf', () => { const dir = createFixture('api-proto', { 'proto/service.proto': 'syntax = "proto3";', }); const scan = scanRepo(dir); expect(scan.apiSpecs).toContain('protobuf'); }); });
Files to change
src/lib/scanner.js— adddetectAPISpecs()+ helper, call fromscanRepo()src/commands/scan.js— display in outputtests/scanner.test.js— add tests
Acceptance criteria
-
npm testpasses - OpenAPI, GraphQL, and protobuf detected correctly
-
aspens scandisplays detected API specs
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers