@dinakars777/create-nexus 1.0.0
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/README.md +40 -0
- package/dist/index.js +395 -0
- package/package.json +38 -0
- package/src/generator.ts +133 -0
- package/src/index.ts +96 -0
- package/src/templates/agent.ts +31 -0
- package/src/templates/mcp.ts +56 -0
- package/src/templates/stack.ts +83 -0
- package/src/templates/twin.ts +32 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 🧠create-nexus
|
|
2
|
+
|
|
3
|
+
> **The Ultimate Agent-Native Boilerplate Generator.**
|
|
4
|
+
|
|
5
|
+
Most Next.js boilerplates are optimized for human readability.
|
|
6
|
+
**Project Nexus** is optimized to be perfectly indexed, mutated, and scaled by AI Coding Agents (Cursor, Claude, Devin) with *zero hallucinations*.
|
|
7
|
+
|
|
8
|
+
## The Problem
|
|
9
|
+
When you ask an AI Agent to "build a feature" in a standard Next.js codebase, it hallucinates. It mixes Pages Router with App Router, it writes raw SQL instead of using your ORM, or it bypasses your API layer entirely to write Server Actions in the UI.
|
|
10
|
+
|
|
11
|
+
## The Solution
|
|
12
|
+
`create-nexus` generates a fortress. It scaffolds a high-density, strictly-typed environment (Next.js, Hono, Drizzle, Zod) that includes a built-in **Context Control Plane** explicitly designed to govern AI behavior.
|
|
13
|
+
|
|
14
|
+
### 📦 The Tech Stack
|
|
15
|
+
* **Frontend:** Next.js (App Router) + Tailwind CSS
|
|
16
|
+
* **API:** Hono RPC (End-to-End Type Safety)
|
|
17
|
+
* **Database:** Drizzle ORM + Postgres
|
|
18
|
+
* **Validation:** Strict Zod Boundaries
|
|
19
|
+
|
|
20
|
+
### 🤖 The Agent-Native Architecture
|
|
21
|
+
When you run the generator, you aren't just getting React components. You get:
|
|
22
|
+
|
|
23
|
+
1. **The `.agent/` Control Directory**: Contains global `rules.md`, an architectural `project-map.json`, and a `scratchpad.md` for the agent to "think" out loud.
|
|
24
|
+
2. **The Twin-File System**: Every major directory (`src/app`, `src/db`) contains a `CONCEPTS.md`. This defines the Business Logic vs. Implementation boundaries so the agent understands the "why" and doesn't guess context.
|
|
25
|
+
3. **Strict Type-Safety Walls**: Absolute zero `any` types. Every API route uses `zValidator`. If an agent hallucinates an API shape, the Typescript compiler crashes, forcing the agent to read the error and fix itself.
|
|
26
|
+
4. **Verification Hooks**: Pre-configured Husky pre-commit hooks run `tsc --noEmit`. If the agent breaks the build, the Git commit natively fails, forcing it to loop until structurally sound.
|
|
27
|
+
5. **MCP Stub**: An integrated Model Context Protocol server stub (`server/mcp`) allowing local agents to securely query database schemas without reading thousands of lines of code.
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
Instantly generate your Agent-Native workspace:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx @dinakars777/create-nexus
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Follow the interactive prompts to name your project. The CLI will handle directory scaffolding, Git initialization, and NPM dependency installations.
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { intro, outro, spinner, text, confirm, isCancel } from "@clack/prompts";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
|
|
8
|
+
// src/generator.ts
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import path from "path";
|
|
11
|
+
|
|
12
|
+
// src/templates/agent.ts
|
|
13
|
+
var agentRulesTemplate = `# Global Agent Rules
|
|
14
|
+
- **Verify Before Commit:** Always run \`pnpm check\` (or \`npm run check\`) which triggers tsc and vitest after a change. Do not commit failing code.
|
|
15
|
+
- **Strict Typing:** Never use \`any\`. Use Zod for all validations.
|
|
16
|
+
- **Twin-Files:** If you create a new directory, you MUST create a \`CONCEPTS.md\` explaining its business logic and boundaries.
|
|
17
|
+
- **Architecture Limits:** Do not import UI components into the API layer. Do not import Drizzle directly into the Next.js React UI layer.
|
|
18
|
+
- **Think First:** Before making massive destructive changes, write your plan in \`.agent/scratchpad.md\`.
|
|
19
|
+
`;
|
|
20
|
+
var agentProjectMapTemplate = `{
|
|
21
|
+
"name": "nexus-app",
|
|
22
|
+
"architecture": {
|
|
23
|
+
"frontend": "Next.js App Router (React Server Components)",
|
|
24
|
+
"api": "Hono RPC",
|
|
25
|
+
"database": "Drizzle ORM + SQLite",
|
|
26
|
+
"validation": "Zod"
|
|
27
|
+
},
|
|
28
|
+
"directories": {
|
|
29
|
+
"src/app": "Next.js Routing and React UI Pages",
|
|
30
|
+
"src/server": "Hono API backend and RCP routes",
|
|
31
|
+
"src/db": "Drizzle schema operations",
|
|
32
|
+
"server/mcp": "Model Context Protocol stubs"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
var agentScratchpadTemplate = `# Agent Scratchpad
|
|
37
|
+
*Use this file to think out loud, draft complex refactors, or save temporary snippets before applying them to the main codebase.*
|
|
38
|
+
|
|
39
|
+
## Current Objective
|
|
40
|
+
...
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
// src/templates/twin.ts
|
|
44
|
+
var twinAppConceptsTemplate = `# CONCEPTS: Frontend (src/app)
|
|
45
|
+
|
|
46
|
+
## Business Logic
|
|
47
|
+
This directory contains the user-facing React application. It is responsible purely for data presentation and optimistic UI updates.
|
|
48
|
+
|
|
49
|
+
## Boundaries
|
|
50
|
+
- **DO NOT** perform direct database queries (Drizzle) from here.
|
|
51
|
+
- **DO NOT** define raw Zod schemas for the database here.
|
|
52
|
+
- **DO** use the generated Hono RPC client (e.g., \`hc\`) to communicate with the \`src/server\` API.
|
|
53
|
+
- All components should default to React Server Components unless interactive hooks (useState, useEffect) are strictly required (use \`"use client"\`).
|
|
54
|
+
`;
|
|
55
|
+
var twinServerConceptsTemplate = `# CONCEPTS: API Backend (src/server)
|
|
56
|
+
|
|
57
|
+
## Business Logic
|
|
58
|
+
This directory houses the Hono API routes. This is the exclusive gateway for all data mutations and retrievals.
|
|
59
|
+
|
|
60
|
+
## Boundaries
|
|
61
|
+
- **MUST** validate all incoming payloads using \`zValidator\` with Zod.
|
|
62
|
+
- **MUST** export the \`AppType\` router type so the Next.js frontend can consume the RPC client with end-to-end type safety.
|
|
63
|
+
- **DO NOT** import React or UI components into this layer.
|
|
64
|
+
`;
|
|
65
|
+
var twinDbConceptsTemplate = `# CONCEPTS: Database Layer (src/db)
|
|
66
|
+
|
|
67
|
+
## Business Logic
|
|
68
|
+
This directory contains the single source of truth for the database schema using Drizzle ORM.
|
|
69
|
+
|
|
70
|
+
## Boundaries
|
|
71
|
+
- **MUST** export Drizzle Select/Insert schemas (e.g., \`createInsertSchema\`) using \`drizzle-zod\`.
|
|
72
|
+
- This layer does not execute business logic; it strictly defines the schema shapes and relationships.
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
// src/templates/stack.ts
|
|
76
|
+
var honoServerTemplate = `import { Hono } from 'hono';
|
|
77
|
+
import { zValidator } from '@hono/zod-validator';
|
|
78
|
+
import { z } from 'zod';
|
|
79
|
+
import { db } from '../db';
|
|
80
|
+
import { users } from '../db/schema';
|
|
81
|
+
|
|
82
|
+
const app = new Hono().basePath('/api');
|
|
83
|
+
|
|
84
|
+
// @ai-intent: STRICT ZOD VALIDATION REQUIRED FOR ALL ROUTES
|
|
85
|
+
const createUserSchema = z.object({
|
|
86
|
+
name: z.string().min(1),
|
|
87
|
+
email: z.string().email(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const routes = app
|
|
91
|
+
.get('/users', async (c) => {
|
|
92
|
+
const allUsers = await db.select().from(users);
|
|
93
|
+
return c.json(allUsers);
|
|
94
|
+
})
|
|
95
|
+
.post('/users', zValidator('json', createUserSchema), async (c) => {
|
|
96
|
+
const { name, email } = c.req.valid('json');
|
|
97
|
+
const result = await db.insert(users).values({ name, email }).returning();
|
|
98
|
+
return c.json(result[0], 201);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export type AppType = typeof routes;
|
|
102
|
+
export default app;
|
|
103
|
+
`;
|
|
104
|
+
var drizzleSchemaTemplate = `import { pgTable, text, serial, timestamp } from 'drizzle-orm/pg-core';
|
|
105
|
+
|
|
106
|
+
// @ai-intent: SINGLE SOURCE OF TRUTH. Do not write raw SQL.
|
|
107
|
+
export const users = pgTable('users', {
|
|
108
|
+
id: serial('id').primaryKey(),
|
|
109
|
+
name: text('name').notNull(),
|
|
110
|
+
email: text('email').notNull().unique(),
|
|
111
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
112
|
+
});
|
|
113
|
+
`;
|
|
114
|
+
var drizzleDbTemplate = `import { drizzle } from 'drizzle-orm/postgres-js';
|
|
115
|
+
import postgres from 'postgres';
|
|
116
|
+
|
|
117
|
+
const queryClient = postgres(process.env.DATABASE_URL || 'postgres://localhost:5432/nexus');
|
|
118
|
+
export const db = drizzle(queryClient);
|
|
119
|
+
`;
|
|
120
|
+
var nextjsPageTemplate = `import { hc } from 'hono/client';
|
|
121
|
+
import { type AppType } from '../server';
|
|
122
|
+
|
|
123
|
+
// @ai-intent: HONO RPC CLIENT INSTANTIATION
|
|
124
|
+
// Agents MUST use this \`client\` to interact with the backend to preserve end-to-end type safety.
|
|
125
|
+
const client = hc<AppType>('http://localhost:3000');
|
|
126
|
+
|
|
127
|
+
export default async function Home() {
|
|
128
|
+
const res = await client.api.users.$get();
|
|
129
|
+
const users = await res.json();
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<main className="min-h-screen bg-black text-white p-24 font-sans">
|
|
133
|
+
<div className="max-w-xl mx-auto space-y-8">
|
|
134
|
+
<h1 className="text-4xl font-bold tracking-tight">Project Nexus \u{1F9E0}</h1>
|
|
135
|
+
<p className="text-zinc-400">
|
|
136
|
+
Agent-Native Boilerplate initialized. Next.js, Hono, Drizzle, and Zod perfectly aligned.
|
|
137
|
+
</p>
|
|
138
|
+
|
|
139
|
+
<div className="p-6 border border-zinc-800 rounded-lg bg-zinc-950">
|
|
140
|
+
<h2 className="text-xl font-semibold mb-4">Database Users</h2>
|
|
141
|
+
{users.length === 0 ? (
|
|
142
|
+
<p className="text-zinc-500">No users found. Try adding one via the API.</p>
|
|
143
|
+
) : (
|
|
144
|
+
<ul className="space-y-2">
|
|
145
|
+
{users.map((u: any) => (
|
|
146
|
+
<li key={u.id} className="text-zinc-300">\u2022 {u.name} ({u.email})</li>
|
|
147
|
+
))}
|
|
148
|
+
</ul>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</main>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
// src/templates/mcp.ts
|
|
158
|
+
var mcpServerTemplate = `import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
159
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
160
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
161
|
+
import { execSync } from 'child_process';
|
|
162
|
+
import fs from 'fs';
|
|
163
|
+
|
|
164
|
+
// @ai-intent: MCP SERVER STUB
|
|
165
|
+
// Agents can connect to this server via stdio to execute read-only queries against the environment.
|
|
166
|
+
|
|
167
|
+
const server = new Server(
|
|
168
|
+
{ name: "nexus-mcp", version: "1.0.0" },
|
|
169
|
+
{ capabilities: { tools: {} } }
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
173
|
+
return {
|
|
174
|
+
tools: [
|
|
175
|
+
{
|
|
176
|
+
name: "get_drizzle_schema",
|
|
177
|
+
description: "Returns the raw text of the current Drizzle database schema to maintain context without hallucinating.",
|
|
178
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "check_build_health",
|
|
182
|
+
description: "Runs typescript compiler checks to verify if the codebase currently has any type errors.",
|
|
183
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
190
|
+
if (request.params.name === "get_drizzle_schema") {
|
|
191
|
+
try {
|
|
192
|
+
const schema = fs.readFileSync('./src/db/schema.ts', 'utf-8');
|
|
193
|
+
return { toolResult: schema };
|
|
194
|
+
} catch {
|
|
195
|
+
return { toolResult: "Schema file not found." };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (request.params.name === "check_build_health") {
|
|
200
|
+
try {
|
|
201
|
+
const output = execSync('npx tsc --noEmit', { encoding: 'utf-8' });
|
|
202
|
+
return { toolResult: output || "Build is perfectly healthy. No type errors." };
|
|
203
|
+
} catch (e: any) {
|
|
204
|
+
return { toolResult: \`Type errors detected:\\n\${e.stdout}\` };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
throw new Error("Tool not found");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const transport = new StdioServerTransport();
|
|
212
|
+
server.connect(transport).catch(console.error);
|
|
213
|
+
`;
|
|
214
|
+
|
|
215
|
+
// src/generator.ts
|
|
216
|
+
async function generateBoilerplate(projectName) {
|
|
217
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
218
|
+
if (fs.existsSync(targetDir)) {
|
|
219
|
+
throw new Error(`Directory ${projectName} already exists.`);
|
|
220
|
+
}
|
|
221
|
+
await fs.ensureDir(targetDir);
|
|
222
|
+
const dirs = [
|
|
223
|
+
"src/app",
|
|
224
|
+
"src/server",
|
|
225
|
+
"src/db",
|
|
226
|
+
"server/mcp",
|
|
227
|
+
".agent",
|
|
228
|
+
".husky"
|
|
229
|
+
];
|
|
230
|
+
for (const dir of dirs) {
|
|
231
|
+
await fs.ensureDir(path.join(targetDir, dir));
|
|
232
|
+
}
|
|
233
|
+
await fs.writeFile(path.join(targetDir, ".agent/rules.md"), agentRulesTemplate);
|
|
234
|
+
await fs.writeFile(path.join(targetDir, ".agent/project-map.json"), agentProjectMapTemplate);
|
|
235
|
+
await fs.writeFile(path.join(targetDir, ".agent/scratchpad.md"), agentScratchpadTemplate);
|
|
236
|
+
await fs.writeFile(path.join(targetDir, "src/app/CONCEPTS.md"), twinAppConceptsTemplate);
|
|
237
|
+
await fs.writeFile(path.join(targetDir, "src/server/CONCEPTS.md"), twinServerConceptsTemplate);
|
|
238
|
+
await fs.writeFile(path.join(targetDir, "src/db/CONCEPTS.md"), twinDbConceptsTemplate);
|
|
239
|
+
await fs.writeFile(path.join(targetDir, "server/mcp/index.ts"), mcpServerTemplate);
|
|
240
|
+
await fs.writeFile(path.join(targetDir, "src/server/index.ts"), honoServerTemplate);
|
|
241
|
+
await fs.writeFile(path.join(targetDir, "src/db/schema.ts"), drizzleSchemaTemplate);
|
|
242
|
+
await fs.writeFile(path.join(targetDir, "src/db/index.ts"), drizzleDbTemplate);
|
|
243
|
+
await fs.writeFile(path.join(targetDir, "src/app/page.tsx"), nextjsPageTemplate);
|
|
244
|
+
const packageJson = {
|
|
245
|
+
name: projectName,
|
|
246
|
+
version: "0.1.0",
|
|
247
|
+
private: true,
|
|
248
|
+
scripts: {
|
|
249
|
+
"dev": "next dev",
|
|
250
|
+
"build": "next build",
|
|
251
|
+
"start": "next start",
|
|
252
|
+
"lint": "next lint",
|
|
253
|
+
"check": "tsc --noEmit",
|
|
254
|
+
"db:push": "drizzle-kit push",
|
|
255
|
+
"prepare": "husky install"
|
|
256
|
+
},
|
|
257
|
+
dependencies: {
|
|
258
|
+
"next": "14.2.3",
|
|
259
|
+
"react": "^18",
|
|
260
|
+
"react-dom": "^18",
|
|
261
|
+
"hono": "^4.3.0",
|
|
262
|
+
"zod": "^3.23.0",
|
|
263
|
+
"@hono/zod-validator": "^0.2.1",
|
|
264
|
+
"drizzle-orm": "^0.30.0",
|
|
265
|
+
"postgres": "^3.4.4",
|
|
266
|
+
"@modelcontextprotocol/sdk": "^1.27.1"
|
|
267
|
+
},
|
|
268
|
+
devDependencies: {
|
|
269
|
+
"typescript": "^5",
|
|
270
|
+
"@types/node": "^20",
|
|
271
|
+
"@types/react": "^18",
|
|
272
|
+
"@types/react-dom": "^18",
|
|
273
|
+
"postcss": "^8",
|
|
274
|
+
"tailwindcss": "^3.4.1",
|
|
275
|
+
"drizzle-kit": "^0.21.0",
|
|
276
|
+
"husky": "^9.0.0",
|
|
277
|
+
"lint-staged": "^15.2.0"
|
|
278
|
+
},
|
|
279
|
+
"lint-staged": {
|
|
280
|
+
"*.ts?(x)": [
|
|
281
|
+
"tsc --noEmit"
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
await fs.writeJson(path.join(targetDir, "package.json"), packageJson, { spaces: 2 });
|
|
286
|
+
const tsconfig = {
|
|
287
|
+
"compilerOptions": {
|
|
288
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
289
|
+
"allowJs": true,
|
|
290
|
+
"skipLibCheck": true,
|
|
291
|
+
"strict": true,
|
|
292
|
+
"noEmit": true,
|
|
293
|
+
"esModuleInterop": true,
|
|
294
|
+
"module": "esnext",
|
|
295
|
+
"moduleResolution": "bundler",
|
|
296
|
+
"resolveJsonModule": true,
|
|
297
|
+
"isolatedModules": true,
|
|
298
|
+
"jsx": "preserve",
|
|
299
|
+
"incremental": true,
|
|
300
|
+
"plugins": [
|
|
301
|
+
{
|
|
302
|
+
"name": "next"
|
|
303
|
+
}
|
|
304
|
+
],
|
|
305
|
+
"paths": {
|
|
306
|
+
"@/*": ["./src/*"]
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
310
|
+
"exclude": ["node_modules"]
|
|
311
|
+
};
|
|
312
|
+
await fs.writeJson(path.join(targetDir, "tsconfig.json"), tsconfig, { spaces: 2 });
|
|
313
|
+
const huskyPreCommit = `#!/usr/bin/env sh
|
|
314
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
315
|
+
|
|
316
|
+
npx lint-staged
|
|
317
|
+
npm run check
|
|
318
|
+
`;
|
|
319
|
+
await fs.writeFile(path.join(targetDir, ".husky/pre-commit"), huskyPreCommit);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/index.ts
|
|
323
|
+
import { execa } from "execa";
|
|
324
|
+
var program = new Command();
|
|
325
|
+
program.name("create-nexus").description("Scaffold the ultimate Agent-Native Boilerplate.").version("1.0.0");
|
|
326
|
+
program.action(async () => {
|
|
327
|
+
console.log();
|
|
328
|
+
intro(pc.inverse(pc.bold(" \u{1F9E0} CREATE NEXUS: Agent-Native Stack ")));
|
|
329
|
+
const projectName = await text({
|
|
330
|
+
message: "What is your project named?",
|
|
331
|
+
placeholder: "my-agent-app",
|
|
332
|
+
validate(value) {
|
|
333
|
+
if (!value || value.length === 0) return "Project name is required!";
|
|
334
|
+
if (/^[a-zA-Z0-9-]+$/.test(value) === false) return "Project name can only contain letters, numbers, and dashes.";
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
if (isCancel(projectName)) {
|
|
338
|
+
outro("Operation cancelled.");
|
|
339
|
+
process.exit(0);
|
|
340
|
+
}
|
|
341
|
+
const s = spinner();
|
|
342
|
+
s.start(`Scaffolding Agent-Native architecture into ${projectName}...`);
|
|
343
|
+
try {
|
|
344
|
+
await generateBoilerplate(projectName);
|
|
345
|
+
s.stop(pc.green("\u2713 Scaffold instantiated successfully."));
|
|
346
|
+
} catch (err) {
|
|
347
|
+
s.stop(pc.red("\u2716 Failed to generate project."));
|
|
348
|
+
console.error(pc.red(err.message));
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
console.log();
|
|
352
|
+
console.log(pc.bold("Included in this High-Density Stack:"));
|
|
353
|
+
console.log(pc.cyan(" \u2022 Next.js App Router") + " (Frontend)");
|
|
354
|
+
console.log(pc.cyan(" \u2022 Hono RPC") + " (Type-safe API Backend)");
|
|
355
|
+
console.log(pc.cyan(" \u2022 Drizzle ORM + SQLite") + " (Strict Zod Schemas)");
|
|
356
|
+
console.log(pc.cyan(" \u2022 The Twin-File Architecture") + " (CONCEPTS.md context layer)");
|
|
357
|
+
console.log(pc.cyan(" \u2022 Integrated MCP Stub") + " (server/mcp for Agent querying)");
|
|
358
|
+
console.log(pc.cyan(" \u2022 Husky + tsc") + " (Strict commit verification hooks)");
|
|
359
|
+
console.log();
|
|
360
|
+
const installDeps = await confirm({
|
|
361
|
+
message: "Would you like to install dependencies now? (npm install)",
|
|
362
|
+
initialValue: true
|
|
363
|
+
});
|
|
364
|
+
if (isCancel(installDeps)) {
|
|
365
|
+
outro("Operation cancelled.");
|
|
366
|
+
process.exit(0);
|
|
367
|
+
}
|
|
368
|
+
if (installDeps) {
|
|
369
|
+
s.start("Installing dependencies via npm...");
|
|
370
|
+
try {
|
|
371
|
+
await execa("npm", ["install"], { cwd: projectName });
|
|
372
|
+
s.stop(pc.green("\u2713 Dependencies installed."));
|
|
373
|
+
} catch {
|
|
374
|
+
s.stop(pc.red("\u2716 Failed to install dependencies. You can run npm install manually later."));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const initGit = await confirm({
|
|
378
|
+
message: "Initialize a new Git repository?",
|
|
379
|
+
initialValue: true
|
|
380
|
+
});
|
|
381
|
+
if (!isCancel(initGit) && initGit) {
|
|
382
|
+
try {
|
|
383
|
+
await execa("git", ["init"], { cwd: projectName });
|
|
384
|
+
console.log(pc.green("\u2713 Git repository initialized."));
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
console.log();
|
|
389
|
+
console.log(pc.bgGreen(" SUCCESS! ") + ` Project ${projectName} is ready for an AI Agent to dominate.`);
|
|
390
|
+
console.log("Next steps:");
|
|
391
|
+
console.log(pc.bold(` cd ${projectName}`));
|
|
392
|
+
console.log(pc.bold(" npm run dev"));
|
|
393
|
+
outro("Good luck building!");
|
|
394
|
+
});
|
|
395
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dinakars777/create-nexus",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Scaffold the ultimate Agent-Native Boilerplate. Next.js, Hono, Drizzle, Zod, with built-in .agent control plane.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-nexus": "dist/index.js",
|
|
9
|
+
"create-nexus-app": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "tsup watch",
|
|
13
|
+
"build": "tsup src/index.ts --format esm --clean"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"agent",
|
|
18
|
+
"boilerplate",
|
|
19
|
+
"nextjs",
|
|
20
|
+
"hono",
|
|
21
|
+
"drizzle"
|
|
22
|
+
],
|
|
23
|
+
"author": "Dinakar Sarbada",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@clack/prompts": "^0.7.0",
|
|
27
|
+
"commander": "^11.1.0",
|
|
28
|
+
"execa": "^8.0.1",
|
|
29
|
+
"fs-extra": "^11.2.0",
|
|
30
|
+
"picocolors": "^1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/fs-extra": "^11.0.4",
|
|
34
|
+
"@types/node": "^20.10.0",
|
|
35
|
+
"tsup": "^8.0.0",
|
|
36
|
+
"typescript": "^5.3.2"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/generator.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { agentRulesTemplate, agentProjectMapTemplate, agentScratchpadTemplate } from './templates/agent';
|
|
4
|
+
import { twinAppConceptsTemplate, twinServerConceptsTemplate, twinDbConceptsTemplate } from './templates/twin';
|
|
5
|
+
import { honoServerTemplate, drizzleSchemaTemplate, drizzleDbTemplate, nextjsPageTemplate } from './templates/stack';
|
|
6
|
+
import { mcpServerTemplate } from './templates/mcp';
|
|
7
|
+
|
|
8
|
+
export async function generateBoilerplate(projectName: string) {
|
|
9
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
10
|
+
|
|
11
|
+
if (fs.existsSync(targetDir)) {
|
|
12
|
+
throw new Error(`Directory ${projectName} already exists.`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 1. Core Directory Scaffolding
|
|
16
|
+
await fs.ensureDir(targetDir);
|
|
17
|
+
const dirs = [
|
|
18
|
+
'src/app',
|
|
19
|
+
'src/server',
|
|
20
|
+
'src/db',
|
|
21
|
+
'server/mcp',
|
|
22
|
+
'.agent',
|
|
23
|
+
'.husky'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
for (const dir of dirs) {
|
|
27
|
+
await fs.ensureDir(path.join(targetDir, dir));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 2. The Agent Control Directory
|
|
31
|
+
await fs.writeFile(path.join(targetDir, '.agent/rules.md'), agentRulesTemplate);
|
|
32
|
+
await fs.writeFile(path.join(targetDir, '.agent/project-map.json'), agentProjectMapTemplate);
|
|
33
|
+
await fs.writeFile(path.join(targetDir, '.agent/scratchpad.md'), agentScratchpadTemplate);
|
|
34
|
+
|
|
35
|
+
// 3. The Twin-File System (CONCEPTS.md)
|
|
36
|
+
await fs.writeFile(path.join(targetDir, 'src/app/CONCEPTS.md'), twinAppConceptsTemplate);
|
|
37
|
+
await fs.writeFile(path.join(targetDir, 'src/server/CONCEPTS.md'), twinServerConceptsTemplate);
|
|
38
|
+
await fs.writeFile(path.join(targetDir, 'src/db/CONCEPTS.md'), twinDbConceptsTemplate);
|
|
39
|
+
|
|
40
|
+
// 4. MCP Server Stub
|
|
41
|
+
await fs.writeFile(path.join(targetDir, 'server/mcp/index.ts'), mcpServerTemplate);
|
|
42
|
+
|
|
43
|
+
// 5. Stack Templates
|
|
44
|
+
await fs.writeFile(path.join(targetDir, 'src/server/index.ts'), honoServerTemplate);
|
|
45
|
+
await fs.writeFile(path.join(targetDir, 'src/db/schema.ts'), drizzleSchemaTemplate);
|
|
46
|
+
await fs.writeFile(path.join(targetDir, 'src/db/index.ts'), drizzleDbTemplate);
|
|
47
|
+
await fs.writeFile(path.join(targetDir, 'src/app/page.tsx'), nextjsPageTemplate);
|
|
48
|
+
|
|
49
|
+
// 6. Base Package Config & Verification Hooks (Husky)
|
|
50
|
+
const packageJson = {
|
|
51
|
+
name: projectName,
|
|
52
|
+
version: "0.1.0",
|
|
53
|
+
private: true,
|
|
54
|
+
scripts: {
|
|
55
|
+
"dev": "next dev",
|
|
56
|
+
"build": "next build",
|
|
57
|
+
"start": "next start",
|
|
58
|
+
"lint": "next lint",
|
|
59
|
+
"check": "tsc --noEmit",
|
|
60
|
+
"db:push": "drizzle-kit push",
|
|
61
|
+
"prepare": "husky install"
|
|
62
|
+
},
|
|
63
|
+
dependencies: {
|
|
64
|
+
"next": "14.2.3",
|
|
65
|
+
"react": "^18",
|
|
66
|
+
"react-dom": "^18",
|
|
67
|
+
"hono": "^4.3.0",
|
|
68
|
+
"zod": "^3.23.0",
|
|
69
|
+
"@hono/zod-validator": "^0.2.1",
|
|
70
|
+
"drizzle-orm": "^0.30.0",
|
|
71
|
+
"postgres": "^3.4.4",
|
|
72
|
+
"@modelcontextprotocol/sdk": "^1.27.1"
|
|
73
|
+
},
|
|
74
|
+
devDependencies: {
|
|
75
|
+
"typescript": "^5",
|
|
76
|
+
"@types/node": "^20",
|
|
77
|
+
"@types/react": "^18",
|
|
78
|
+
"@types/react-dom": "^18",
|
|
79
|
+
"postcss": "^8",
|
|
80
|
+
"tailwindcss": "^3.4.1",
|
|
81
|
+
"drizzle-kit": "^0.21.0",
|
|
82
|
+
"husky": "^9.0.0",
|
|
83
|
+
"lint-staged": "^15.2.0"
|
|
84
|
+
},
|
|
85
|
+
"lint-staged": {
|
|
86
|
+
"*.ts?(x)": [
|
|
87
|
+
"tsc --noEmit"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
await fs.writeJson(path.join(targetDir, 'package.json'), packageJson, { spaces: 2 });
|
|
93
|
+
|
|
94
|
+
// TSConfig
|
|
95
|
+
const tsconfig = {
|
|
96
|
+
"compilerOptions": {
|
|
97
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
98
|
+
"allowJs": true,
|
|
99
|
+
"skipLibCheck": true,
|
|
100
|
+
"strict": true,
|
|
101
|
+
"noEmit": true,
|
|
102
|
+
"esModuleInterop": true,
|
|
103
|
+
"module": "esnext",
|
|
104
|
+
"moduleResolution": "bundler",
|
|
105
|
+
"resolveJsonModule": true,
|
|
106
|
+
"isolatedModules": true,
|
|
107
|
+
"jsx": "preserve",
|
|
108
|
+
"incremental": true,
|
|
109
|
+
"plugins": [
|
|
110
|
+
{
|
|
111
|
+
"name": "next"
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"paths": {
|
|
115
|
+
"@/*": ["./src/*"]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
119
|
+
"exclude": ["node_modules"]
|
|
120
|
+
};
|
|
121
|
+
await fs.writeJson(path.join(targetDir, 'tsconfig.json'), tsconfig, { spaces: 2 });
|
|
122
|
+
|
|
123
|
+
// Husky Pre-commit hook
|
|
124
|
+
const huskyPreCommit = `#!/usr/bin/env sh
|
|
125
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
126
|
+
|
|
127
|
+
npx lint-staged
|
|
128
|
+
npm run check
|
|
129
|
+
`;
|
|
130
|
+
await fs.writeFile(path.join(targetDir, '.husky/pre-commit'), huskyPreCommit);
|
|
131
|
+
// Ensure the hook is executable (not natively supported by fs.writeFile easily cross-platform, but good enough for a generated boilerplate before install)
|
|
132
|
+
|
|
133
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { intro, outro, spinner, text, confirm, isCancel } from '@clack/prompts';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { generateBoilerplate } from './generator';
|
|
6
|
+
import { execa } from 'execa';
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('create-nexus')
|
|
12
|
+
.description('Scaffold the ultimate Agent-Native Boilerplate.')
|
|
13
|
+
.version('1.0.0');
|
|
14
|
+
|
|
15
|
+
program.action(async () => {
|
|
16
|
+
console.log();
|
|
17
|
+
intro(pc.inverse(pc.bold(' 🧠CREATE NEXUS: Agent-Native Stack ')));
|
|
18
|
+
|
|
19
|
+
const projectName = await text({
|
|
20
|
+
message: 'What is your project named?',
|
|
21
|
+
placeholder: 'my-agent-app',
|
|
22
|
+
validate(value) {
|
|
23
|
+
if (!value || value.length === 0) return 'Project name is required!';
|
|
24
|
+
if (/^[a-zA-Z0-9-]+$/.test(value) === false) return 'Project name can only contain letters, numbers, and dashes.';
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (isCancel(projectName)) {
|
|
29
|
+
outro('Operation cancelled.');
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const s = spinner();
|
|
34
|
+
s.start(`Scaffolding Agent-Native architecture into ${projectName}...`);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await generateBoilerplate(projectName as string);
|
|
38
|
+
s.stop(pc.green('✓ Scaffold instantiated successfully.'));
|
|
39
|
+
} catch (err: any) {
|
|
40
|
+
s.stop(pc.red('✖ Failed to generate project.'));
|
|
41
|
+
console.error(pc.red(err.message));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log();
|
|
46
|
+
console.log(pc.bold('Included in this High-Density Stack:'));
|
|
47
|
+
console.log(pc.cyan(' • Next.js App Router') + ' (Frontend)');
|
|
48
|
+
console.log(pc.cyan(' • Hono RPC') + ' (Type-safe API Backend)');
|
|
49
|
+
console.log(pc.cyan(' • Drizzle ORM + SQLite') + ' (Strict Zod Schemas)');
|
|
50
|
+
console.log(pc.cyan(' • The Twin-File Architecture') + ' (CONCEPTS.md context layer)');
|
|
51
|
+
console.log(pc.cyan(' • Integrated MCP Stub') + ' (server/mcp for Agent querying)');
|
|
52
|
+
console.log(pc.cyan(' • Husky + tsc') + ' (Strict commit verification hooks)');
|
|
53
|
+
console.log();
|
|
54
|
+
|
|
55
|
+
const installDeps = await confirm({
|
|
56
|
+
message: 'Would you like to install dependencies now? (npm install)',
|
|
57
|
+
initialValue: true,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (isCancel(installDeps)) {
|
|
61
|
+
outro('Operation cancelled.');
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (installDeps) {
|
|
66
|
+
s.start('Installing dependencies via npm...');
|
|
67
|
+
try {
|
|
68
|
+
await execa('npm', ['install'], { cwd: projectName as string });
|
|
69
|
+
s.stop(pc.green('✓ Dependencies installed.'));
|
|
70
|
+
} catch {
|
|
71
|
+
s.stop(pc.red('✖ Failed to install dependencies. You can run npm install manually later.'));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const initGit = await confirm({
|
|
76
|
+
message: 'Initialize a new Git repository?',
|
|
77
|
+
initialValue: true,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!isCancel(initGit) && initGit) {
|
|
81
|
+
try {
|
|
82
|
+
await execa('git', ['init'], { cwd: projectName as string });
|
|
83
|
+
console.log(pc.green('✓ Git repository initialized.'));
|
|
84
|
+
} catch {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log();
|
|
88
|
+
console.log(pc.bgGreen(' SUCCESS! ') + ` Project ${projectName} is ready for an AI Agent to dominate.`);
|
|
89
|
+
console.log('Next steps:');
|
|
90
|
+
console.log(pc.bold(` cd ${projectName}`));
|
|
91
|
+
console.log(pc.bold(' npm run dev'));
|
|
92
|
+
|
|
93
|
+
outro('Good luck building!');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const agentRulesTemplate = `# Global Agent Rules
|
|
2
|
+
- **Verify Before Commit:** Always run \`pnpm check\` (or \`npm run check\`) which triggers tsc and vitest after a change. Do not commit failing code.
|
|
3
|
+
- **Strict Typing:** Never use \`any\`. Use Zod for all validations.
|
|
4
|
+
- **Twin-Files:** If you create a new directory, you MUST create a \`CONCEPTS.md\` explaining its business logic and boundaries.
|
|
5
|
+
- **Architecture Limits:** Do not import UI components into the API layer. Do not import Drizzle directly into the Next.js React UI layer.
|
|
6
|
+
- **Think First:** Before making massive destructive changes, write your plan in \`.agent/scratchpad.md\`.
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
export const agentProjectMapTemplate = `{
|
|
10
|
+
"name": "nexus-app",
|
|
11
|
+
"architecture": {
|
|
12
|
+
"frontend": "Next.js App Router (React Server Components)",
|
|
13
|
+
"api": "Hono RPC",
|
|
14
|
+
"database": "Drizzle ORM + SQLite",
|
|
15
|
+
"validation": "Zod"
|
|
16
|
+
},
|
|
17
|
+
"directories": {
|
|
18
|
+
"src/app": "Next.js Routing and React UI Pages",
|
|
19
|
+
"src/server": "Hono API backend and RCP routes",
|
|
20
|
+
"src/db": "Drizzle schema operations",
|
|
21
|
+
"server/mcp": "Model Context Protocol stubs"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export const agentScratchpadTemplate = `# Agent Scratchpad
|
|
27
|
+
*Use this file to think out loud, draft complex refactors, or save temporary snippets before applying them to the main codebase.*
|
|
28
|
+
|
|
29
|
+
## Current Objective
|
|
30
|
+
...
|
|
31
|
+
`;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const mcpServerTemplate = `import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
|
|
7
|
+
// @ai-intent: MCP SERVER STUB
|
|
8
|
+
// Agents can connect to this server via stdio to execute read-only queries against the environment.
|
|
9
|
+
|
|
10
|
+
const server = new Server(
|
|
11
|
+
{ name: "nexus-mcp", version: "1.0.0" },
|
|
12
|
+
{ capabilities: { tools: {} } }
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
16
|
+
return {
|
|
17
|
+
tools: [
|
|
18
|
+
{
|
|
19
|
+
name: "get_drizzle_schema",
|
|
20
|
+
description: "Returns the raw text of the current Drizzle database schema to maintain context without hallucinating.",
|
|
21
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "check_build_health",
|
|
25
|
+
description: "Runs typescript compiler checks to verify if the codebase currently has any type errors.",
|
|
26
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
33
|
+
if (request.params.name === "get_drizzle_schema") {
|
|
34
|
+
try {
|
|
35
|
+
const schema = fs.readFileSync('./src/db/schema.ts', 'utf-8');
|
|
36
|
+
return { toolResult: schema };
|
|
37
|
+
} catch {
|
|
38
|
+
return { toolResult: "Schema file not found." };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (request.params.name === "check_build_health") {
|
|
43
|
+
try {
|
|
44
|
+
const output = execSync('npx tsc --noEmit', { encoding: 'utf-8' });
|
|
45
|
+
return { toolResult: output || "Build is perfectly healthy. No type errors." };
|
|
46
|
+
} catch (e: any) {
|
|
47
|
+
return { toolResult: \`Type errors detected:\\n\${e.stdout}\` };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw new Error("Tool not found");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const transport = new StdioServerTransport();
|
|
55
|
+
server.connect(transport).catch(console.error);
|
|
56
|
+
`;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export const honoServerTemplate = `import { Hono } from 'hono';
|
|
2
|
+
import { zValidator } from '@hono/zod-validator';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { db } from '../db';
|
|
5
|
+
import { users } from '../db/schema';
|
|
6
|
+
|
|
7
|
+
const app = new Hono().basePath('/api');
|
|
8
|
+
|
|
9
|
+
// @ai-intent: STRICT ZOD VALIDATION REQUIRED FOR ALL ROUTES
|
|
10
|
+
const createUserSchema = z.object({
|
|
11
|
+
name: z.string().min(1),
|
|
12
|
+
email: z.string().email(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const routes = app
|
|
16
|
+
.get('/users', async (c) => {
|
|
17
|
+
const allUsers = await db.select().from(users);
|
|
18
|
+
return c.json(allUsers);
|
|
19
|
+
})
|
|
20
|
+
.post('/users', zValidator('json', createUserSchema), async (c) => {
|
|
21
|
+
const { name, email } = c.req.valid('json');
|
|
22
|
+
const result = await db.insert(users).values({ name, email }).returning();
|
|
23
|
+
return c.json(result[0], 201);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type AppType = typeof routes;
|
|
27
|
+
export default app;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export const drizzleSchemaTemplate = `import { pgTable, text, serial, timestamp } from 'drizzle-orm/pg-core';
|
|
31
|
+
|
|
32
|
+
// @ai-intent: SINGLE SOURCE OF TRUTH. Do not write raw SQL.
|
|
33
|
+
export const users = pgTable('users', {
|
|
34
|
+
id: serial('id').primaryKey(),
|
|
35
|
+
name: text('name').notNull(),
|
|
36
|
+
email: text('email').notNull().unique(),
|
|
37
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
38
|
+
});
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
export const drizzleDbTemplate = `import { drizzle } from 'drizzle-orm/postgres-js';
|
|
42
|
+
import postgres from 'postgres';
|
|
43
|
+
|
|
44
|
+
const queryClient = postgres(process.env.DATABASE_URL || 'postgres://localhost:5432/nexus');
|
|
45
|
+
export const db = drizzle(queryClient);
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export const nextjsPageTemplate = `import { hc } from 'hono/client';
|
|
49
|
+
import { type AppType } from '../server';
|
|
50
|
+
|
|
51
|
+
// @ai-intent: HONO RPC CLIENT INSTANTIATION
|
|
52
|
+
// Agents MUST use this \`client\` to interact with the backend to preserve end-to-end type safety.
|
|
53
|
+
const client = hc<AppType>('http://localhost:3000');
|
|
54
|
+
|
|
55
|
+
export default async function Home() {
|
|
56
|
+
const res = await client.api.users.$get();
|
|
57
|
+
const users = await res.json();
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<main className="min-h-screen bg-black text-white p-24 font-sans">
|
|
61
|
+
<div className="max-w-xl mx-auto space-y-8">
|
|
62
|
+
<h1 className="text-4xl font-bold tracking-tight">Project Nexus 🧠</h1>
|
|
63
|
+
<p className="text-zinc-400">
|
|
64
|
+
Agent-Native Boilerplate initialized. Next.js, Hono, Drizzle, and Zod perfectly aligned.
|
|
65
|
+
</p>
|
|
66
|
+
|
|
67
|
+
<div className="p-6 border border-zinc-800 rounded-lg bg-zinc-950">
|
|
68
|
+
<h2 className="text-xl font-semibold mb-4">Database Users</h2>
|
|
69
|
+
{users.length === 0 ? (
|
|
70
|
+
<p className="text-zinc-500">No users found. Try adding one via the API.</p>
|
|
71
|
+
) : (
|
|
72
|
+
<ul className="space-y-2">
|
|
73
|
+
{users.map((u: any) => (
|
|
74
|
+
<li key={u.id} className="text-zinc-300">• {u.name} ({u.email})</li>
|
|
75
|
+
))}
|
|
76
|
+
</ul>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</main>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const twinAppConceptsTemplate = `# CONCEPTS: Frontend (src/app)
|
|
2
|
+
|
|
3
|
+
## Business Logic
|
|
4
|
+
This directory contains the user-facing React application. It is responsible purely for data presentation and optimistic UI updates.
|
|
5
|
+
|
|
6
|
+
## Boundaries
|
|
7
|
+
- **DO NOT** perform direct database queries (Drizzle) from here.
|
|
8
|
+
- **DO NOT** define raw Zod schemas for the database here.
|
|
9
|
+
- **DO** use the generated Hono RPC client (e.g., \`hc\`) to communicate with the \`src/server\` API.
|
|
10
|
+
- All components should default to React Server Components unless interactive hooks (useState, useEffect) are strictly required (use \`"use client"\`).
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
export const twinServerConceptsTemplate = `# CONCEPTS: API Backend (src/server)
|
|
14
|
+
|
|
15
|
+
## Business Logic
|
|
16
|
+
This directory houses the Hono API routes. This is the exclusive gateway for all data mutations and retrievals.
|
|
17
|
+
|
|
18
|
+
## Boundaries
|
|
19
|
+
- **MUST** validate all incoming payloads using \`zValidator\` with Zod.
|
|
20
|
+
- **MUST** export the \`AppType\` router type so the Next.js frontend can consume the RPC client with end-to-end type safety.
|
|
21
|
+
- **DO NOT** import React or UI components into this layer.
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const twinDbConceptsTemplate = `# CONCEPTS: Database Layer (src/db)
|
|
25
|
+
|
|
26
|
+
## Business Logic
|
|
27
|
+
This directory contains the single source of truth for the database schema using Drizzle ORM.
|
|
28
|
+
|
|
29
|
+
## Boundaries
|
|
30
|
+
- **MUST** export Drizzle Select/Insert schemas (e.g., \`createInsertSchema\`) using \`drizzle-zod\`.
|
|
31
|
+
- This layer does not execute business logic; it strictly defines the schema shapes and relationships.
|
|
32
|
+
`;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"rootDir": "src"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|