create-kuckit-app 0.4.0 → 0.4.1

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.
Files changed (41) hide show
  1. package/dist/bin.js +1 -1
  2. package/dist/{create-project-geQBZ0Ru.js → create-project-CAsuZMK5.js} +2 -1
  3. package/dist/index.js +1 -1
  4. package/package.json +1 -1
  5. package/templates/base/.claude/CLAUDE.md +83 -0
  6. package/templates/base/AGENTS.md +86 -0
  7. package/templates/base/apps/server/AGENTS.md +42 -85
  8. package/templates/base/apps/server/package.json +2 -14
  9. package/templates/base/apps/server/src/config.ts +12 -0
  10. package/templates/base/apps/server/src/modules.ts +66 -0
  11. package/templates/base/apps/server/src/server.ts +4 -46
  12. package/templates/base/apps/web/AGENTS.md +63 -85
  13. package/templates/base/apps/web/package.json +2 -6
  14. package/templates/base/apps/web/src/components/KuckitModuleRoute.tsx +29 -7
  15. package/templates/base/apps/web/src/components/dashboard/dashboard-overview.tsx +2 -2
  16. package/templates/base/apps/web/src/components/dashboard/nav-user.tsx +2 -2
  17. package/templates/base/apps/web/src/main.tsx +12 -22
  18. package/templates/base/apps/web/src/modules.client.ts +43 -9
  19. package/templates/base/apps/web/src/routes/__root.tsx +1 -1
  20. package/templates/base/apps/web/src/routes/dashboard.tsx +1 -1
  21. package/templates/base/apps/web/src/routes/index.tsx +2 -2
  22. package/templates/base/apps/web/src/routes/login.tsx +2 -2
  23. package/templates/base/drizzle.config.ts +2 -6
  24. package/templates/base/kuckit.config.ts +30 -0
  25. package/templates/base/packages/items-module/AGENTS.md +83 -0
  26. package/templates/base/apps/server/src/app.ts +0 -20
  27. package/templates/base/apps/server/src/auth.ts +0 -10
  28. package/templates/base/apps/server/src/config/modules.ts +0 -21
  29. package/templates/base/apps/server/src/container.ts +0 -83
  30. package/templates/base/apps/server/src/health.ts +0 -27
  31. package/templates/base/apps/server/src/middleware/container.ts +0 -41
  32. package/templates/base/apps/server/src/module-rest-routes.ts +0 -47
  33. package/templates/base/apps/server/src/rest-router-registry.ts +0 -32
  34. package/templates/base/apps/server/src/rpc-router-registry.ts +0 -26
  35. package/templates/base/apps/server/src/rpc.ts +0 -31
  36. package/templates/base/apps/web/src/providers/KuckitProvider.tsx +0 -123
  37. package/templates/base/apps/web/src/providers/ServicesProvider.tsx +0 -47
  38. package/templates/base/apps/web/src/services/auth-client.ts +0 -12
  39. package/templates/base/apps/web/src/services/index.ts +0 -3
  40. package/templates/base/apps/web/src/services/rpc.ts +0 -29
  41. package/templates/base/apps/web/src/services/types.ts +0 -14
@@ -10,118 +10,96 @@ React frontend using TanStack Router, TanStack Query, and Kuckit client modules.
10
10
 
11
11
  | File | Purpose |
12
12
  | ---------------------------------- | --------------------------------- |
13
- | `main.tsx` | App entry point |
13
+ | `main.tsx` | App entry point (17 lines) |
14
14
  | `modules.client.ts` | Client module registration |
15
- | `providers/KuckitProvider.tsx` | Kuckit context setup |
16
- | `providers/ServicesProvider.tsx` | Services context (RPC, auth) |
17
15
  | `routes/` | TanStack Router file-based routes |
18
16
  | `routes/$.tsx` | Catch-all for module routes |
19
17
  | `components/KuckitModuleRoute.tsx` | Renders module-registered routes |
20
18
 
21
- ## Adding Routes
22
-
23
- Create files in `src/routes/`:
24
-
25
- - `src/routes/about.tsx` → `/about`
26
- - `src/routes/users/$id.tsx` → `/users/:id`
27
- - `src/routes/settings/index.tsx` → `/settings`
28
-
29
- **File-based routes take precedence** over module-registered routes.
19
+ ## How It Works
30
20
 
