Guidelines for AI agents working on this codebase.
Issue Bridge is a backend service that collects feedback from any source (websites, apps, bots, forms, internal tools) via a single API endpoint and automatically creates well-formatted GitHub issues on a target repository. It uses AI (OpenRouter) to rewrite raw feedback into professional engineering issues, and GitHub App authentication to create them.
Each instance of Issue Bridge serves one project. Project-specific context is loaded from PROJECT_CONTEXT.md at startup, so the AI writes issues that match the project's domain, tech stack, and terminology.
Any client (website, mobile app, Slack bot, form, CLI, etc.)
│
▼
POST /api/feedback
→ validateFeedback (Zod middleware)
→ feedbackService.processFeedback()
→ load projectContext (from PROJECT_CONTEXT.md, cached at startup)
→ openrouterService.rewriteFeedback(feedback, projectContext)
// AI generates { title, body, labels } using project context
→ githubService.createIssue() // GitHub App auth via Octokit
→ 201 { issueNumber, issueUrl, title }
- Express app factory pattern (
createApp()inapp.ts) — enables testing with Supertest without starting a real server - GitHub App auth (not PAT or OAuth) — uses
@octokit/auth-appwith App ID + private key + installation ID for server-to-server authentication - Project context from file —
PROJECT_CONTEXT.mdis read once at startup and injected into every AI prompt, so issues reference real project components - Native fetch for OpenRouter — no axios or other HTTP client needed
- Zod 4 for both env validation (fail-fast at startup) and request validation (middleware)
- JSON mode in OpenRouter request — ensures AI returns parseable JSON
| File | Purpose |
|---|---|
src/config/env.ts |
Zod schema for all env vars. App exits on invalid config. |
src/config/project-context.ts |
Reads PROJECT_CONTEXT.md at startup, exports projectContext singleton |
src/types/feedback.ts |
FeedbackRequest Zod schema + FeedbackResponse interface |
src/types/issue.ts |
StructuredIssue and ProjectContext interfaces |
src/prompts/feedback-to-issue.ts |
System prompt + buildUserMessage() that prepends project context to feedback |
src/services/openrouter.service.ts |
Calls OpenRouter chat completions with project context, validates response with Zod |
src/services/github.service.ts |
Singleton Octokit client with GitHub App auth, createIssue() |
src/services/feedback.service.ts |
Orchestrator: passes project context to AI → calls GitHub |
src/middleware/validate-feedback.ts |
Zod validation middleware for POST body |
src/middleware/error-handler.ts |
Maps errors to HTTP status codes (400, 502, 500) |
src/routes/feedback.route.ts |
Route handler for POST /api/feedback |
PROJECT_CONTEXT.md |
Project-specific context (README/docs of the target project). Read at startup. |
- Package manager: pnpm (not npm or yarn)
- Module system: ESM (
"type": "module"in package.json). Use.jsextensions in imports. - No mutation: Prefer creating new objects over mutating existing ones
- Error handling: Services throw errors, middleware catches and formats them
- Testing: Vitest + Supertest. Mock external services (fetch, Octokit). Never call real APIs in tests.
No code changes needed. Any client that can send HTTP POST with a JSON body can use Issue Bridge. Just call POST /api/feedback with { "feedback": "..." }.
Replace PROJECT_CONTEXT.md with the new project's README or documentation. Update GITHUB_DEFAULT_REPO_OWNER and GITHUB_DEFAULT_REPO_NAME in .env. Restart the service.
- Create route in
src/routes/ - Add validation middleware if needed in
src/middleware/ - Register route in
src/app.ts - Add integration test in
src/__tests__/
Edit src/prompts/feedback-to-issue.ts. The prompt instructs the AI to return JSON with title, body, and labels. If you change the output schema, also update structuredIssueSchema in openrouter.service.ts and the StructuredIssue type in types/issue.ts.
Edit src/config/project-context.ts to change the name, description, or techStack fields. The readme field is auto-loaded from PROJECT_CONTEXT.md.
GitHub auth is isolated in src/services/github.service.ts. The singleton Octokit instance uses createAppAuth. To switch auth methods, modify getOctokit() and update env vars in src/config/env.ts.
pnpm test # Run all tests once
pnpm test:watch # Watch mode
pnpm typecheck # TypeScript type checkingTests mock all external dependencies:
openrouter.servicetests mockglobalThis.fetchgithub.servicetests mockoctokitmodule- Integration tests mock both services +
project-contextmodule
Required env vars are validated at startup via Zod in src/config/env.ts. If any are missing or invalid, the process exits with a clear error message. See .env.example for the full list.