@cogito.ai/cli 0.2.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/LICENSE +23 -0
- package/dist/index.js +17870 -0
- package/dist/registry.json +26 -0
- package/dist/templates/web-nextjs/.env.example +22 -0
- package/dist/templates/web-nextjs/.github/copilot-instructions.md +43 -0
- package/dist/templates/web-nextjs/AGENTS.md +46 -0
- package/dist/templates/web-nextjs/README.md +204 -0
- package/dist/templates/web-nextjs/eslint.config.js +12 -0
- package/dist/templates/web-nextjs/messages/en.json +10 -0
- package/dist/templates/web-nextjs/messages/zh.json +10 -0
- package/dist/templates/web-nextjs/middleware.ts +34 -0
- package/dist/templates/web-nextjs/next-env.d.ts +6 -0
- package/dist/templates/web-nextjs/next.config.ts +8 -0
- package/dist/templates/web-nextjs/openspec/changes/.gitkeep +0 -0
- package/dist/templates/web-nextjs/openspec/changes/archive/.gitkeep +0 -0
- package/dist/templates/web-nextjs/openspec/config.yaml +28 -0
- package/dist/templates/web-nextjs/openspec/specs/.gitkeep +0 -0
- package/dist/templates/web-nextjs/package.json +37 -0
- package/dist/templates/web-nextjs/src/app/[locale]/globals.css +3 -0
- package/dist/templates/web-nextjs/src/app/[locale]/hello/page.tsx +45 -0
- package/dist/templates/web-nextjs/src/app/[locale]/layout.tsx +37 -0
- package/dist/templates/web-nextjs/src/app/[locale]/page.tsx +12 -0
- package/dist/templates/web-nextjs/src/core/repositories/.gitkeep +2 -0
- package/dist/templates/web-nextjs/src/core/repositories/IGreetingRepository.ts +14 -0
- package/dist/templates/web-nextjs/src/core/types/.gitkeep +3 -0
- package/dist/templates/web-nextjs/src/core/types/greeting.ts +9 -0
- package/dist/templates/web-nextjs/src/features/_experiments/.gitkeep +2 -0
- package/dist/templates/web-nextjs/src/features/hello/__contract__.ts +16 -0
- package/dist/templates/web-nextjs/src/features/hello/hello.test.ts +16 -0
- package/dist/templates/web-nextjs/src/features/hello/index.ts +5 -0
- package/dist/templates/web-nextjs/src/features/hello/service.ts +12 -0
- package/dist/templates/web-nextjs/src/i18n/request.ts +11 -0
- package/dist/templates/web-nextjs/src/infra/db/.gitkeep +3 -0
- package/dist/templates/web-nextjs/src/infra/db/SupabaseGreetingRepository.ts +58 -0
- package/dist/templates/web-nextjs/src/infra/db/client.ts +55 -0
- package/dist/templates/web-nextjs/src/infra/db/schema.ts +19 -0
- package/dist/templates/web-nextjs/tailwind.config.ts +11 -0
- package/dist/templates/web-nextjs/tsconfig.json +22 -0
- package/dist/templates/web-nextjs/vitest.config.ts +16 -0
- package/package.json +37 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1",
|
|
3
|
+
"templates": [
|
|
4
|
+
{
|
|
5
|
+
"id": "web-nextjs",
|
|
6
|
+
"name": "@cogito.ai/template-web-nextjs",
|
|
7
|
+
"description": "Full-stack Next.js 16 starter with Supabase, next-intl, Tailwind CSS and Vitest",
|
|
8
|
+
"minCliVersion": "0.1.0",
|
|
9
|
+
"source": "templates/web-nextjs",
|
|
10
|
+
"resolvedDependencies": {
|
|
11
|
+
"@supabase/ssr": "^0.10.3",
|
|
12
|
+
"next": "16",
|
|
13
|
+
"next-intl": "^4.13.0",
|
|
14
|
+
"react": "19",
|
|
15
|
+
"react-dom": "19",
|
|
16
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
17
|
+
"tailwindcss": "^4.3.0",
|
|
18
|
+
"typescript": "^6.0.3",
|
|
19
|
+
"vitest": "^4.1.8",
|
|
20
|
+
"@cogito.ai/eslint-config": "^0.2.0",
|
|
21
|
+
"@cogito.ai/tsconfig": "^0.2.0",
|
|
22
|
+
"@fission-ai/openspec": "^1.3.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
# Supabase
|
|
3
|
+
# Get these from: Supabase Dashboard → Project Settings → API
|
|
4
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
# Project URL — safe to expose in browser
|
|
7
|
+
NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
|
|
8
|
+
|
|
9
|
+
# Anon key — safe to expose in browser (Row Level Security enforces access)
|
|
10
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your-anon-key>
|
|
11
|
+
|
|
12
|
+
# Service role key — SERVER ONLY, never expose to browser
|
|
13
|
+
# Used for admin operations that bypass RLS (e.g., seed scripts, cron jobs)
|
|
14
|
+
# Leave empty if not needed
|
|
15
|
+
SUPABASE_SERVICE_ROLE_KEY=<your-service-role-key>
|
|
16
|
+
|
|
17
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
# App
|
|
19
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
# Public URL of this app (used for OG image URLs, absolute redirects, etc.)
|
|
22
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# AgentDock Web-Next.js Template — Copilot Instructions
|
|
2
|
+
|
|
3
|
+
> Applies to: GitHub Copilot (chat + completions) in any project generated from this template.
|
|
4
|
+
|
|
5
|
+
## Directory Contract
|
|
6
|
+
|
|
7
|
+
This project enforces a four-layer directory contract. **Before placing any code**, confirm which layer it belongs to:
|
|
8
|
+
|
|
9
|
+
| Layer | Path | Purpose | Rules |
|
|
10
|
+
| --------------- | ---------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
11
|
+
| **core** | `src/core/` | Stable domain layer — types, repository interfaces, pure logic | No framework imports, no DB clients, read-only |
|
|
12
|
+
| **features** | `src/features/` | AI coding zone — business features | Must have `__contract__.ts`; no direct DB access; no cross-feature imports |
|
|
13
|
+
| **infra** | `src/infra/` | Infrastructure — Supabase clients, third-party SDKs | Requires approval; only `core/repositories` implementations live here |
|
|
14
|
+
| **experiments** | `src/features/_experiments/` | Sandbox | Never imported by non-experimental features |
|
|
15
|
+
|
|
16
|
+
## Layer 2 Architecture Rules (error-level, enforced by ESLint)
|
|
17
|
+
|
|
18
|
+
Four rules are enforced at `error` severity in `src/features/**`. Violations fail `pnpm lint`.
|
|
19
|
+
|
|
20
|
+
1. **`no-direct-db-in-features`** — Features must not import from `infra/db`. Use repository interfaces from `src/core/repositories/` instead.
|
|
21
|
+
2. **`require-feature-contract`** — Every feature subdirectory must contain `__contract__.ts` declaring its public API boundary.
|
|
22
|
+
3. **`no-cross-feature`** — Features must not directly import from sibling feature internals. Import through the feature's `index.ts` only.
|
|
23
|
+
4. **`no-core-mutation`** — `src/core/` is read-only. Features and infra must not modify core types or interfaces in-place.
|
|
24
|
+
|
|
25
|
+
## Hard Rules
|
|
26
|
+
|
|
27
|
+
1. **No secrets** — Never write real API keys, tokens, or passwords. Use placeholders: `YOUR_KEY_HERE`, `process.env.YOUR_VAR`.
|
|
28
|
+
2. **TypeScript strict** — `strict: true` enforced. No `any`, no `// @ts-ignore` without explanation.
|
|
29
|
+
3. **Conventional commits** — `type(scope): summary` format required.
|
|
30
|
+
4. **pnpm only** — Do not use `npm install` or `yarn`. Always `pnpm add`.
|
|
31
|
+
5. **ESLint config** — Architecture rules come from `packages/eslint-config/features.js` in the AgentDock platform.
|
|
32
|
+
|
|
33
|
+
## Feature Development Pattern
|
|
34
|
+
|
|
35
|
+
When adding a new feature `src/features/<name>/`:
|
|
36
|
+
|
|
37
|
+
1. Create `__contract__.ts` first — declare input/output types and public API.
|
|
38
|
+
2. Create `service.ts` — pure business logic, no side effects.
|
|
39
|
+
3. Create `index.ts` — only export what `__contract__.ts` declares.
|
|
40
|
+
4. Create `<name>.test.ts` — Vitest unit tests.
|
|
41
|
+
5. Create page/component in `src/app/[locale]/` to consume the feature.
|
|
42
|
+
|
|
43
|
+
Reference implementation: `src/features/hello/`.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# AgentDock Web-Next.js Template — Agent Execution Boundaries
|
|
2
|
+
|
|
3
|
+
> For AI coding agents (GitHub Copilot CLI, etc.) running in projects generated from this template.
|
|
4
|
+
|
|
5
|
+
## Generation Assumption
|
|
6
|
+
|
|
7
|
+
- Template source may contain monorepo-only dependency specifiers (for example, workspace links).
|
|
8
|
+
- Scaffold generation MUST rewrite these dependencies to installable versions for the generated standalone project.
|
|
9
|
+
|
|
10
|
+
## Autonomy Boundaries
|
|
11
|
+
|
|
12
|
+
### May execute autonomously
|
|
13
|
+
|
|
14
|
+
- `pnpm install`, `pnpm build`, `pnpm check-types`, `pnpm lint`, `pnpm test`
|
|
15
|
+
- Creating / editing files within the current task scope
|
|
16
|
+
- Adding new features inside `src/features/` (following the contract pattern)
|
|
17
|
+
- Adding repository interfaces to `src/core/repositories/`
|
|
18
|
+
- Updating translations in `messages/`
|
|
19
|
+
- Writing or updating tests in `src/**/*.test.ts`
|
|
20
|
+
- Running `openspec` CLI commands (read-only: `list`, `status`, `instructions`, `validate`)
|
|
21
|
+
|
|
22
|
+
### Must pause and confirm
|
|
23
|
+
|
|
24
|
+
- Any `git push`, `git push --force`, or publishing to a registry
|
|
25
|
+
- Deleting files (including tracked files)
|
|
26
|
+
- Adding new **top-level** directories outside the four-layer contract (`core/`, `features/`, `infra/`, `app/`)
|
|
27
|
+
- Adding new `dependencies` or `devDependencies` to `package.json`
|
|
28
|
+
- Any change to `openspec/config.yaml`
|
|
29
|
+
- Modifying `middleware.ts` (affects routing for all locales)
|
|
30
|
+
|
|
31
|
+
### Prohibited
|
|
32
|
+
|
|
33
|
+
- Writing real secrets, API keys, or credentials anywhere
|
|
34
|
+
- Running `rm -rf` on tracked directories
|
|
35
|
+
- Bypassing git hooks with `--no-verify`
|
|
36
|
+
- Importing from `src/infra/db/` inside `src/features/**` (violates Layer 2 rules)
|
|
37
|
+
- Creating features without `__contract__.ts` (violates require-feature-contract)
|
|
38
|
+
|
|
39
|
+
## Acceptance Criteria (before marking a task done)
|
|
40
|
+
|
|
41
|
+
1. `pnpm lint` — exits 0, no Layer 2 violations in `src/features/`
|
|
42
|
+
2. ESLint ignores build artifacts (at minimum `.next/**`) so generated files are never linted.
|
|
43
|
+
3. `pnpm build && pnpm lint` — both exit 0 (lint remains stable after a build).
|
|
44
|
+
4. `pnpm test` — all unit tests pass
|
|
45
|
+
5. `pnpm check-types` — TypeScript strict mode, no errors
|
|
46
|
+
6. New features include `__contract__.ts`, `index.ts`, and at least one test
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
> Generated from the [AgentDock](https://github.com/CogitoTech/agentdock) `web-nextjs` template.
|
|
4
|
+
|
|
5
|
+
Full-stack Next.js 16 starter with Supabase, i18n, Tailwind CSS and strict TypeScript.
|
|
6
|
+
Designed for both human developers and AI coding agents.
|
|
7
|
+
|
|
8
|
+
## Tech Stack
|
|
9
|
+
|
|
10
|
+
| Layer | Technology |
|
|
11
|
+
|-------|-----------|
|
|
12
|
+
| Framework | [Next.js 16](https://nextjs.org) (App Router) |
|
|
13
|
+
| Language | [TypeScript 5+](https://www.typescriptlang.org) (strict mode) |
|
|
14
|
+
| Styling | [Tailwind CSS v4](https://tailwindcss.com) |
|
|
15
|
+
| Database / Auth | [Supabase](https://supabase.com) (`@supabase/ssr`) |
|
|
16
|
+
| i18n | [next-intl](https://next-intl.dev) (en / zh out of the box) |
|
|
17
|
+
| Testing | [Vitest](https://vitest.dev) |
|
|
18
|
+
| Package manager | [pnpm](https://pnpm.io) ≥ 9 |
|
|
19
|
+
| Runtime | Node.js ≥ 18 |
|
|
20
|
+
| Governance | [OpenSpec](https://github.com/fission-ai/openspec) (`@fission-ai/openspec`) |
|
|
21
|
+
|
|
22
|
+
## Directory Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
src/
|
|
26
|
+
├── app/ # Next.js App Router — pages and layouts (per-locale)
|
|
27
|
+
├── core/ # Domain types + repository interfaces (no framework deps)
|
|
28
|
+
├── features/ # AI coding zone — one directory per feature
|
|
29
|
+
│ └── hello/ # Reference feature (copy this pattern)
|
|
30
|
+
├── infra/ # Supabase implementations (requires human review to edit)
|
|
31
|
+
│ └── db/ # Supabase client + repository implementations
|
|
32
|
+
└── i18n/ # next-intl configuration
|
|
33
|
+
messages/ # Translation files (en.json, zh.json)
|
|
34
|
+
openspec/ # Project governance — changes and specs
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> **Four-layer contract** is enforced by ESLint Layer 2 rules.
|
|
38
|
+
> Features must not import from `infra/db` directly — use `core/repositories` interfaces.
|
|
39
|
+
|
|
40
|
+
## Getting Started
|
|
41
|
+
|
|
42
|
+
### 1. Prerequisites
|
|
43
|
+
|
|
44
|
+
- Node.js ≥ 18
|
|
45
|
+
- pnpm ≥ 9 — `npm install -g pnpm`
|
|
46
|
+
- A [Supabase](https://supabase.com) project (free tier works)
|
|
47
|
+
|
|
48
|
+
### 2. Install dependencies
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm install
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Configure environment variables
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cp .env.example .env.local
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Open `.env.local` and fill in your values:
|
|
61
|
+
|
|
62
|
+
| Variable | Where to find it |
|
|
63
|
+
|----------|-----------------|
|
|
64
|
+
| `NEXT_PUBLIC_SUPABASE_URL` | Supabase Dashboard → Project Settings → API → Project URL |
|
|
65
|
+
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase Dashboard → Project Settings → API → anon / public |
|
|
66
|
+
| `SUPABASE_SERVICE_ROLE_KEY` | Supabase Dashboard → Project Settings → API → service_role (server only) |
|
|
67
|
+
| `NEXT_PUBLIC_APP_URL` | `http://localhost:3000` for local dev |
|
|
68
|
+
|
|
69
|
+
### 4. Start development server
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpm dev
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
### Common commands
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pnpm dev # Start dev server (http://localhost:3000)
|
|
83
|
+
pnpm build # Production build
|
|
84
|
+
pnpm start # Serve production build
|
|
85
|
+
pnpm test # Run Vitest unit tests
|
|
86
|
+
pnpm check-types # TypeScript type check (no emit)
|
|
87
|
+
pnpm lint # ESLint (includes Layer 2 architectural rules)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Adding a new feature
|
|
91
|
+
|
|
92
|
+
Features live in `src/features/`. Follow the reference implementation in `src/features/hello/`:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
src/features/<your-feature>/
|
|
96
|
+
├── __contract__.ts # Public API surface (required — export types and functions)
|
|
97
|
+
├── index.ts # Re-exports from __contract__.ts
|
|
98
|
+
└── *.test.ts # Vitest unit tests
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Rules:
|
|
102
|
+
- Features import from `src/core/` (domain interfaces) — never from `src/infra/` directly.
|
|
103
|
+
- Every feature directory **must** have `__contract__.ts` (ESLint enforces this).
|
|
104
|
+
- Add translations to `messages/en.json` and `messages/zh.json`.
|
|
105
|
+
|
|
106
|
+
### Adding a language
|
|
107
|
+
|
|
108
|
+
1. Add locale to `src/i18n/routing.ts` and `middleware.ts`.
|
|
109
|
+
2. Create `messages/<locale>.json` with translated keys.
|
|
110
|
+
|
|
111
|
+
### Running tests
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pnpm test # Run all tests once
|
|
115
|
+
pnpm test --watch # Watch mode
|
|
116
|
+
pnpm test --coverage # With coverage report
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Deployment
|
|
120
|
+
|
|
121
|
+
### Vercel (recommended)
|
|
122
|
+
|
|
123
|
+
1. Push your project to GitHub.
|
|
124
|
+
2. Import the repository in [Vercel](https://vercel.com/new).
|
|
125
|
+
3. Add all environment variables from `.env.example` in **Project Settings → Environment Variables**.
|
|
126
|
+
4. Deploy — Vercel auto-detects Next.js.
|
|
127
|
+
|
|
128
|
+
### Docker / other platforms
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pnpm build
|
|
132
|
+
pnpm start
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Ensure all `NEXT_PUBLIC_*` variables are set at **build time** (they are inlined by Next.js).
|
|
136
|
+
Server-only variables (`SUPABASE_SERVICE_ROLE_KEY`) must be available at **runtime**.
|
|
137
|
+
|
|
138
|
+
### Supabase: production checklist
|
|
139
|
+
|
|
140
|
+
- [ ] Enable Row Level Security (RLS) on all tables.
|
|
141
|
+
- [ ] Review and tighten RLS policies.
|
|
142
|
+
- [ ] Rotate the `service_role` key if it was ever exposed.
|
|
143
|
+
- [ ] Enable Supabase Auth email confirmation.
|
|
144
|
+
|
|
145
|
+
## Governance (OpenSpec)
|
|
146
|
+
|
|
147
|
+
This project uses [OpenSpec](https://github.com/fission-ai/openspec) for AI-assisted change governance.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
openspec list # List all changes
|
|
151
|
+
openspec status --change <name> # Check change artifact status
|
|
152
|
+
openspec instructions apply --change <name> # Get implementation instructions
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Changes live in `openspec/changes/`. The four-gate builder workflow:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Human approves roadmap → Human reviews scope → AI codes → Machine gates (lint/types/tests)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
See `openspec/config.yaml` for project-specific governance rules.
|
|
162
|
+
|
|
163
|
+
## Environment Variables Reference
|
|
164
|
+
|
|
165
|
+
| Variable | Required | Exposed to browser | Description |
|
|
166
|
+
|----------|----------|--------------------|-------------|
|
|
167
|
+
| `NEXT_PUBLIC_SUPABASE_URL` | ✅ | ✅ | Supabase project URL |
|
|
168
|
+
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | ✅ | ✅ | Supabase anon key (RLS protected) |
|
|
169
|
+
| `SUPABASE_SERVICE_ROLE_KEY` | Optional | ❌ | Admin key, bypasses RLS |
|
|
170
|
+
| `NEXT_PUBLIC_APP_URL` | Optional | ✅ | Canonical app URL |
|
|
171
|
+
|
|
172
|
+
> Variables prefixed with `NEXT_PUBLIC_` are bundled into client-side JavaScript.
|
|
173
|
+
> Never put secrets in `NEXT_PUBLIC_*` variables.
|
|
174
|
+
|
|
175
|
+
## FAQs
|
|
176
|
+
|
|
177
|
+
**Q: pnpm install fails with `ERR_PNPM_FETCH_404` for `@cogito.ai/*`**
|
|
178
|
+
A: The `@cogito.ai/tsconfig` and `@cogito.ai/eslint-config` packages must be published to npm before use. If you generated this project before the packages were published, run `pnpm install` again after they are live at [npmjs.com/@agentdock](https://www.npmjs.com/org/cogito.ai).
|
|
179
|
+
|
|
180
|
+
**Q: How do I add a new Supabase table?**
|
|
181
|
+
A: Create the table in the Supabase Dashboard, then add a repository interface to `src/core/repositories/` and implement it in `src/infra/db/`. Do not call Supabase directly from `src/features/` — use the interface.
|
|
182
|
+
|
|
183
|
+
**Q: How do I disable i18n and use a single language?**
|
|
184
|
+
A: Set a single locale in `src/i18n/routing.ts` and remove the `[locale]` segment handling in `middleware.ts`. Keep `messages/en.json` and remove unused locale files.
|
|
185
|
+
|
|
186
|
+
**Q: TypeScript is strict — how do I handle `!` assertions?**
|
|
187
|
+
A: Prefer explicit checks (`if (!value) throw new Error(...)`) over `!` assertions. If you must use one, add a comment explaining why the value is guaranteed non-null.
|
|
188
|
+
|
|
189
|
+
**Q: Can AI agents work in this project?**
|
|
190
|
+
A: Yes. See `AGENTS.md` for the autonomy boundary contract. AI agents may freely edit `src/features/` but must pause for human review before editing `src/infra/` or `middleware.ts`.
|
|
191
|
+
|
|
192
|
+
## Contributing
|
|
193
|
+
|
|
194
|
+
This project follows [Conventional Commits](https://www.conventionalcommits.org):
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
feat(hello): add greeting animation
|
|
198
|
+
fix(i18n): correct zh translation key
|
|
199
|
+
chore: update dependencies
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const base = require("@cogito.ai/eslint-config/base");
|
|
3
|
+
const features = require("@cogito.ai/eslint-config/features");
|
|
4
|
+
|
|
5
|
+
/** @type {import("eslint").Linter.Config[]} */
|
|
6
|
+
module.exports = [
|
|
7
|
+
{
|
|
8
|
+
ignores: [".next/**"],
|
|
9
|
+
},
|
|
10
|
+
...base,
|
|
11
|
+
...features,
|
|
12
|
+
];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import createMiddleware from 'next-intl/middleware'
|
|
2
|
+
import { defineRouting } from 'next-intl/routing'
|
|
3
|
+
import { NextResponse, type NextRequest } from 'next/server'
|
|
4
|
+
|
|
5
|
+
export const routing = defineRouting({
|
|
6
|
+
locales: ['en', 'zh'],
|
|
7
|
+
defaultLocale: 'en',
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const handleI18nRouting = createMiddleware(routing)
|
|
11
|
+
|
|
12
|
+
export default function middleware(request: NextRequest) {
|
|
13
|
+
const segments = request.nextUrl.pathname.split('/').filter(Boolean)
|
|
14
|
+
const firstSegment = segments[0]
|
|
15
|
+
|
|
16
|
+
if (firstSegment !== undefined) {
|
|
17
|
+
const isLocaleLike = /^[a-z]{2}(?:-[A-Z]{2})?$/.test(firstSegment)
|
|
18
|
+
const isSupportedLocale = routing.locales.includes(firstSegment as 'en' | 'zh')
|
|
19
|
+
|
|
20
|
+
// Normalize unknown locale-like prefixes: /fr/hello -> /en/hello.
|
|
21
|
+
if (isLocaleLike && !isSupportedLocale) {
|
|
22
|
+
const url = request.nextUrl.clone()
|
|
23
|
+
url.pathname = `/${routing.defaultLocale}/${segments.slice(1).join('/')}`.replace(/\/$/, '')
|
|
24
|
+
return NextResponse.redirect(url)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return handleI18nRouting(request)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const config = {
|
|
32
|
+
// Match all pathnames except Next.js internals and static files.
|
|
33
|
+
matcher: ['/((?!_next|_vercel|.*\\..*).*)'],
|
|
34
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
schema: spec-driven
|
|
2
|
+
|
|
3
|
+
# Project context (shown to AI when creating artifacts)
|
|
4
|
+
context: |
|
|
5
|
+
You are working in a project generated from the AgentDock web-nextjs template.
|
|
6
|
+
|
|
7
|
+
Tech stack: Next.js 16 (App Router), React 19, TypeScript 5+ (strict), Tailwind CSS,
|
|
8
|
+
Supabase (@supabase/ssr), next-intl (i18n), Vitest (unit tests), pnpm ≥9, Node ≥18.
|
|
9
|
+
|
|
10
|
+
Four-layer directory contract (enforced by ESLint Layer 2 rules at error severity):
|
|
11
|
+
src/core/ — domain types + repository interfaces (no framework deps, read-only)
|
|
12
|
+
src/features/ — AI coding zone; each feature needs __contract__.ts + index.ts + tests
|
|
13
|
+
src/infra/ — Supabase implementation (requires human approval before editing)
|
|
14
|
+
src/app/ — Next.js App Router pages and layouts
|
|
15
|
+
|
|
16
|
+
Governance rules:
|
|
17
|
+
- Conventional commits: type(scope): summary
|
|
18
|
+
- pnpm only — no npm/yarn
|
|
19
|
+
- TypeScript strict — no any, no @ts-ignore without explanation
|
|
20
|
+
- Features must not import from infra/db directly (use core/repositories interfaces)
|
|
21
|
+
- Every feature directory must have __contract__.ts
|
|
22
|
+
|
|
23
|
+
Reference feature: src/features/hello/ (complete example of the four-layer pattern)
|
|
24
|
+
|
|
25
|
+
# Workflow rules
|
|
26
|
+
rules:
|
|
27
|
+
proposal:
|
|
28
|
+
- "Proposal MUST contain a Non-goals section listing what this change will NOT implement."
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cogito.ai/template-web-nextjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Full-stack Next.js 16 starter with Supabase, next-intl, Tailwind CSS and Vitest",
|
|
5
|
+
"private": true,
|
|
6
|
+
"engines": {
|
|
7
|
+
"pnpm": ">=9",
|
|
8
|
+
"node": ">=18"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "next dev",
|
|
12
|
+
"build": "next build",
|
|
13
|
+
"start": "next start",
|
|
14
|
+
"lint": "eslint .",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"check-types": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@supabase/ssr": "^0.10.3",
|
|
20
|
+
"next": "16",
|
|
21
|
+
"next-intl": "^4.13.0",
|
|
22
|
+
"react": "19",
|
|
23
|
+
"react-dom": "19"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
27
|
+
"tailwindcss": "^4.3.0",
|
|
28
|
+
"typescript": "^6.0.3",
|
|
29
|
+
"vitest": "^4.1.8",
|
|
30
|
+
"@cogito.ai/eslint-config": "workspace:*",
|
|
31
|
+
"@cogito.ai/tsconfig": "workspace:*",
|
|
32
|
+
"@fission-ai/openspec": "^1.3.1"
|
|
33
|
+
},
|
|
34
|
+
"agentdock": {
|
|
35
|
+
"minCliVersion": "0.1.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getTranslations } from 'next-intl/server'
|
|
2
|
+
import { greet } from '@/features/hello'
|
|
3
|
+
import type { Greeting } from '@/core/types/greeting'
|
|
4
|
+
|
|
5
|
+
async function loadRecentGreetings(): Promise<Greeting[]> {
|
|
6
|
+
// Gracefully skip DB access when Supabase is not configured.
|
|
7
|
+
if (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) {
|
|
8
|
+
return []
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const { SupabaseGreetingRepository } = await import('@/infra/db/SupabaseGreetingRepository')
|
|
13
|
+
const repo = new SupabaseGreetingRepository()
|
|
14
|
+
return await repo.findRecent()
|
|
15
|
+
} catch {
|
|
16
|
+
return []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default async function HelloPage() {
|
|
21
|
+
const t = await getTranslations('hello')
|
|
22
|
+
const greeting = greet('World')
|
|
23
|
+
const recentGreetings = await loadRecentGreetings()
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
27
|
+
<h1 className="text-4xl font-bold">{t('title')}</h1>
|
|
28
|
+
<p className="mt-4 text-xl text-gray-700">{greeting}</p>
|
|
29
|
+
<p className="mt-2 text-gray-500">{t('greeting', { name: 'World' })}</p>
|
|
30
|
+
|
|
31
|
+
{recentGreetings.length > 0 && (
|
|
32
|
+
<section className="mt-8">
|
|
33
|
+
<h2 className="text-lg font-semibold">Recent greetings</h2>
|
|
34
|
+
<ul className="mt-2 space-y-1">
|
|
35
|
+
{recentGreetings.map((g) => (
|
|
36
|
+
<li key={g.id} className="text-sm text-gray-600">
|
|
37
|
+
{g.name} — {g.createdAt}
|
|
38
|
+
</li>
|
|
39
|
+
))}
|
|
40
|
+
</ul>
|
|
41
|
+
</section>
|
|
42
|
+
)}
|
|
43
|
+
</main>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Metadata } from 'next'
|
|
2
|
+
import { NextIntlClientProvider } from 'next-intl'
|
|
3
|
+
import { getMessages } from 'next-intl/server'
|
|
4
|
+
import { notFound } from 'next/navigation'
|
|
5
|
+
import './globals.css'
|
|
6
|
+
|
|
7
|
+
const locales = ['en', 'zh'] as const
|
|
8
|
+
type Locale = (typeof locales)[number]
|
|
9
|
+
|
|
10
|
+
export const metadata: Metadata = {
|
|
11
|
+
title: 'AgentDock Web Template',
|
|
12
|
+
description: 'Generated by AgentDock scaffold platform',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default async function LocaleLayout({
|
|
16
|
+
children,
|
|
17
|
+
params,
|
|
18
|
+
}: {
|
|
19
|
+
children: React.ReactNode
|
|
20
|
+
params: Promise<{ locale: string }>
|
|
21
|
+
}) {
|
|
22
|
+
const { locale } = await params
|
|
23
|
+
|
|
24
|
+
if (!locales.includes(locale as Locale)) {
|
|
25
|
+
notFound()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const messages = await getMessages()
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<html lang={locale}>
|
|
32
|
+
<body>
|
|
33
|
+
<NextIntlClientProvider messages={messages}>{children}</NextIntlClientProvider>
|
|
34
|
+
</body>
|
|
35
|
+
</html>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useTranslations } from 'next-intl'
|
|
2
|
+
|
|
3
|
+
export default function HomePage() {
|
|
4
|
+
const t = useTranslations('home')
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
8
|
+
<h1 className="text-4xl font-bold">{t('title')}</h1>
|
|
9
|
+
<p className="mt-4 text-gray-600">{t('description')}</p>
|
|
10
|
+
</main>
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Greeting } from '../types/greeting'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Repository interface for Greeting persistence.
|
|
5
|
+
* Uses domain types only — no Supabase types leak through.
|
|
6
|
+
* Implement this in src/infra/db/ to swap storage backends.
|
|
7
|
+
*/
|
|
8
|
+
export interface IGreetingRepository {
|
|
9
|
+
/** Persist a new greeting and return the saved record. */
|
|
10
|
+
save(name: string): Promise<Greeting>
|
|
11
|
+
|
|
12
|
+
/** Return the most recent greeting records. */
|
|
13
|
+
findRecent(): Promise<Greeting[]>
|
|
14
|
+
}
|