31
- ## Module Route Integration
32
-
33
- Modules register routes via `defineKuckitClientModule`. These are rendered via a catch-all pattern:
34
-
35
- 1. Module registers route in `routes` array
36
- 2. `RouteRegistry` collects routes during `loadKuckitClientModules()`
37
- 3. Catch-all route (`$.tsx`) matches any unhandled path
38
- 4. `KuckitModuleRoute` looks up path in registry and renders the component
21
+ The web app uses `@kuckit/app-web` which provides a provider factory:
39
22
 
40
23
  ```tsx
41
- // routes/$.tsx - Catches module routes
42
- import { KuckitModuleRoute } from '@/components/KuckitModuleRoute'
43
-
44
- export function Route() {
45
- return <KuckitModuleRoute />
46
- }
24
+ // main.tsx
25
+ import ReactDOM from 'react-dom/client'
26
+ import { createKuckitWebProvider } from '@kuckit/app-web'
27
+ import { routeTree } from './routeTree.gen'
28
+ import { Route as rootRoute } from './routes/__root'
29
+ import { getClientModuleSpecs } from './modules.client'
30
+ import './index.css'
31
+
32
+ const KuckitApp = createKuckitWebProvider({
33
+ serverUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000',
34
+ modules: getClientModuleSpecs(),
35
+ env: import.meta.env.MODE,
36
+ routeTree,
37
+ rootRoute,
38
+ })
39
+
40
+ const rootEl = document.getElementById('app')!
41
+ ReactDOM.createRoot(rootEl).render(<KuckitApp />)
47
42
  ```
48
43
 
49
- ## Context Providers
50
-
51
- The app wraps content in Kuckit providers:
44
+ The `@kuckit/app-web` package handles:
52
45
 
53
- ```tsx
54
- <ServicesProvider>
55
- {' '}
56
- {/* RPC client, auth */}
57
- <KuckitProvider>
58
- {' '}
59
- {/* Nav, slots, routes registries */}
60
- <KuckitRpcProvider>
61
- {' '}
62
- {/* RPC context for modules */}
63
- {children}
64
- </KuckitRpcProvider>
65
- </KuckitProvider>
66
- </ServicesProvider>
67
- ```
46
+ - RPC client and oRPC utils creation
47
+ - React Query client setup
48
+ - Better-Auth client integration
49
+ - Module loading and route registration
50
+ - Navigation and slot registries
51
+ - Provider composition
68
52
 
69
- **Available hooks:**
53
+ ## Available Hooks
70
54
 
71
- - `useRpc<T>()` - Get typed RPC client for API calls
72
- - `useNavItems()` - Flat navigation items list
73
- - `useNavTree()` - Hierarchical navigation tree
74
- - `useSlot(name)` - Get components for a slot
75
- - `useHasSlot(name)` - Check if slot has content
55
+ Import from `@kuckit/app-web`:
76
56
 
77
- ## useRpc Hook
57
+ - `useAuth()` - Auth client for sign in/out, session
58
+ - `useRpc()` - Typed RPC client for API calls
59
+ - `useOrpc()` - oRPC TanStack Query utils
60
+ - `useQueryClient()` - React Query client
61
+ - `useKuckitWeb()` - Full context with registries
78
62
 
79
- Module components must use `useRpc()` to access the API:
80
-
81
- ```tsx
82
- import { useRpc } from '@kuckit/sdk-react'
83
- import { useQuery } from '@tanstack/react-query'
84
-
85
- interface MyRpc {
86
- items: { list: (input: {}) => Promise<Item[]> }
87
- }
63
+ ## Adding Routes
88
64
 
89
- function MyComponent() {
90
- const rpc = useRpc<MyRpc>()
65
+ Create files in `src/routes/`:
91
66
 
92
- const { data } = useQuery({
93
- queryKey: ['items'],
94
- queryFn: () => rpc.items.list({}),
95
- })
96
- }
97
- ```
67
+ - `src/routes/about.tsx` `/about`
68
+ - `src/routes/users/$id.tsx` → `/users/:id`
69
+ - `src/routes/settings/index.tsx` `/settings`
98
70
 
99
- **Important**: Never use `import.meta.env.VITE_SERVER_URL` directly in module components - it's unavailable in external packages. The `useRpc` hook provides the correctly-configured client.
71
+ **File-based routes take precedence** over module-registered routes.
100
72
 
101
- ## Using Module UI Components
73
+ ## Adding Client Modules
102
74
 
