Skip to content

Commit fb61a78

Browse files
dcramerclaudecodex
authored
feat(packaging): ship wrapper-first Next.js npm integration (#20)
Make Junior deployable as an npm dependency using standard Next.js wrappers. The previous package approach depended on CLI-generated shim files and custom build commands. This change moves to a wrapper-first integration so consumers keep normal Next.js dev/build/start scripts while importing Junior runtime surfaces directly. Key changes: - Add importable runtime surfaces for app layout, catch-all API routing, instrumentation, and Next.js config composition - Introduce `withJunior()` to merge required `serverExternalPackages` and `outputFileTracingIncludes` for `/api/**`, with optional Sentry wrapping - Keep runtime handlers centralized under `src/handlers/*` and reuse them from app routes and package exports - Update packaging/build outputs (`tsup`, exports map, `files`, peer dependency layout) so published artifacts are consumable from external Next.js apps - Move default bot content to root-level `data/`, `skills/`, and `plugins/` conventions for scaffolded and consuming repos - Update README/setup guidance to the wrapper model (`app/api/[...path]/route.js`, `next.config.mjs`, `instrumentation.js`, optional `app/layout.js`) - Preserve existing `outputFileTracingIncludes["/api/**"]` entries when applying Junior config instead of overwriting consumer-defined values - Restore webhook route parity by matching only single-segment platforms (e.g. `/api/webhooks/slack`) and returning 404 for multi-segment paths This keeps deployment ergonomics aligned with normal Next.js behavior while preserving the runtime/data loading guarantees Junior needs in production. Refs GH-9 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GPT-5 <noreply@openai.com> Co-authored-by: GPT-5 Codex <codex@openai.com>
1 parent d129966 commit fb61a78

299 files changed

Lines changed: 12316 additions & 663 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 0 additions & 9 deletions
This file was deleted.

README.md

Lines changed: 8 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,15 @@
1-
# junior
1+
# junior monorepo
22

3-
Slack bot built with Next.js + Chat SDK.
3+
This repository is organized as a workspace:
44

5-
Junior responds when mentioned in Slack and can continue replying in subscribed threads. It also supports slash-invoked local skills (`/skill-name ...`) and built-in tools (web search/fetch, image generation, Slack canvases/lists).
5+
- `packages/junior`: publishable `junior` package and source
6+
- `packages/jr-sentry`: smoke-test consumer app that uses `junior` via `workspace:*`
67

7-
## Requirements
8-
9-
- Node.js 20+
10-
- pnpm
11-
- Vercel CLI
12-
- Slack app credentials already configured in Vercel
13-
- Redis configured in Vercel (`REDIS_URL`)
14-
15-
## Local setup
16-
17-
1. Install dependencies.
8+
Common commands from repo root:
189

1910
```bash
2011
pnpm install
12+
pnpm build:pkg
13+
pnpm test
14+
pnpm --filter jr-sentry build
2115
```
22-
23-
2. Link this repo to the Sentry Vercel project and pull dev env.
24-
25-
```bash
26-
pnpm dlx vercel@latest login
27-
pnpm dlx vercel@latest switch
28-
pnpm dlx vercel@latest link --yes --scope sentry
29-
pnpm dlx vercel@latest env pull .env --environment=development --scope sentry
30-
```
31-
32-
3. Start the app.
33-
34-
```bash
35-
pnpm dev
36-
```
37-
38-
## Slack tunnel (Cloudflare)
39-
40-
Install `cloudflared` if you don't have it (`brew install cloudflared` on macOS).
41-
42-
### Quick (random hostname each time)
43-
44-
```bash
45-
cloudflared tunnel --url http://localhost:3000
46-
```
47-
48-
### Stable hostname (one-time setup)
49-
50-
Requires a free Cloudflare account and a domain managed through Cloudflare DNS.
51-
52-
```bash
53-
cloudflared tunnel login
54-
cloudflared tunnel create junior-dev
55-
cloudflared tunnel route dns junior-dev junior-dev.yourdomain.com
56-
```
57-
58-
Then each time you develop:
59-
60-
```bash
61-
cloudflared tunnel run --url http://localhost:3000 junior-dev
62-
```
63-
64-
### Configuring Slack
65-
66-
Set Slack Event Subscriptions and Interactivity request URL to:
67-
68-
```text
69-
https://<tunnel-host>/api/webhooks/slack
70-
```
71-
72-
With a stable hostname you only need to do this once. Invite `@junior` to a channel and mention it.
73-
74-
## Evals
75-
76-
Use evals for end-to-end behavior testing of Junior's reply pipeline (prompting, tools, and expected outputs) with LLM-judged numeric scoring.
77-
78-
Evals intentionally exclude live Slack integration concerns (Slack transport, app permissions, and webhook delivery).
79-
80-
Authoring guidance lives in `evals/README.md` and `specs/testing/evals-spec.md`.
81-
82-
```bash
83-
pnpm evals
84-
```
85-
86-
## Test env isolation
87-
88-
Vitest loads `.env`, `.env.local`, `.env.test`, then `.env.test.local` so test-specific values override development/prod values.
89-
90-
Slack credentials are intentionally replaced with test values for tests/evals to prevent accidental use of real Slack tokens.

next.config.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

package.json

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,18 @@
11
{
2-
"name": "junior",
3-
"version": "0.1.0",
2+
"name": "junior-monorepo",
43
"private": true,
5-
"type": "module",
64
"packageManager": "pnpm@10.30.2",
75
"scripts": {
8-
"dev": "next dev",
9-
"build": "next build",
10-
"start": "next start",
11-
"test": "pnpm run test:slack-boundary && vitest run",
12-
"test:watch": "vitest",
13-
"preevals": "pnpm run test:slack-boundary",
14-
"evals": "pnpm exec vitest run -c vitest.evals.config.ts",
15-
"test:slack-boundary": "node scripts/check-slack-test-boundary.mjs",
16-
"typecheck": "tsc --noEmit",
17-
"skills:check": "node scripts/check-skills.mjs"
18-
},
19-
"dependencies": {
20-
"@ai-sdk/gateway": "^3.0.57",
21-
"@chat-adapter/slack": "^4.15.0",
22-
"@chat-adapter/state-memory": "^4.15.0",
23-
"@chat-adapter/state-redis": "^4.15.0",
24-
"@mariozechner/pi-agent-core": "^0.55.0",
25-
"@mariozechner/pi-ai": "^0.55.0",
26-
"@sentry/nextjs": "^10.40.0",
27-
"@sinclair/typebox": "^0.34.48",
28-
"@slack/web-api": "^7.14.1",
29-
"@vercel/sandbox": "^1.7.1",
30-
"@workflow/serde": "4.1.0-beta.2",
31-
"ai": "^6.0.103",
32-
"bash-tool": "^1.3.15",
33-
"chat": "^4.15.0",
34-
"just-bash": "^2.10.6",
35-
"next": "^16.1.6",
36-
"node-html-markdown": "^2.0.0",
37-
"react": "^19.2.4",
38-
"react-dom": "^19.2.4",
39-
"workflow": "4.1.0-beta.60",
40-
"yaml": "^2.8.2",
41-
"zod": "^4.3.6"
6+
"dev": "pnpm --filter junior dev",
7+
"build": "pnpm --filter junior build",
8+
"build:pkg": "pnpm --filter junior build:pkg",
9+
"start": "pnpm --filter junior start",
10+
"test": "pnpm --filter junior test",
11+
"test:watch": "pnpm --filter junior test:watch",
12+
"preevals": "pnpm --filter junior preevals",
13+
"evals": "pnpm --filter junior evals",
14+
"typecheck": "pnpm --filter junior typecheck",
15+
"skills:check": "pnpm --filter junior skills:check"
4216
},
4317
"pnpm": {
4418
"peerDependencyRules": {
@@ -48,17 +22,7 @@
4822
}
4923
},
5024
"patchedDependencies": {
51-
"@chat-adapter/slack": "patches/@chat-adapter__slack.patch"
25+
"@chat-adapter/slack": "packages/junior/patches/@chat-adapter__slack.patch"
5226
}
53-
},
54-
"devDependencies": {
55-
"@types/node": "^25.3.0",
56-
"@types/react": "^19.2.14",
57-
"@types/react-dom": "^19.2.3",
58-
"msw": "^2.12.10",
59-
"typescript": "^5.9.3",
60-
"vercel": "^50.23.2",
61-
"vitest": "^4.0.18",
62-
"vitest-evals": "^0.6.0"
6327
}
6428
}

packages/jr-sentry/.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
SLACK_BOT_TOKEN=
2+
SLACK_SIGNING_SECRET=
3+
JUNIOR_BOT_NAME=
4+
AI_MODEL=
5+
AI_FAST_MODEL=
6+
REDIS_URL=
7+
NEXT_PUBLIC_SENTRY_DSN=

packages/jr-sentry/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
.next/
3+
.env
4+
.env.local
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { GET, POST } from "junior/handler";
2+
export const runtime = "nodejs";

packages/jr-sentry/app/layout.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "junior/app/layout";

packages/jr-sentry/data/SOUL.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# jr-sentry
2+
3+
You are jr-sentry, a helpful assistant.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { register, onRequestError } from "junior/instrumentation";

0 commit comments

Comments
 (0)