create-kuckit-app 0.4.0 → 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/dist/bin.js +1 -1
- package/dist/{create-project-geQBZ0Ru.js → create-project-CAsuZMK5.js} +2 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/base/.claude/CLAUDE.md +83 -0
- package/templates/base/.claude/{commands/file-beads.md → skills/file-beads/SKILL.md} +3 -3
- package/templates/base/.claude/{commands/review-beads.md → skills/review-beads/SKILL.md} +3 -3
- package/templates/base/AGENTS.md +86 -0
- package/templates/base/apps/server/AGENTS.md +42 -85
- package/templates/base/apps/server/package.json +1 -13
- package/templates/base/apps/server/src/config.ts +12 -0
- package/templates/base/apps/server/src/modules.ts +66 -0
- package/templates/base/apps/server/src/server.ts +4 -46
- package/templates/base/apps/web/AGENTS.md +68 -91
- package/templates/base/apps/web/package.json +2 -6
- package/templates/base/apps/web/src/components/dashboard/dashboard-overview.tsx +2 -2
- package/templates/base/apps/web/src/components/dashboard/nav-user.tsx +2 -2
- package/templates/base/apps/web/src/main.tsx +12 -22
- package/templates/base/apps/web/src/modules.client.ts +43 -9
- package/templates/base/apps/web/src/routes/$.tsx +1 -1
- package/templates/base/apps/web/src/routes/__root.tsx +1 -1
- package/templates/base/apps/web/src/routes/dashboard/$.tsx +1 -1
- package/templates/base/apps/web/src/routes/dashboard.tsx +1 -1
- package/templates/base/apps/web/src/routes/index.tsx +2 -2
- package/templates/base/apps/web/src/routes/login.tsx +2 -2
- package/templates/base/docker-compose.yml +1 -1
- package/templates/base/drizzle.config.ts +2 -6
- package/templates/base/kuckit.config.ts +30 -0
- package/templates/base/package.json +3 -0
- package/templates/base/packages/items-module/AGENTS.md +83 -0
- package/templates/base/apps/server/src/app.ts +0 -20
- package/templates/base/apps/server/src/auth.ts +0 -10
- package/templates/base/apps/server/src/config/modules.ts +0 -21
- package/templates/base/apps/server/src/container.ts +0 -83
- package/templates/base/apps/server/src/health.ts +0 -27
- package/templates/base/apps/server/src/middleware/container.ts +0 -41
- package/templates/base/apps/server/src/module-rest-routes.ts +0 -47
- package/templates/base/apps/server/src/rest-router-registry.ts +0 -32
- package/templates/base/apps/server/src/rpc-router-registry.ts +0 -26
- package/templates/base/apps/server/src/rpc.ts +0 -31
- package/templates/base/apps/web/src/components/KuckitModuleRoute.tsx +0 -119
- package/templates/base/apps/web/src/providers/KuckitProvider.tsx +0 -123
- package/templates/base/apps/web/src/providers/ServicesProvider.tsx +0 -47
- package/templates/base/apps/web/src/services/auth-client.ts +0 -12
- package/templates/base/apps/web/src/services/index.ts +0 -3
- package/templates/base/apps/web/src/services/rpc.ts +0 -29
- package/templates/base/apps/web/src/services/types.ts +0 -14
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as createProject } from "./create-project-
|
|
2
|
+
import { t as createProject } from "./create-project-CAsuZMK5.js";
|
|
3
3
|
import { program } from "commander";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -39,7 +39,8 @@ async function createProject(name, options) {
|
|
|
39
39
|
const replacements = {
|
|
40
40
|
__APP_NAME__: name,
|
|
41
41
|
__APP_NAME_LOWER__: name.toLowerCase(),
|
|
42
|
-
__APP_NAME_KEBAB__: name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
|
|
42
|
+
__APP_NAME_KEBAB__: name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
|
|
43
|
+
__APP_TITLE__: name
|
|
43
44
|
};
|
|
44
45
|
console.log(" Copying template files...");
|
|
45
46
|
await copyDir(templateDir, targetDir, replacements);
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -42,3 +42,86 @@ This is a Kuckit application using Clean Architecture patterns.
|
|
|
42
42
|
- Adapters: `*.drizzle.ts`
|
|
43
43
|
- Routers: `*.router.ts`
|
|
44
44
|
- Use cases: verb-noun (e.g., `create-item.ts`, `list-items.ts`)
|
|
45
|
+
|
|
46
|
+
<!-- MCP_AGENT_MAIL_AND_BEADS_SNIPPET_START -->
|
|
47
|
+
|
|
48
|
+
## MCP Agent Mail: coordination for multi-agent workflows
|
|
49
|
+
|
|
50
|
+
What it is
|
|
51
|
+
|
|
52
|
+
- A mail-like layer that lets coding agents coordinate asynchronously via MCP tools and resources.
|
|
53
|
+
- Provides identities, inbox/outbox, searchable threads, and advisory file reservations, with human-auditable artifacts in Git.
|
|
54
|
+
|
|
55
|
+
Why it's useful
|
|
56
|
+
|
|
57
|
+
- Prevents agents from stepping on each other with explicit file reservations (leases) for files/globs.
|
|
58
|
+
- Keeps communication out of your token budget by storing messages in a per-project archive.
|
|
59
|
+
- Offers quick reads (`resource://inbox/...`, `resource://thread/...`) and macros that bundle common flows.
|
|
60
|
+
|
|
61
|
+
How to use effectively
|
|
62
|
+
|
|
63
|
+
1. Same repository
|
|
64
|
+
- Register an identity: call `ensure_project`, then `register_agent` using this repo's absolute path as `project_key`.
|
|
65
|
+
- Reserve files before you edit: `file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true)` to signal intent and avoid conflict.
|
|
66
|
+
- Communicate with threads: use `send_message(..., thread_id="FEAT-123")`; check inbox with `fetch_inbox` and acknowledge with `acknowledge_message`.
|
|
67
|
+
- Read fast: `resource://inbox/{Agent}?project=<abs-path>&limit=20` or `resource://thread/{id}?project=<abs-path>&include_bodies=true`.
|
|
68
|
+
- Tip: set `AGENT_NAME` in your environment so the pre-commit guard can block commits that conflict with others' active exclusive file reservations.
|
|
69
|
+
|
|
70
|
+
2. Across different repos in one project (e.g., Next.js frontend + FastAPI backend)
|
|
71
|
+
- Option A (single project bus): register both sides under the same `project_key` (shared key/path). Keep reservation patterns specific (e.g., `frontend/**` vs `backend/**`).
|
|
72
|
+
- Option B (separate projects): each repo has its own `project_key`; use `macro_contact_handshake` or `request_contact`/`respond_contact` to link agents, then message directly. Keep a shared `thread_id` (e.g., ticket key) across repos for clean summaries/audits.
|
|
73
|
+
|
|
74
|
+
Macros vs granular tools
|
|
75
|
+
|
|
76
|
+
- Prefer macros when you want speed or are on a smaller model: `macro_start_session`, `macro_prepare_thread`, `macro_file_reservation_cycle`, `macro_contact_handshake`.
|
|
77
|
+
- Use granular tools when you need control: `register_agent`, `file_reservation_paths`, `send_message`, `fetch_inbox`, `acknowledge_message`.
|
|
78
|
+
|
|
79
|
+
Common pitfalls
|
|
80
|
+
|
|
81
|
+
- "from_agent not registered": always `register_agent` in the correct `project_key` first.
|
|
82
|
+
- "FILE_RESERVATION_CONFLICT": adjust patterns, wait for expiry, or use a non-exclusive reservation when appropriate.
|
|
83
|
+
- Auth errors: if JWT+JWKS is enabled, include a bearer token with a `kid` that matches server JWKS; static bearer is used only when JWT is disabled.
|
|
84
|
+
|
|
85
|
+
## Integrating with Beads (dependency-aware task planning)
|
|
86
|
+
|
|
87
|
+
Beads provides a lightweight, dependency-aware issue database and a CLI (`bd`) for selecting "ready work," setting priorities, and tracking status. It complements MCP Agent Mail's messaging, audit trail, and file-reservation signals. Project: [steveyegge/beads](https://github.com/steveyegge/beads)
|
|
88
|
+
|
|
89
|
+
Recommended conventions
|
|
90
|
+
|
|
91
|
+
- **Single source of truth**: Use **Beads** for task status/priority/dependencies; use **Agent Mail** for conversation, decisions, and attachments (audit).
|
|
92
|
+
- **Shared identifiers**: Use the Beads issue id (e.g., `bd-123`) as the Mail `thread_id` and prefix message subjects with `[bd-123]`.
|
|
93
|
+
- **Reservations**: When starting a `bd-###` task, call `file_reservation_paths(...)` for the affected paths; include the issue id in the `reason` and release on completion.
|
|
94
|
+
|
|
95
|
+
Typical flow (agents)
|
|
96
|
+
|
|
97
|
+
1. **Pick ready work** (Beads)
|
|
98
|
+
- `bd ready --json` → choose one item (highest priority, no blockers)
|
|
99
|
+
2. **Reserve edit surface** (Mail)
|
|
100
|
+
- `file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true, reason="bd-123")`
|
|
101
|
+
3. **Announce start** (Mail)
|
|
102
|
+
- `send_message(..., thread_id="bd-123", subject="[bd-123] Start: <short title>", ack_required=true)`
|
|
103
|
+
4. **Work and update**
|
|
104
|
+
- Reply in-thread with progress and attach artifacts/images; keep the discussion in one thread per issue id
|
|
105
|
+
5. **Complete and release**
|
|
106
|
+
- `bd close bd-123 --reason "Completed"` (Beads is status authority)
|
|
107
|
+
- `release_file_reservations(project_key, agent_name, paths=["src/**"])`
|
|
108
|
+
- Final Mail reply: `[bd-123] Completed` with summary and links
|
|
109
|
+
|
|
110
|
+
Mapping cheat-sheet
|
|
111
|
+
|
|
112
|
+
- **Mail `thread_id`** ↔ `bd-###`
|
|
113
|
+
- **Mail subject**: `[bd-###] …`
|
|
114
|
+
- **File reservation `reason`**: `bd-###`
|
|
115
|
+
- **Commit messages (optional)**: include `bd-###` for traceability
|
|
116
|
+
|
|
117
|
+
Event mirroring (optional automation)
|
|
118
|
+
|
|
119
|
+
- On `bd update --status blocked`, send a high-importance Mail message in thread `bd-###` describing the blocker.
|
|
120
|
+
- On Mail "ACK overdue" for a critical decision, add a Beads label (e.g., `needs-ack`) or bump priority to surface it in `bd ready`.
|
|
121
|
+
|
|
122
|
+
Pitfalls to avoid
|
|
123
|
+
|
|
124
|
+
- Don't create or manage tasks in Mail; treat Beads as the single task queue.
|
|
125
|
+
- Always include `bd-###` in message `thread_id` to avoid ID drift across tools.
|
|
126
|
+
|
|
127
|
+
<!-- MCP_AGENT_MAIL_AND_BEADS_SNIPPET_END -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
name: file-beads
|
|
3
|
+
description: File detailed Beads epics and issues from a plan. Use when converting plans, roadmaps, or feature specs into structured Beads issues with proper dependencies.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# File Beads Epics and Issues from Plan
|
|
@@ -9,7 +9,7 @@ You are tasked with converting a plan into a comprehensive set of Beads epics an
|
|
|
9
9
|
|
|
10
10
|
## Step 1: Understand the Plan
|
|
11
11
|
|
|
12
|
-
First, review the plan context provided
|
|
12
|
+
First, review the plan context provided by the user.
|
|
13
13
|
|
|
14
14
|
If no specific plan is provided, ask the user to share the plan or point to a planning document (check `history/` directory for recent plans).
|
|
15
15
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
name: review-beads
|
|
3
|
+
description: Review, proofread, and refine filed Beads epics and issues. Use when you need to polish issues for clarity, fix dependencies, or ensure workers have a smooth implementation experience.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Review and Refine Beads Issues
|
|
@@ -16,7 +16,7 @@ bd list --json
|
|
|
16
16
|
bd ready --json
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
If specific IDs were provided
|
|
19
|
+
If specific IDs were provided, focus on those. Otherwise, review all issues.
|
|
20
20
|
|
|
21
21
|
## Step 2: Systematic Review Checklist
|
|
22
22
|
|
package/templates/base/AGENTS.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# AGENTS.md - **APP_NAME**
|
|
2
2
|
|
|
3
|
+
> **Main Documentation**: [Kuckit SDK](https://github.com/draphonix/kuckit)
|
|
4
|
+
|
|
3
5
|
## Quick Start
|
|
4
6
|
|
|
5
7
|
```bash
|
|
@@ -26,6 +28,90 @@ __APP_NAME_KEBAB__/
|
|
|
26
28
|
|
|
27
29
|
**Note:** Core packages (`api`, `auth`, `db`, `domain`, `contracts`) are installed from npm as `@kuckit/*` dependencies, not copied into your project.
|
|
28
30
|
|
|
31
|
+
## Bootstrap Architecture
|
|
32
|
+
|
|
33
|
+
Kuckit uses **bootstrap packages** to minimize boilerplate in your apps:
|
|
34
|
+
|
|
35
|
+
### Server Bootstrap (`@kuckit/app-server`)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// apps/server/src/server.ts
|
|
39
|
+
import 'dotenv/config'
|
|
40
|
+
import { runKuckitServer } from '@kuckit/app-server'
|
|
41
|
+
import { loadConfig } from './config/server'
|
|
42
|
+
import { getModuleSpecs } from './config/modules'
|
|
43
|
+
|
|
44
|
+
runKuckitServer({
|
|
45
|
+
loadConfig,
|
|
46
|
+
getModuleSpecs,
|
|
47
|
+
}).catch((error) => {
|
|
48
|
+
console.error('Failed to start server:', error)
|
|
49
|
+
process.exit(1)
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**What `runKuckitServer` handles automatically:**
|
|
54
|
+
|
|
55
|
+
- Express app creation with CORS, JSON parsing
|
|
56
|
+
- DI container setup with per-request scoping
|
|
57
|
+
- Module loading and API route wiring
|
|
58
|
+
- Auth, health, metrics endpoints
|
|
59
|
+
- Graceful shutdown
|
|
60
|
+
|
|
61
|
+
**Customization via hooks:**
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
runKuckitServer({
|
|
65
|
+
loadConfig,
|
|
66
|
+
getModuleSpecs,
|
|
67
|
+
hooks: {
|
|
68
|
+
onAppCreated: (app) => app.use(helmet()),
|
|
69
|
+
onContainerReady: (container) => {
|
|
70
|
+
/* warm caches */
|
|
71
|
+
},
|
|
72
|
+
onServerReady: (port) => console.log(`Listening on ${port}`),
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Web Bootstrap (`@kuckit/app-web`)
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// apps/web/src/main.tsx
|
|
81
|
+
import { createKuckitWebProvider } from '@kuckit/app-web'
|
|
82
|
+
import { createAuthClient } from 'better-auth/react'
|
|
83
|
+
import config from '../kuckit.config'
|
|
84
|
+
|
|
85
|
+
const authClient = createAuthClient({
|
|
86
|
+
baseURL: import.meta.env.VITE_API_URL,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const KuckitProvider = createKuckitWebProvider({
|
|
90
|
+
config,
|
|
91
|
+
authClient,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
95
|
+
<StrictMode>
|
|
96
|
+
<KuckitProvider>
|
|
97
|
+
<App />
|
|
98
|
+
</KuckitProvider>
|
|
99
|
+
</StrictMode>
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Access registries and auth via hooks:**
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { useKuckitWeb, useAuth } from '@kuckit/app-web'
|
|
107
|
+
|
|
108
|
+
function MyComponent() {
|
|
109
|
+
const { routeRegistry, navRegistry, slotRegistry } = useKuckitWeb()
|
|
110
|
+
const authClient = useAuth()
|
|
111
|
+
const { data: session } = authClient.useSession()
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
29
115
|
## Module Development
|
|
30
116
|
|
|
31
117
|
Kuckit uses a **module-based architecture** where each module contains its own Clean Architecture layers internally.
|
|
@@ -8,111 +8,68 @@ Express backend hosting oRPC API, Better-Auth authentication, and the Kuckit mod
|
|
|
8
8
|
|
|
9
9
|
## Key Files
|
|
10
10
|
|
|
11
|
-
| File
|
|
12
|
-
|
|
|
13
|
-
| `server.ts`
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `rpc.ts` | oRPC handler setup |
|
|
17
|
-
| `rpc-router-registry.ts` | Mutable router for modules |
|
|
18
|
-
| `rest-router-registry.ts` | REST router collection |
|
|
19
|
-
| `module-rest-routes.ts` | REST router wiring |
|
|
20
|
-
| `auth.ts` | Better-Auth configuration |
|
|
11
|
+
| File | Purpose |
|
|
12
|
+
| ------------ | ------------------------------------------------- |
|
|
13
|
+
| `server.ts` | Entry point (9 lines) |
|
|
14
|
+
| `config.ts` | Server configuration |
|
|
15
|
+
| `modules.ts` | Module registration (reads from kuckit.config.ts) |
|
|
21
16
|
|
|
22
|
-
##
|
|
17
|
+
## How It Works
|
|
23
18
|
|
|
24
|
-
The server
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
1. createKuckitContainer() - Core services (db, logger, cache, etc.)
|
|
28
|
-
2. loadKuckitModules() with onApiRegistrations callback:
|
|
29
|
-
├─► register() hooks run (DI bindings)
|
|
30
|
-
├─► registerApi() hooks run (routers collected)
|
|
31
|
-
├─► onApiRegistrations() - Wire RPC and REST routers
|
|
32
|
-
└─► onBootstrap() hooks run (startup logic)
|
|
33
|
-
3. setupRPC() - Create RPCHandler AFTER modules loaded
|
|
34
|
-
4. setupModuleRestRouters() - Mount REST routers to Express
|
|
35
|
-
5. Express listens
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Router Registry Pattern
|
|
39
|
-
|
|
40
|
-
oRPC's `RPCHandler` captures the router at construction time. Modules wire their routers into a **mutable object** before the handler is created:
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
// rpc-router-registry.ts
|
|
44
|
-
export const rootRpcRouter = { ...appRouter }
|
|
45
|
-
|
|
46
|
-
export const wireModuleRpcRouters = (registrations: ApiRegistration[]) => {
|
|
47
|
-
for (const reg of registrations) {
|
|
48
|
-
if (reg.type === 'rpc-router') {
|
|
49
|
-
rootRpcRouter[reg.name] = reg.router
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
```
|
|
19
|
+
The server uses `@kuckit/app-server` which handles all the complexity internally:
|
|
54
20
|
|
|
55
21
|
```typescript
|
|
56
22
|
// server.ts
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
wireModuleRpcRouters(registrations) // Wire BEFORE setupRPC()
|
|
62
|
-
collectModuleRestRouters(registrations) // Collect REST routers
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
setupRPC(app) // Now create handler with fully-wired router
|
|
67
|
-
setupModuleRestRouters(app) // Mount REST routers at /api/<name>
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## REST Router Pattern
|
|
23
|
+
import 'dotenv/config'
|
|
24
|
+
import { runKuckitServer } from '@kuckit/app-server'
|
|
25
|
+
import { loadConfig } from './config'
|
|
26
|
+
import { getModuleSpecs } from './modules'
|
|
71
27
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
// In your module's registerApi()
|
|
76
|
-
ctx.addApiRegistration({
|
|
77
|
-
type: 'rest-router',
|
|
78
|
-
name: 'ai',
|
|
79
|
-
router: createAiRouter(),
|
|
80
|
-
prefix: '/ai', // Optional, defaults to /<name>
|
|
81
|
-
})
|
|
28
|
+
runKuckitServer({ loadConfig, getModuleSpecs }).catch(console.error)
|
|
82
29
|
```
|
|
83
30
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- Per-request scoped container via `req.scope`
|
|
87
|
-
- Session via `req.scope.cradle.session`
|
|
88
|
-
- Full Express `Request` and `Response` objects
|
|
31
|
+
The `@kuckit/app-server` package handles:
|
|
89
32
|
|
|
90
|
-
|
|
33
|
+
- DI container creation and setup
|
|
34
|
+
- Module loading with proper lifecycle hooks
|
|
35
|
+
- RPC router wiring (oRPC)
|
|
36
|
+
- REST router mounting
|
|
37
|
+
- Authentication (Better-Auth)
|
|
38
|
+
- Health endpoints
|
|
39
|
+
- Graceful shutdown
|
|
91
40
|
|
|
92
|
-
|
|
41
|
+
## Adding New Modules
|
|
93
42
|
|
|
94
|
-
|
|
95
|
-
- `requestLogger` - Logger with request context
|
|
96
|
-
- `session` - Current user session (if authenticated)
|
|
43
|
+
**`kuckit.config.ts` is the single source of truth for modules.**
|
|
97
44
|
|
|
98
|
-
|
|
45
|
+
1. Add to `kuckit.config.ts` (at project root):
|
|
99
46
|
|
|
100
|
-
|
|
47
|
+
```typescript
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
modules: [
|
|
50
|
+
{ package: '@__APP_NAME_KEBAB__/items-module' },
|
|
51
|
+
{ package: '@acme/billing-module' }, // Add new module here
|
|
52
|
+
],
|
|
53
|
+
})
|
|
54
|
+
```
|
|
101
55
|
|
|
102
|
-
|
|
103
|
-
2. Add to `config/modules.ts`:
|
|
56
|
+
2. Add import and mapping to `modules.ts`:
|
|
104
57
|
|
|
105
58
|
```typescript
|
|
106
|
-
import { kuckitModule as
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
59
|
+
import { kuckitModule as billingModule } from '@acme/billing-module'
|
|
60
|
+
|
|
61
|
+
const KNOWN_MODULES = {
|
|
62
|
+
// ...existing
|
|
63
|
+
'@acme/billing-module': {
|
|
64
|
+
module: billingModule,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
112
67
|
```
|
|
113
68
|
|
|
114
69
|
3. Restart the server
|
|
115
70
|
|
|
71
|
+
The CLI command `bunx kuckit add @acme/billing-module` automates both steps.
|
|
72
|
+
|
|
116
73
|
## Environment Variables
|
|
117
74
|
|
|
118
75
|
See `.env.example` in this directory.
|
|
@@ -8,24 +8,12 @@
|
|
|
8
8
|
"dev": "bun run --hot src/server.ts"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"express": "^5.1.0",
|
|
12
|
-
"cors": "^2.8.5",
|
|
13
11
|
"dotenv": "^17.0.0",
|
|
14
|
-
"
|
|
15
|
-
"pg": "^8.14.1",
|
|
16
|
-
"@orpc/server": "^1.10.0",
|
|
17
|
-
"@orpc/zod": "^1.10.0",
|
|
18
|
-
"better-auth": "^1.3.0",
|
|
19
|
-
"zod": "^3.23.0",
|
|
12
|
+
"@kuckit/app-server": "^2.0.0",
|
|
20
13
|
"@kuckit/sdk": "^2.0.0",
|
|
21
|
-
"@kuckit/api": "^2.0.0",
|
|
22
|
-
"@kuckit/auth": "^2.0.0",
|
|
23
14
|
"@kuckit/db": "^2.0.0"
|
|
24
15
|
},
|
|
25
16
|
"devDependencies": {
|
|
26
|
-
"@types/express": "^5.0.1",
|
|
27
|
-
"@types/cors": "^2.8.17",
|
|
28
|
-
"@types/pg": "^8.11.11",
|
|
29
17
|
"typescript": "^5.8.2",
|
|
30
18
|
"tsdown": "^0.15.5"
|
|
31
19
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ensureDatabaseUrl } from '@kuckit/db/connection'
|
|
2
|
+
import type { KuckitServerConfig } from '@kuckit/app-server'
|
|
3
|
+
|
|
4
|
+
export const loadConfig = (): KuckitServerConfig => ({
|
|
5
|
+
databaseUrl: ensureDatabaseUrl(),
|
|
6
|
+
enableFileLogging: process.env.ENABLE_FILE_LOGGING === 'true',
|
|
7
|
+
logDir: process.env.LOG_DIR || './logs',
|
|
8
|
+
logLevel: (process.env.LOG_LEVEL || 'INFO') as 'DEBUG' | 'INFO' | 'WARN' | 'ERROR',
|
|
9
|
+
env: process.env.NODE_ENV || 'development',
|
|
10
|
+
port: parseInt(process.env.PORT || '3000', 10),
|
|
11
|
+
corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:3001',
|
|
12
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ModuleSpec, KuckitConfig } from '@kuckit/sdk'
|
|
2
|
+
import { tryLoadKuckitConfig } from '@kuckit/sdk'
|
|
3
|
+
import { kuckitModule as itemsModule } from '@__APP_NAME_KEBAB__/items-module'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Known modules mapping: package name → direct module import
|
|
7
|
+
*
|
|
8
|
+
* This enables config-driven module loading while keeping direct imports
|
|
9
|
+
* for workspace packages (required for monorepo compatibility).
|
|
10
|
+
*
|
|
11
|
+
* When adding a new module:
|
|
12
|
+
* 1. Add to kuckit.config.ts (source of truth)
|
|
13
|
+
* 2. Add import and mapping here
|
|
14
|
+
*/
|
|
15
|
+
const KNOWN_MODULES: Record<string, { module: unknown; defaultConfig?: unknown }> = {
|
|
16
|
+
// KUCKIT_KNOWN_MODULES_START
|
|
17
|
+
'@__APP_NAME_KEBAB__/items-module': {
|
|
18
|
+
module: itemsModule,
|
|
19
|
+
},
|
|
20
|
+
// KUCKIT_KNOWN_MODULES_END
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert unified config to ModuleSpec array.
|
|
25
|
+
* For known modules, uses direct imports. For npm packages, uses package-based loading.
|
|
26
|
+
*/
|
|
27
|
+
function configToModuleSpecs(config: KuckitConfig): ModuleSpec[] {
|
|
28
|
+
return config.modules
|
|
29
|
+
.filter((m) => m.enabled !== false)
|
|
30
|
+
.map((m) => {
|
|
31
|
+
const known = KNOWN_MODULES[m.package]
|
|
32
|
+
if (known) {
|
|
33
|
+
return {
|
|
34
|
+
module: known.module,
|
|
35
|
+
config: m.config ?? known.defaultConfig,
|
|
36
|
+
} as ModuleSpec
|
|
37
|
+
}
|
|
38
|
+
// For npm packages, use package-based loading
|
|
39
|
+
return {
|
|
40
|
+
package: m.package,
|
|
41
|
+
config: m.config,
|
|
42
|
+
} as ModuleSpec
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fallback module specs (used when kuckit.config.ts is not found)
|
|
48
|
+
*/
|
|
49
|
+
const getFallbackModuleSpecs = (): ModuleSpec[] => [{ module: itemsModule }]
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get module specs from kuckit.config.ts (single source of truth).
|
|
53
|
+
* Falls back to direct module specs if config is not found.
|
|
54
|
+
*/
|
|
55
|
+
export const getModuleSpecs = async (): Promise<ModuleSpec[]> => {
|
|
56
|
+
const config = await tryLoadKuckitConfig()
|
|
57
|
+
|
|
58
|
+
if (config) {
|
|
59
|
+
console.log(`[kuckit] Loading modules from: ${config._configPath}`)
|
|
60
|
+
return configToModuleSpecs(config)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Fallback for legacy projects without kuckit.config.ts
|
|
64
|
+
console.log('[kuckit] No config found, using fallback module specs')
|
|
65
|
+
return getFallbackModuleSpecs()
|
|
66
|
+
}
|
|
@@ -1,51 +1,9 @@
|
|
|
1
1
|
import 'dotenv/config'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { setupRPC } from './rpc'
|
|
6
|
-
import { setupModuleRestRouters } from './module-rest-routes'
|
|
7
|
-
import { setupHealth } from './health'
|
|
8
|
-
import { disposeContainer } from './container'
|
|
2
|
+
import { runKuckitServer } from '@kuckit/app-server'
|
|
3
|
+
import { loadConfig } from './config'
|
|
4
|
+
import { getModuleSpecs } from './modules'
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
* Bootstrap and start server
|
|
12
|
-
*/
|
|
13
|
-
const bootstrap = async () => {
|
|
14
|
-
const app = createApp()
|
|
15
|
-
|
|
16
|
-
await setupContainerMiddleware(app)
|
|
17
|
-
const rootContainer = getRootContainer()
|
|
18
|
-
|
|
19
|
-
setupAuth(app)
|
|
20
|
-
setupRPC(app)
|
|
21
|
-
setupModuleRestRouters(app)
|
|
22
|
-
setupHealth(app, rootContainer)
|
|
23
|
-
|
|
24
|
-
const port = process.env.PORT || 3000
|
|
25
|
-
const server = app.listen(port, () => {
|
|
26
|
-
console.log(`Server is running on port ${port}`)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
const shutdown = async () => {
|
|
30
|
-
console.log('Shutting down gracefully...')
|
|
31
|
-
|
|
32
|
-
server.close(async () => {
|
|
33
|
-
await disposeContainer(rootContainer)
|
|
34
|
-
console.log('Server closed')
|
|
35
|
-
process.exit(0)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
setTimeout(() => {
|
|
39
|
-
console.error('Forced shutdown')
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}, 10000)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
process.on('SIGTERM', shutdown)
|
|
45
|
-
process.on('SIGINT', shutdown)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
bootstrap().catch((error) => {
|
|
6
|
+
runKuckitServer({ loadConfig, getModuleSpecs }).catch((error) => {
|
|
49
7
|
console.error('Failed to start server:', error)
|
|
50
8
|
process.exit(1)
|
|
51
9
|
})
|