103
- ```tsx
104
- import { ItemsPage } from '@__APP_NAME_KEBAB__/items-module/ui'
75
+ **`kuckit.config.ts` is the single source of truth for modules.**
105
76
 
106
- // In your route component
107
- export function Route() {
108
- return <ItemsPage />
109
- }
110
- ```
77
+ 1. Add to `kuckit.config.ts` (at project root):
111
78
 
112
- ## Adding Client Modules
79
+ ```typescript
80
+ export default defineConfig({
81
+ modules: [
82
+ { package: '@__APP_NAME_KEBAB__/items-module' },
83
+ { package: '@acme/billing-module' }, // Add new module here
84
+ ],
85
+ })
86
+ ```
113
87
 
114
- 1. Import in `modules.client.ts`:
88
+ 2. Add import and mapping to `modules.client.ts`:
115
89
 
116
90
  ```typescript
117
- import { kuckitClientModule as myClientModule } from '@__APP_NAME_KEBAB__/my-module/client'
118
-
119
- export const clientModules = [
120
- itemsClientModule,
121
- myClientModule, // Add here
122
- ]
91
+ import { kuckitClientModule as billingClientModule } from '@acme/billing-module/client'
92
+
93
+ const KNOWN_CLIENT_MODULES = {
94
+ // ...existing
95
+ '@acme/billing-module': {
96
+ module: billingClientModule,
97
+ },
98
+ }
123
99
  ```
124
100
 
101
+ The CLI command `bunx kuckit add @acme/billing-module` automates both steps.
102
+
125
103
  ## Environment Variables
126
104
 
127
105
  See `.env.example` in this directory.
@@ -9,10 +9,8 @@
9
9
  "check-types": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
+ "@kuckit/app-web": "^2.0.0",
12
13
  "@kuckit/sdk-react": "^2.0.0",
13
- "@kuckit/auth": "^2.0.0",
14
- "@orpc/client": "^1.10.0",
15
- "@orpc/tanstack-query": "^1.10.0",
16
14
  "@radix-ui/react-avatar": "^1.1.10",
17
15
  "@radix-ui/react-collapsible": "^1.1.11",
18
16
  "@radix-ui/react-dialog": "^1.1.14",
@@ -22,14 +20,12 @@
22
20
  "@radix-ui/react-tooltip": "^1.2.7",
23
21
  "@tanstack/react-query": "^5.0.0",
24
22
  "@tanstack/react-router": "^1.114.0",
25
- "better-auth": "^1.3.0",
26
23
  "class-variance-authority": "^0.7.1",
27
24
  "clsx": "^2.1.1",
28
25
  "lucide-react": "^0.511.0",
29
26
  "react": "^19.0.0",
30
27
  "react-dom": "^19.0.0",
31
- "tailwind-merge": "^3.0.0",
32
- "zod": "^3.23.0"
28
+ "tailwind-merge": "^3.0.0"
33
29
  },
