-
Notifications
You must be signed in to change notification settings - Fork 18
chore: add agent rules file #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| # AGENTS.md - DKG Node Codebase Guide | ||
|
|
||
| > Quick reference for coding agents working on this Turborepo monorepo. | ||
|
|
||
| ## Architecture | ||
|
|
||
| - **Turborepo monorepo** | Node.js >= 22 | npm workspaces | ||
| - **Main app**: `apps/agent` (Expo frontend + Express backend with MCP server) | ||
| - **Plugins**: `packages/plugin-*` (modular DKG/MCP/API functionality) | ||
| - **Tech stack**: SQLite, Drizzle ORM, Expo Router, Model Context Protocol, DKG.js | ||
|
|
||
| ## Plugin System (Core Pattern) | ||
|
|
||
| ```typescript | ||
| // packages/plugin-{name}/src/index.ts | ||
| import { defineDkgPlugin } from "@dkg/plugins"; | ||
| import { openAPIRoute, z } from "@dkg/plugin-swagger"; | ||
|
|
||
| export default defineDkgPlugin((ctx, mcp, api) => { | ||
| // ctx: { dkg: DKG, blob: BlobStorage } | ||
| // mcp: McpServer for AI tools | ||
| // api: Express Router for HTTP endpoints | ||
|
|
||
| // Register MCP tool | ||
| mcp.registerTool("tool-name", { | ||
| title: "Tool Title", | ||
| description: "Description for LLM", | ||
| inputSchema: { param: z.string() }, | ||
| }, async ({ param }) => ({ | ||
| content: [{ type: "text", text: "result" }], | ||
| })); | ||
|
|
||
| // Register API endpoint | ||
| api.get("/endpoint", openAPIRoute({ | ||
| tag: "Category", | ||
| summary: "Description", | ||
| query: z.object({ param: z.string() }), | ||
| response: { schema: z.object({ result: z.any() }) }, | ||
| }, (req, res) => { | ||
| res.json({ result: req.query.param }); | ||
| })); | ||
| }); | ||
|
|
||
| // Namespaced plugin with auth | ||
| export const protectedPlugin = plugin.withNamespace("protected", { | ||
| middlewares: [authorized(["scope-name"])], | ||
| }); | ||
| ``` | ||
|
|
||
| ## Critical Conventions | ||
|
|
||
| ### API Routes | ||
| - **ALWAYS** use `openAPIRoute()` wrapper (auto-validates + generates Swagger docs) | ||
| - **ALWAYS** validate with Zod schemas | ||
| - Returns 400 auto for invalid inputs | ||
|
|
||
| ### Testing | ||
| Always write tests along with features | ||
|
|
||
| ```typescript | ||
| // tests/{name}.spec.ts - MUST import from dist/ | ||
| import plugin from "../dist/index.js"; | ||
| import { createMcpServerClientPair, createExpressApp, | ||
| createInMemoryBlobStorage, createMockDkgClient } from "@dkg/plugins/testing"; | ||
|
|
||
| const mockDkgContext = { | ||
| dkg: createMockDkgClient(), | ||
| blob: createInMemoryBlobStorage(), | ||
| }; | ||
|
|
||
| // Required test categories: "Core Functionality" and "Error Handling" | ||
| ``` | ||
|
|
||
| ### Database | ||
| - Drizzle ORM with SQLite | ||
| - Schemas in `src/database/schema.ts` or `src/server/database/sqlite/` | ||
| - Migrations in `drizzle/` folder | ||
| - Generate: `npm run build:migrations` | ||
|
|
||
| ### File Structure | ||
| ``` | ||
| packages/plugin-{name}/ | ||
| ├── src/index.ts # Export defineDkgPlugin | ||
| ├── tests/{name}.spec.ts # Import from ../dist/ | ||
| ├── package.json # Dependencies: @dkg/plugins, @dkg/plugin-swagger | ||
| ├── tsconfig.json | ||
| └── eslint.config.mjs | ||
| ``` | ||
|
|
||
| ## Essential Commands | ||
|
|
||
| ```bash | ||
| # Development | ||
| npm run dev # Start all services | ||
| turbo gen plugin # Generate new plugin | ||
| npm run build # Build all packages | ||
|
|
||
| # Before commits | ||
| turbo format check-types lint build | ||
|
|
||
| # Testing | ||
| npm test # All tests | ||
| npm run test:api # Plugin tests only | ||
| npm run test:integration # Integration tests | ||
|
|
||
| # Database | ||
| npm run build:migrations # Generate migrations | ||
| npm run drizzle:studio # Visual DB browser | ||
| ``` | ||
|
|
||
| ## Auth & Middleware | ||
|
|
||
| ```typescript | ||
| import { authorized } from "@dkg/plugin-oauth"; | ||
|
|
||
| // Apply scope-based auth | ||
| api.use("/protected", authorized(["scope-name"])); | ||
|
|
||
| // Access auth in handler | ||
| const userId = res.locals.auth?.extra?.userId; | ||
| ``` | ||
|
|
||
| ## Common Pitfalls | ||
|
|
||
| 1. **Tests import from dist**, NOT src: `import from "../dist/index.js"` | ||
| 2. **Always use openAPIRoute** for API endpoints (breaks Swagger otherwise) | ||
| 3. **Run `npm install` at root** after adding plugin dependencies | ||
| 4. **TypeScript config**: Extends `@dkg/typescript-config/base.json` | ||
| 5. **Plugin namespaces**: Prefix MCP tools automatically (`namespace__toolName`) | ||
|
|
||
| ## Key Imports | ||
|
|
||
| ```typescript | ||
| import { defineDkgPlugin, DkgContext, DkgPlugin } from "@dkg/plugins"; | ||
| import { openAPIRoute, z } from "@dkg/plugin-swagger"; | ||
| import { authorized } from "@dkg/plugin-oauth"; | ||
| import type { express } from "@dkg/plugins/types"; | ||
| ``` | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| ```bash | ||
| # Required | ||
| DATABASE_URL=dkg.db # SQLite DB name | ||
| OPENAI_API_KEY=sk-... # LLM API key | ||
| DKG_PUBLISH_WALLET=0x... # Blockchain wallet | ||
|
|
||
| # Optional | ||
| DKG_BLOCKCHAIN=hardhat1:31337 # Network | ||
| DKG_OTNODE_URL=http://localhost:8900 # OT-node | ||
| PORT=9200 # Server port | ||
| EXPO_PUBLIC_MCP_URL=http://localhost:9200 | ||
| EXPO_PUBLIC_APP_URL=http://localhost:8081 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is for dev environment, for prod it's also 9200 |
||
| ``` | ||
|
|
||
| ## Test Template | ||
|
|
||
| ```typescript | ||
| describe("Plugin Name", () => { | ||
| let mockMcpServer, mockMcpClient, app; | ||
|
|
||
| beforeEach(async () => { | ||
| const { server, client, connect } = await createMcpServerClientPair(); | ||
| mockMcpServer = server; | ||
| mockMcpClient = client; | ||
| app = createExpressApp(); | ||
|
|
||
| plugin(mockDkgContext, mockMcpServer, express.Router()); | ||
| await connect(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't we need to mount the router for api tests below this line? |
||
| }); | ||
|
|
||
| describe("Core Functionality", () => { | ||
| it("should register tools", async () => { | ||
| const tools = await mockMcpClient.listTools().then(t => t.tools); | ||
| expect(tools.some(t => t.name === "tool-name")).to.equal(true); | ||
| }); | ||
| }); | ||
|
|
||
| describe("Error Handling", () => { | ||
| it("should return 400 for invalid input", async () => { | ||
| await request(app).get("/endpoint").expect(400); | ||
| }); | ||
| }); | ||
| }); | ||
| ``` | ||
|
|
||
| ## Frontend (React Native + Expo) | ||
|
|
||
| - **Path alias**: `@/` → `src/` | ||
| - **Routing**: File-based via Expo Router | ||
| - **Protected routes**: `app/(protected)/` | ||
| - **Layout**: `app/_layout.tsx` | ||
|
|
||
| ## Plugin Registration | ||
|
|
||
| ```typescript | ||
| // apps/agent/src/server/index.ts | ||
| import myPlugin from "@dkg/plugin-my-name"; | ||
|
|
||
| const app = createPluginServer({ | ||
| name: "DKG API", | ||
| version: "1.0.0", | ||
| context: { dkg, blob: blobStorage }, | ||
| plugins: [ | ||
| defaultPlugin, | ||
| oauthPlugin, | ||
| myPlugin, // Add here | ||
| swaggerPlugin({ version, securitySchemes }), | ||
| ], | ||
| }); | ||
| ``` | ||
|
|
||
| ## Code Quality | ||
|
|
||
| - **Formatter**: Prettier (auto-formats) | ||
| - **Linter**: ESLint with TypeScript | ||
| - **Type checking**: `npm run check-types` | ||
| - **Pre-commit**: Format → Lint → Type check → Build | ||
|
|
||
| ## Debugging Tips | ||
|
|
||
| 1. Check `apps/agent/dist/index.js` exists after build | ||
| 2. Test database: `rm *.db && npm run script:setup` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we instruct the agent that for debugging one of the first it does is delete the db? |
||
| 3. View Swagger docs: `http://localhost:9200/swagger` | ||
| 4. Integration tests in `apps/agent/tests/integration/` | ||
| 5. Use `tsx` for running scripts: `npx tsx script.ts` | ||
| 6. Use docs about DKG Node project at `docs` | ||
|
|
||
| ## Reference Examples | ||
|
|
||
| - **Simple plugin**: `packages/plugin-example/` | ||
| - **Complex plugin**: `packages/plugin-dkg-publisher/` | ||
| - **OAuth patterns**: `packages/plugin-oauth/` | ||
| - **Testing guide**: `packages/PLUGIN_TESTING_GUIDE.md` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change comment to dkg-engine