34
30
  "devDependencies": {
35
31
  "@tailwindcss/vite": "^4.0.15",
@@ -1,6 +1,5 @@
1
1
  import { useRouterState, Link, useNavigate } from '@tanstack/react-router'
2
- import { useKuckit } from '@/providers/KuckitProvider'
3
- import { useServices } from '@/providers/ServicesProvider'
2
+ import { useKuckitWeb, useAuth } from '@kuckit/app-web'
4
3
  import { useEffect, useState } from 'react'
5
4
 
6
5
  /**
@@ -24,8 +23,8 @@ function isRootLevelRoute(path: string): boolean {
24
23
  * For root routes with meta.requiresAuth: true, redirects to /login if not authenticated.
25
24
  */
26
25
  export function KuckitModuleRoute() {
27
- const { routeRegistry } = useKuckit()
28
- const { authClient } = useServices()
26
+ const { routeRegistry } = useKuckitWeb()
27
+ const authClient = useAuth()
29
28
  const { location } = useRouterState()
30
29
  const navigate = useNavigate()
31
30
  const pathname = location.pathname
@@ -47,11 +46,34 @@ export function KuckitModuleRoute() {
47
46
  // For dashboard context, also try matching with /dashboard prefix stripped
48
47
  const modulePathname = isDashboardContext ? pathname.replace('/dashboard', '') || '/' : pathname
49
48
 
50
- // Find matching route
51
- let routeDef = contextRoutes.find((r) => r.path === pathname)
49
+ // Match route path against pathname, supporting catch-all patterns like /docs/$
50
+ function matchRoute(routePath: string, pathname: string): boolean {
51
+ // Exact match
52
+ if (routePath === pathname) return true
53
+
54
+ // Catch-all pattern: /docs/$ matches /docs/anything/here
55
+ if (routePath.endsWith('/$')) {
56
+ const base = routePath.slice(0, -1) // Remove trailing $
57
+ return pathname.startsWith(base)
58
+ }
59
+
60
+ return false
61
+ }
62
+
63
+ // Find matching route - prefer exact matches over catch-all
64
+ const exactMatch = contextRoutes.find((r) => r.path === pathname)
65
+ const catchAllMatch = contextRoutes.find(
66
+ (r) => r.path.endsWith('/$') && matchRoute(r.path, pathname)
67
+ )
68
+ let routeDef = exactMatch || catchAllMatch
69
+
52
70
  if (!routeDef && isDashboardContext) {
53
71
  // For dashboard routes, also try matching stripped path
54
- routeDef = contextRoutes.find((r) => r.path === modulePathname)
72
+ const exactDash = contextRoutes.find((r) => r.path === modulePathname)
73
+ const catchAllDash = contextRoutes.find(
74
+ (r) => r.path.endsWith('/$') && matchRoute(r.path, modulePathname)
75
+ )
76
+ routeDef = exactDash || catchAllDash
55
77
  }
56
78
 
57
79
  // Check auth for root routes with requiresAuth
@@ -1,7 +1,7 @@
1
- import { useServices } from '@/providers/ServicesProvider'
1
+ import { useAuth } from '@kuckit/app-web'
2
2
 
3
3
  export function DashboardOverview() {
4
- const { authClient } = useServices()
4
+ const authClient = useAuth()
5
5
  const { data: session } = authClient.useSession()
6
6
 
7
7
  return (
@@ -1,6 +1,6 @@
1
1
  import { ChevronsUpDown, LogOut, User } from 'lucide-react'
2
2
  import { useNavigate } from '@tanstack/react-router'
3
- import { useServices } from '@/providers/ServicesProvider'
3
+ import { useAuth } from '@kuckit/app-web'
4
4
  import {
5
5
  DropdownMenu,
6
6
  DropdownMenuContent,
@@ -19,7 +19,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
19
19
  export function NavUser() {
20
20
  const { isMobile } = useSidebar()
21
21
  const navigate = useNavigate()
22
- const { authClient } = useServices()
22
+ const authClient = useAuth()
23
23
  const { data: session } = authClient.useSession()
24
24
 
25
25
  const user = session?.user
@@ -1,27 +1,17 @@
1
- import { createRouter } from '@tanstack/react-router'
2
1
  import ReactDOM from 'react-dom/client'
2
+ import { createKuckitWebProvider } from '@kuckit/app-web'
3
3
  import { routeTree } from './routeTree.gen'
4
- import { ServicesProvider } from './providers/ServicesProvider'
5
- import { KuckitProvider } from './providers/KuckitProvider'
4
+ import { Route as rootRoute } from './routes/__root'
5
+ import { getClientModuleSpecs } from './modules.client'
6
6
  import './index.css'
7
7
 
8
- declare module '@tanstack/react-router' {
9
- interface Register {
10
- router: ReturnType<typeof createRouter<typeof routeTree>>
11
- }
12
- }
8
+ const KuckitApp = createKuckitWebProvider({
9
+ serverUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000',
10
+ modules: getClientModuleSpecs(),
11
+ env: import.meta.env.MODE,
12
+ routeTree,
13
+ rootRoute,
14
+ })
13
15
 
14
- const rootElement = document.getElementById('app')
15
-
16
- if (!rootElement) {
17
- throw new Error('Root element not found')
18
- }
19
-
20
- if (!rootElement.innerHTML) {
21
- const root = ReactDOM.createRoot(rootElement)
22
- root.render(
23
- <ServicesProvider>
24
- <KuckitProvider />
25
- </ServicesProvider>
26
- )
27
- }
16
+ const rootEl = document.getElementById('app')!
17
+ ReactDOM.createRoot(rootEl).render(<KuckitApp />)
@@ -1,17 +1,51 @@
1
1
  import type { ClientModuleSpec } from '@kuckit/sdk-react'
2
2
  import { kuckitClientModule as itemsClientModule } from '@__APP_NAME_KEBAB__/items-module/client'
3
+ import config from '../../../kuckit.config'
3
4
 
4
5
  /**
5
- * Client modules configuration
6
+ * Known client modules mapping: package name → direct module import
6
7
  *
7
- * Modules installed via 'kuckit add' will be added here automatically.
8
+ * This enables config-driven module loading while keeping direct imports
9
+ * for workspace packages (required for Vite bundling).
10
+ *
11
+ * When adding a new module:
12
+ * 1. Add to kuckit.config.ts (source of truth)
13
+ * 2. Add import and mapping here
8
14
  */
9
- export const getClientModuleSpecs = (): ClientModuleSpec[] => {
10
- const modules: ClientModuleSpec[] = [
11
- // KUCKIT_CLIENT_MODULES_START
12
- { module: itemsClientModule },
13
- // KUCKIT_CLIENT_MODULES_END
14
- ]
15
+ const KNOWN_CLIENT_MODULES: Record<string, { module: unknown }> = {
16
+ // KUCKIT_KNOWN_CLIENT_MODULES_START
17
+ '@__APP_NAME_KEBAB__/items-module': {
18
+ module: itemsClientModule,
19
+ },
20
+ // KUCKIT_KNOWN_CLIENT_MODULES_END
21
+ }
22
+
23
+ /**
24
+ * Convert unified config to ClientModuleSpec array.
25
+ * For known modules, uses direct imports. For npm packages, uses package-based loading.
26
+ */
27
+ function configToClientModuleSpecs(): ClientModuleSpec[] {
28
+ return config.modules
29
+ .filter((m) => m.enabled !== false)
30
+ .map((m) => {
31
+ const known = KNOWN_CLIENT_MODULES[m.package]
32
+ if (known) {
33
+ return {
34
+ module: known.module,
35
+ config: m.config,
36
+ } as ClientModuleSpec
37
+ }
38
+ // For npm packages, use package-based loading (client subpath)
39
+ return {
40
+ package: `${m.package}/client`,
41
+ config: m.config,
42
+ } as ClientModuleSpec
43
+ })
44
+ }
15
45
 
16
- return modules
46
+ /**
47
+ * Get client module specs from kuckit.config.ts (single source of truth).
48
+ */
49
+ export const getClientModuleSpecs = (): ClientModuleSpec[] => {
50
+ return configToClientModuleSpecs()
17
51
  }
@@ -1,6 +1,6 @@
1
1
  import type { QueryClient } from '@tanstack/react-query'
2
2
  import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'
3
- import type { ORPCUtils } from '../services/types'
3
+ import type { ORPCUtils } from '@kuckit/app-web'
4
4
 
5
5
  export interface RouterAppContext {
6
6
  orpc: ORPCUtils
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'
2
2
  import { DashboardLayout } from '@/components/dashboard/dashboard-layout'
3
- import { createAuthClientService } from '../services/auth-client'
3
+ import { createAuthClientService } from '@kuckit/app-web'
4
4
 
5
5
  export const Route = createFileRoute('/dashboard')({
6
6
  beforeLoad: async () => {
@@ -1,12 +1,12 @@
1
1
  import { createFileRoute, Link } from '@tanstack/react-router'
2
- import { useServices } from '../providers/ServicesProvider'
2
+ import { useAuth } from '@kuckit/app-web'
3
3
 
4
4
  export const Route = createFileRoute('/')({
5
5
  component: HomePage,
6
6
  })
7
7
 
8
8
  function HomePage() {
9
- const { authClient } = useServices()
9
+ const authClient = useAuth()
10
10
  const { data: session, isPending } = authClient.useSession()
11
11
 
12
12
  if (isPending) {
@@ -1,13 +1,13 @@
1
1
  import { createFileRoute, useNavigate } from '@tanstack/react-router'
2
2
  import { useState } from 'react'
3
- import { useServices } from '../providers/ServicesProvider'
3
+ import { useAuth } from '@kuckit/app-web'
4
4
 
5
5
  export const Route = createFileRoute('/login')({
6
6
  component: LoginPage,
7
7
  })
8
8
 
9
9
  function LoginPage() {
10
- const { authClient } = useServices()
10
+ const authClient = useAuth()
11
11
  const navigate = useNavigate()
12
12
  const [isSignUp, setIsSignUp] = useState(false)
13
13
  const [email, setEmail] = useState('')
@@ -2,6 +2,7 @@ import { defineConfig } from 'drizzle-kit'
2
2
  import dotenv from 'dotenv'
3
3
  import { dirname, resolve } from 'path'
4
4
  import { fileURLToPath } from 'url'
5
+ import { getModuleSchemaPaths } from '@kuckit/db/schema-discovery'
5
6
 
6
7
  const currentFilePath = fileURLToPath(import.meta.url)
7
8
  const currentDirPath = dirname(currentFilePath)
@@ -26,12 +27,7 @@ function buildDatabaseUrl(): string {
26
27
  }
27
28
 
28
29
  export default defineConfig({
29
- schema: [
30
- // Auth schema from @kuckit/db (resolved from node_modules)
31
- './node_modules/@kuckit/db/dist/schema/auth.js',
32
- // Local module schemas
33
- resolve(currentDirPath, './packages/items-module/src/adapters'),
34
- ],
30
+ schema: getModuleSchemaPaths(),
35
31
  out: './drizzle',
36
32
  dialect: 'postgresql',
37
33
  dbCredentials: { url: buildDatabaseUrl() },
@@ -0,0 +1,30 @@
1
+ import { defineConfig } from '@kuckit/sdk/config'
2
+
3
+ /**
4
+ * Kuckit Configuration
5
+ *
6
+ * This is the unified configuration file for both server and client modules.
7
+ * Both apps/server and apps/web will read from this single source of truth.
8
+ *
9
+ * @see https://github.com/kuckit/kuckit#configuration
10
+ */
11
+ export default defineConfig({
12
+ modules: [
13
+ // KUCKIT_MODULES_START
14
+ { package: '@__APP_NAME_KEBAB__/items-module', schemaDir: 'src/adapters' },
15
+ // KUCKIT_MODULES_END
16
+ ],
17
+
18
+ server: {
19
+ apiPrefix: '/api',
20
+ },
21
+
22
+ client: {
23
+ routeInjection: true,
24
+ componentRegistry: 'context',
25
+ },
26
+
27
+ discovery: {
28
+ enabled: false,
29
+ },
30
+ })
@@ -208,3 +208,86 @@ export function ItemsPage() {
208
208
  ```
209
209
 
210
210
  **Important**: Never use `import.meta.env` directly in module components - use `useRpc()` instead. Module packages are bundled separately and don't have access to the host app's environment variables.
211
+
212
+ <!-- MCP_AGENT_MAIL_AND_BEADS_SNIPPET_START -->
213
+
214
+ ## MCP Agent Mail: coordination for multi-agent workflows
215
+
216
+ What it is
217
+
218
+ - A mail-like layer that lets coding agents coordinate asynchronously via MCP tools and resources.
219
+ - Provides identities, inbox/outbox, searchable threads, and advisory file reservations, with human-auditable artifacts in Git.
220
+
221
+ Why it's useful
222
+
223
+ - Prevents agents from stepping on each other with explicit file reservations (leases) for files/globs.
224
+ - Keeps communication out of your token budget by storing messages in a per-project archive.
225
+ - Offers quick reads (`resource://inbox/...`, `resource://thread/...`) and macros that bundle common flows.
226
+
227
+ How to use effectively
228
+
229
+ 1. Same repository
230
+ - Register an identity: call `ensure_project`, then `register_agent` using this repo's absolute path as `project_key`.
231
+ - Reserve files before you edit: `file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true)` to signal intent and avoid conflict.
232
+ - Communicate with threads: use `send_message(..., thread_id="FEAT-123")`; check inbox with `fetch_inbox` and acknowledge with `acknowledge_message`.
233
+ - Read fast: `resource://inbox/{Agent}?project=<abs-path>&limit=20` or `resource://thread/{id}?project=<abs-path>&include_bodies=true`.
234
+ - Tip: set `AGENT_NAME` in your environment so the pre-commit guard can block commits that conflict with others' active exclusive file reservations.
235
+
236
+ 2. Across different repos in one project (e.g., Next.js frontend + FastAPI backend)
237
+ - 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/**`).
238
+ - 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.
239
+
240
+ Macros vs granular tools
241
+
242
+ - 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`.
243
+ - Use granular tools when you need control: `register_agent`, `file_reservation_paths`, `send_message`, `fetch_inbox`, `acknowledge_message`.
244
+
245
+ Common pitfalls
246
+
247
+ - "from_agent not registered": always `register_agent` in the correct `project_key` first.
248
+ - "FILE_RESERVATION_CONFLICT": adjust patterns, wait for expiry, or use a non-exclusive reservation when appropriate.
249
+ - 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.
250
+
251
+ ## Integrating with Beads (dependency-aware task planning)
252
+
253
+ 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)
254
+
255
+ Recommended conventions
256
+
257
+ - **Single source of truth**: Use **Beads** for task status/priority/dependencies; use **Agent Mail** for conversation, decisions, and attachments (audit).
258
+ - **Shared identifiers**: Use the Beads issue id (e.g., `bd-123`) as the Mail `thread_id` and prefix message subjects with `[bd-123]`.
259
+ - **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.
260
+
261
+ Typical flow (agents)
262
+
263
+ 1. **Pick ready work** (Beads)
264
+ - `bd ready --json` → choose one item (highest priority, no blockers)
265
+ 2. **Reserve edit surface** (Mail)
266
+ - `file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true, reason="bd-123")`
267
+ 3. **Announce start** (Mail)
268
+ - `send_message(..., thread_id="bd-123", subject="[bd-123] Start: <short title>", ack_required=true)`
269
+ 4. **Work and update**
270
+ - Reply in-thread with progress and attach artifacts/images; keep the discussion in one thread per issue id
271
+ 5. **Complete and release**
272
+ - `bd close bd-123 --reason "Completed"` (Beads is status authority)
273
+ - `release_file_reservations(project_key, agent_name, paths=["src/**"])`
274
+ - Final Mail reply: `[bd-123] Completed` with summary and links
275
+
276
+ Mapping cheat-sheet
277
+
278
+ - **Mail `thread_id`** ↔ `bd-###`
279
+ - **Mail subject**: `[bd-###] …`
280
+ - **File reservation `reason`**: `bd-###`
281
+ - **Commit messages (optional)**: include `bd-###` for traceability
282
+
283
+ Event mirroring (optional automation)
284
+
285
+ - On `bd update --status blocked`, send a high-importance Mail message in thread `bd-###` describing the blocker.
286
+ - 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`.
287
+
288
+ Pitfalls to avoid
289
+
290
+ - Don't create or manage tasks in Mail; treat Beads as the single task queue.
291
+ - Always include `bd-###` in message `thread_id` to avoid ID drift across tools.
292
+
293
+ <!-- MCP_AGENT_MAIL_AND_BEADS_SNIPPET_END -->
@@ -1,20 +0,0 @@
1
- import express, { type Express } from 'express'
2
- import cors from 'cors'
3
-
4
- /**
5
- * Create and configure Express app
6
- */
7
- export const createApp = (): Express => {
8
- const app = express()
9
-
10
- app.use(
11
- cors({
12
- origin: process.env.CORS_ORIGIN || 'http://localhost:3001',
13
- methods: ['GET', 'POST', 'OPTIONS'],
14
- allowedHeaders: ['Content-Type', 'Authorization'],
15
- credentials: true,
16
- })
17
- )
18
-
19
- return app
20
- }
@@ -1,10 +0,0 @@
1
- import { auth } from '@kuckit/auth'
2
- import { toNodeHandler } from 'better-auth/node'
3
- import type { Express } from 'express'
4
-
5
- /**
6
- * Setup Better-Auth routes
7
- */
8
- export const setupAuth = (app: Express) => {
9
- app.all('/api/auth{/*path}', toNodeHandler(auth))
10
- }
@@ -1,21 +0,0 @@
1
- import type { ModuleSpec } from '@kuckit/sdk'
2
- import { kuckitModule as itemsModule } from '@__APP_NAME_KEBAB__/items-module'
3
-
4
- /**
5
- * Module specifications for the server application
6
- *
7
- * Add modules here to configure the server's functionality.
8
- * Modules are loaded in order, with dependencies resolved automatically.
9
- *
10
- * IMPORTANT: Server and client modules must be kept in sync!
11
- * If you add/remove a module here, also update apps/web/src/modules.client.ts
12
- */
13
- export const getModuleSpecs = (): ModuleSpec[] => {
14
- const modules: ModuleSpec[] = [
15
- // KUCKIT_SERVER_MODULES_START
16
- { module: itemsModule },
17
- // KUCKIT_SERVER_MODULES_END
18
- ]
19
-
20
- return modules
21
- }