@ai-support-agent/cli 0.1.32 → 0.1.33-beta.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.
- package/dist/alert-processor.d.ts +0 -8
- package/dist/alert-processor.d.ts.map +1 -1
- package/dist/alert-processor.js +5 -73
- package/dist/alert-processor.js.map +1 -1
- package/dist/api-client.d.ts +0 -4
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +0 -4
- package/dist/api-client.js.map +1 -1
- package/dist/commands/claude-code-args.d.ts +1 -0
- package/dist/commands/claude-code-args.d.ts.map +1 -1
- package/dist/commands/claude-code-args.js +3 -0
- package/dist/commands/claude-code-args.js.map +1 -1
- package/dist/commands/claude-code-runner.d.ts.map +1 -1
- package/dist/commands/claude-code-runner.js +2 -1
- package/dist/commands/claude-code-runner.js.map +1 -1
- package/dist/commands/plugin-dir.d.ts +20 -0
- package/dist/commands/plugin-dir.d.ts.map +1 -0
- package/dist/commands/plugin-dir.js +71 -0
- package/dist/commands/plugin-dir.js.map +1 -0
- package/dist/constants.d.ts +0 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +0 -1
- package/dist/constants.js.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +9 -0
- package/dist/plugin/LICENSE +26 -0
- package/dist/plugin/README.md +86 -0
- package/dist/plugin/SYNC.md +113 -0
- package/dist/plugin/agents/build-error-resolver.md +159 -0
- package/dist/plugin/agents/code-reviewer.md +277 -0
- package/dist/plugin/agents/django-reviewer.md +159 -0
- package/dist/plugin/agents/infra-reviewer.md +172 -0
- package/dist/plugin/agents/investigator.md +75 -0
- package/dist/plugin/agents/nextjs-reviewer.md +191 -0
- package/dist/plugin/agents/php-reviewer.md +167 -0
- package/dist/plugin/agents/planner.md +184 -0
- package/dist/plugin/agents/python-reviewer.md +161 -0
- package/dist/plugin/agents/react-reviewer.md +150 -0
- package/dist/plugin/agents/silent-failure-hunter.md +158 -0
- package/dist/plugin/agents/typescript-reviewer.md +179 -0
- package/dist/plugin/agents/ui-reviewer.md +203 -0
- package/dist/plugin/commands/add-feature.md +301 -0
- package/dist/plugin/commands/build-fix.md +47 -0
- package/dist/plugin/commands/code-review.md +228 -0
- package/dist/plugin/commands/fix-defect.md +393 -0
- package/dist/plugin/commands/learn-eval.md +94 -0
- package/dist/plugin/commands/learn.md +84 -0
- package/dist/plugin/commands/plan.md +211 -0
- package/dist/plugin/commands/test-coverage.md +64 -0
- package/dist/plugin/commands/update-docs.md +98 -0
- package/dist/plugin/hooks/hooks.json +59 -0
- package/dist/plugin/hooks/scripts/auto-format.sh +63 -0
- package/dist/plugin/hooks/scripts/check-secrets-before-commit.sh +50 -0
- package/dist/plugin/hooks/scripts/guard-dangerous-commands.sh +55 -0
- package/dist/plugin/hooks/scripts/on-command-resume.sh +112 -0
- package/dist/plugin/hooks/scripts/on-command-stop.sh +200 -0
- package/dist/plugin/hooks/scripts/protect-sensitive-files.sh +58 -0
- package/dist/plugin/rules/common/coding-guidelines.md +73 -0
- package/dist/plugin/rules/documentation/api-docs.md +46 -0
- package/dist/plugin/rules/documentation/docs-site.md +60 -0
- package/dist/plugin/rules/documentation/source-docs.md +89 -0
- package/dist/plugin/rules/documentation/test-docs.md +39 -0
- package/dist/plugin/rules/logging/logging-rules.md +83 -0
- package/dist/plugin/rules/php/coding-rules.md +40 -0
- package/dist/plugin/rules/python/coding-rules.md +40 -0
- package/dist/plugin/rules/typescript/coding-rules.md +45 -0
- package/dist/plugin/skills/api-design/SKILL.md +269 -0
- package/dist/plugin/skills/backend-patterns/SKILL.md +312 -0
- package/dist/plugin/skills/database-migrations/SKILL.md +323 -0
- package/dist/plugin/skills/docker-patterns/SKILL.md +308 -0
- package/dist/plugin/skills/docs-site/SKILL.md +341 -0
- package/dist/plugin/skills/e2e-testing/SKILL.md +334 -0
- package/dist/plugin/skills/frontend-patterns/SKILL.md +318 -0
- package/dist/plugin/skills/integration-testing/SKILL.md +273 -0
- package/package.json +2 -2
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docker-patterns
|
|
3
|
+
description: A collection of Docker / Docker Compose best practices covering multi-stage builds, layer caching, image size reduction, security, healthchecks, and Compose-based dev environment setup. Reference this when creating, modifying, or reviewing a Dockerfile or docker-compose.yml.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Docker Patterns
|
|
7
|
+
|
|
8
|
+
A skill that captures the decision criteria for writing Dockerfiles and Docker Compose files.
|
|
9
|
+
When starting from scratch, work through this order: base image selection → stage separation → cache optimization → security → healthchecks.
|
|
10
|
+
|
|
11
|
+
## Multi-Stage Builds
|
|
12
|
+
|
|
13
|
+
Separate the build stage from the runtime stage so that build-only tooling (compilers, devDependencies, the Composer binary, etc.) never ends up in the runtime image.
|
|
14
|
+
|
|
15
|
+
Benefits:
|
|
16
|
+
- The runtime image is smaller, which speeds up distribution and deployment
|
|
17
|
+
- Vulnerabilities originating from build tools don't linger in the runtime environment
|
|
18
|
+
- Source code and intermediate build artifacts can't leak into the final image
|
|
19
|
+
|
|
20
|
+
### Node.js example
|
|
21
|
+
|
|
22
|
+
```dockerfile
|
|
23
|
+
# Good: use devDependencies in the build stage, hand only the build output to runtime
|
|
24
|
+
FROM node:22-slim AS build
|
|
25
|
+
WORKDIR /app
|
|
26
|
+
COPY package.json package-lock.json ./
|
|
27
|
+
RUN npm ci
|
|
28
|
+
COPY . .
|
|
29
|
+
RUN npm run build
|
|
30
|
+
|
|
31
|
+
FROM node:22-slim AS runtime
|
|
32
|
+
WORKDIR /app
|
|
33
|
+
ENV NODE_ENV=production
|
|
34
|
+
COPY package.json package-lock.json ./
|
|
35
|
+
RUN npm ci --omit=dev
|
|
36
|
+
COPY --from=build /app/dist ./dist
|
|
37
|
+
USER node
|
|
38
|
+
CMD ["node", "dist/main.js"]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Python example
|
|
42
|
+
|
|
43
|
+
```dockerfile
|
|
44
|
+
# Good: copy only the installed dependency output into the runtime stage
|
|
45
|
+
FROM python:3.12-slim AS build
|
|
46
|
+
WORKDIR /app
|
|
47
|
+
RUN apt-get update && apt-get install -y --no-install-recommends build-essential \
|
|
48
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
49
|
+
COPY requirements.txt ./
|
|
50
|
+
RUN pip install --prefix=/install --no-cache-dir -r requirements.txt
|
|
51
|
+
|
|
52
|
+
FROM python:3.12-slim AS runtime
|
|
53
|
+
WORKDIR /app
|
|
54
|
+
COPY --from=build /install /usr/local
|
|
55
|
+
COPY . .
|
|
56
|
+
RUN useradd --create-home --shell /usr/sbin/nologin appuser
|
|
57
|
+
USER appuser
|
|
58
|
+
CMD ["python", "-m", "app"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Keep compiler packages such as build-essential confined to the build stage. Only bring the artifacts installed via `--prefix` into the runtime stage.
|
|
62
|
+
|
|
63
|
+
### PHP-FPM example
|
|
64
|
+
|
|
65
|
+
```dockerfile
|
|
66
|
+
# Good: resolve dependencies in a composer stage, ship only vendor/ into the runtime image
|
|
67
|
+
FROM composer:2 AS vendor
|
|
68
|
+
WORKDIR /app
|
|
69
|
+
COPY composer.json composer.lock ./
|
|
70
|
+
RUN composer install --no-dev --prefer-dist --no-scripts --no-interaction
|
|
71
|
+
|
|
72
|
+
FROM php:8.3-fpm AS runtime
|
|
73
|
+
WORKDIR /var/www/html
|
|
74
|
+
RUN docker-php-ext-install pdo_mysql opcache
|
|
75
|
+
COPY --from=vendor /app/vendor ./vendor
|
|
76
|
+
COPY . .
|
|
77
|
+
RUN chown -R www-data:www-data /var/www/html
|
|
78
|
+
USER www-data
|
|
79
|
+
CMD ["php-fpm"]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This keeps the (tens-of-MB) Composer binary and any dev-only dependencies excluded via `--no-dev` out of the runtime image.
|
|
83
|
+
|
|
84
|
+
## Layer Caching
|
|
85
|
+
|
|
86
|
+
Docker caches layers individually and re-executes every layer from the point where the copied content first changes. Dependency manifests (lock files) change far less often than source code, so copy them in on their own first, letting the dependency-install step ride the cache.
|
|
87
|
+
|
|
88
|
+
```dockerfile
|
|
89
|
+
# Bad: even a one-line source change forces npm ci to run every time
|
|
90
|
+
COPY . .
|
|
91
|
+
RUN npm ci
|
|
92
|
+
|
|
93
|
+
# Good: npm ci stays cached as long as the lock file doesn't change
|
|
94
|
+
COPY package.json package-lock.json ./
|
|
95
|
+
RUN npm ci
|
|
96
|
+
COPY . .
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Files to copy first, by language:
|
|
100
|
+
- Node.js: package.json and package-lock.json (or pnpm-lock.yaml / yarn.lock)
|
|
101
|
+
- Python: requirements.txt (or pyproject.toml plus poetry.lock / uv.lock)
|
|
102
|
+
- PHP: composer.json and composer.lock
|
|
103
|
+
|
|
104
|
+
Additional notes:
|
|
105
|
+
- Always combine `apt-get update` and `apt-get install` into a single `RUN`. Splitting them lets a stale package-list layer get reused, causing the install to fail or pull outdated versions.
|
|
106
|
+
- Place instructions that change less often earlier in the Dockerfile.
|
|
107
|
+
|
|
108
|
+
## Reducing Image Size
|
|
109
|
+
|
|
110
|
+
### Choosing between slim and alpine
|
|
111
|
+
|
|
112
|
+
- slim (Debian-based): the default choice. glibc-compatible, so native extensions and prebuilt binaries work out of the box.
|
|
113
|
+
- alpine (musl-based): reserve this for when you truly need the smallest possible size. Watch out for:
|
|
114
|
+
- Prebuilt binaries that assume glibc may not run under musl libc
|
|
115
|
+
- Python loses access to manylinux wheels, forcing a source build that's slower and often needs extras like build-base
|
|
116
|
+
- Node.js native modules (sharp, bcrypt, etc.) may also need rebuilding
|
|
117
|
+
- Subtle behavioral differences (e.g., DNS resolution) that are hard to debug
|
|
118
|
+
|
|
119
|
+
When in doubt, choose slim. Reserve alpine for cases with no native dependencies, or where image size constraints are strict.
|
|
120
|
+
|
|
121
|
+
### .dockerignore
|
|
122
|
+
|
|
123
|
+
Keeps the build context small and prevents unwanted or sensitive files from leaking into the image. Always place one at the project root.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
# baseline .dockerignore
|
|
127
|
+
.git
|
|
128
|
+
node_modules
|
|
129
|
+
vendor
|
|
130
|
+
__pycache__
|
|
131
|
+
*.log
|
|
132
|
+
.env
|
|
133
|
+
.env.*
|
|
134
|
+
dist
|
|
135
|
+
coverage
|
|
136
|
+
Dockerfile
|
|
137
|
+
docker-compose*.yml
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Without a `.dockerignore`, a Dockerfile using `COPY . .` will bake `.env` and `.git` straight into the image.
|
|
141
|
+
|
|
142
|
+
## Security
|
|
143
|
+
|
|
144
|
+
### Run as a non-root user
|
|
145
|
+
|
|
146
|
+
Running the container process as root widens the blast radius if a vulnerability is exploited.
|
|
147
|
+
|
|
148
|
+
```dockerfile
|
|
149
|
+
# Bad: no USER directive (runs as root)
|
|
150
|
+
CMD ["node", "server.js"]
|
|
151
|
+
|
|
152
|
+
# Good: use the user bundled with the official image (node for Node.js, www-data for php-fpm)
|
|
153
|
+
USER node
|
|
154
|
+
CMD ["node", "server.js"]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
For images that don't ship a ready-made user, create one with `useradd` (see the Python example above). `chown` any directories that need write access before switching `USER`.
|
|
158
|
+
|
|
159
|
+
### Never bake secrets into the image
|
|
160
|
+
|
|
161
|
+
Secrets passed via ARG / ENV / COPY can be recovered from `docker history` or by inspecting layers. Removing a file with `rm` in a later layer does not remove it from earlier layers.
|
|
162
|
+
|
|
163
|
+
```dockerfile
|
|
164
|
+
# Bad: the build-arg value persists in the image history
|
|
165
|
+
ARG NPM_TOKEN
|
|
166
|
+
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc \
|
|
167
|
+
&& npm ci && rm .npmrc
|
|
168
|
+
|
|
169
|
+
# Good: BuildKit secret mounts never land in a layer
|
|
170
|
+
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Pass the secret at build time with something like `docker build --secret id=npmrc,src=$HOME/.npmrc .`. For runtime secrets, use environment variables (Compose's `env_file`) or a secrets-management backend, and add `.env` files to both `.dockerignore` and `.gitignore`.
|
|
174
|
+
|
|
175
|
+
### Pin versions (never use `latest`)
|
|
176
|
+
|
|
177
|
+
```dockerfile
|
|
178
|
+
# Bad: content changes depending on when the image is pulled, breaking reproducibility
|
|
179
|
+
FROM node:latest
|
|
180
|
+
FROM python
|
|
181
|
+
|
|
182
|
+
# Good: pin at least major.minor
|
|
183
|
+
FROM node:22-slim
|
|
184
|
+
FROM python:3.12-slim
|
|
185
|
+
|
|
186
|
+
# Good: pin by digest when you need strict reproducibility
|
|
187
|
+
FROM node:22-slim@sha256:<digest>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Pin versions for apt/pip packages too whenever reproducibility matters.
|
|
191
|
+
|
|
192
|
+
## Healthchecks
|
|
193
|
+
|
|
194
|
+
### Dockerfile HEALTHCHECK
|
|
195
|
+
|
|
196
|
+
```dockerfile
|
|
197
|
+
# Good: poll the app's health endpoint on a schedule
|
|
198
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
199
|
+
CMD curl -fsS http://localhost:3000/healthz || exit 1
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
On slim-family images that lack `curl`, fall back to `wget` or a check written in the language runtime itself (e.g. `CMD ["node", "healthcheck.js"]`).
|
|
203
|
+
|
|
204
|
+
### Compose healthcheck and depends_on
|
|
205
|
+
|
|
206
|
+
By default, `depends_on` only guarantees *startup order*, not readiness. To wait until a database is actually ready to accept connections, combine `healthcheck` with a `condition`.
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
services:
|
|
210
|
+
db:
|
|
211
|
+
image: postgres:16
|
|
212
|
+
healthcheck:
|
|
213
|
+
test: ["CMD-SHELL", "pg_isready -U app"]
|
|
214
|
+
interval: 5s
|
|
215
|
+
timeout: 3s
|
|
216
|
+
retries: 10
|
|
217
|
+
start_period: 10s
|
|
218
|
+
|
|
219
|
+
api:
|
|
220
|
+
build: .
|
|
221
|
+
depends_on:
|
|
222
|
+
db:
|
|
223
|
+
condition: service_healthy # don't start api until db is healthy
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Condition types:
|
|
227
|
+
- `service_started`: wait only for the container to start (default)
|
|
228
|
+
- `service_healthy`: wait until the healthcheck succeeds
|
|
229
|
+
- `service_completed_successfully`: wait for a one-shot task (e.g. a migration) to exit successfully
|
|
230
|
+
|
|
231
|
+
## Compose-Based Development Environments
|
|
232
|
+
|
|
233
|
+
### Choosing between volume types
|
|
234
|
+
|
|
235
|
+
- Named volumes: for persistent data you want to survive container rebuilds, such as database data
|
|
236
|
+
- Bind mounts: for things like source code, where you want host-side edits reflected immediately
|
|
237
|
+
|
|
238
|
+
```yaml
|
|
239
|
+
services:
|
|
240
|
+
api:
|
|
241
|
+
build: .
|
|
242
|
+
volumes:
|
|
243
|
+
- .:/app # source via bind mount (for hot reload)
|
|
244
|
+
- /app/node_modules # don't let the host's node_modules overwrite the container's
|
|
245
|
+
db:
|
|
246
|
+
image: postgres:16
|
|
247
|
+
volumes:
|
|
248
|
+
- db-data:/var/lib/postgresql/data # data via named volume
|
|
249
|
+
|
|
250
|
+
volumes:
|
|
251
|
+
db-data:
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Override files
|
|
255
|
+
|
|
256
|
+
Keep the base definition in `docker-compose.yml`, and isolate dev-only differences (bind mounts, debug ports, dev commands) in `docker-compose.override.yml`. Since `docker compose up` merges the override file automatically, production and CI should explicitly pass `-f docker-compose.yml` only, excluding the dev-only overrides.
|
|
257
|
+
|
|
258
|
+
### Bind ports to localhost
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
# Bad: exposed on all interfaces, reachable from the LAN or beyond
|
|
262
|
+
ports:
|
|
263
|
+
- "5432:5432"
|
|
264
|
+
|
|
265
|
+
# Good: bind a dev database to the loopback interface only
|
|
266
|
+
ports:
|
|
267
|
+
- "127.0.0.1:5432:5432"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
For services the host never needs to reach directly, skip `ports` entirely and rely on container-to-container communication (via service-name resolution).
|
|
271
|
+
|
|
272
|
+
### Network isolation
|
|
273
|
+
|
|
274
|
+
Minimize the reachable surface. Put only the reverse proxy on the outward-facing network, and keep the database reachable solely from the app.
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
services:
|
|
278
|
+
proxy:
|
|
279
|
+
networks: [frontend]
|
|
280
|
+
api:
|
|
281
|
+
networks: [frontend, backend]
|
|
282
|
+
db:
|
|
283
|
+
networks: [backend] # unreachable from proxy
|
|
284
|
+
|
|
285
|
+
networks:
|
|
286
|
+
frontend:
|
|
287
|
+
backend:
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Anti-Patterns
|
|
291
|
+
|
|
292
|
+
- Specifying a base image as `FROM xxx:latest` (breaks reproducibility)
|
|
293
|
+
- Writing `COPY . .` before the dependency-install step (invalidates the cache on every build)
|
|
294
|
+
- Running `COPY . .` without a `.dockerignore` (leaks `.git` or `.env` into the image)
|
|
295
|
+
- Passing secrets via ARG / ENV (recoverable from `docker history`)
|
|
296
|
+
- Running the app as root because `USER` was never set
|
|
297
|
+
- Splitting `apt-get update` and `apt-get install` into separate `RUN` instructions
|
|
298
|
+
- Leaving the apt cache (`/var/lib/apt/lists`) or pip cache in the image instead of clearing it
|
|
299
|
+
- Cramming multiple processes into one container (app + cron + nginx, etc.)
|
|
300
|
+
- Assuming `depends_on` alone guarantees the database is ready (no `condition` specified)
|
|
301
|
+
- Exposing a dev database port on `0.0.0.0`
|
|
302
|
+
- Reaching for alpine without considering native extension compatibility
|
|
303
|
+
- Writing logs to a file instead of stdout/stderr (let the runtime handle log collection)
|
|
304
|
+
- Running `git clone` inside the image instead of `COPY`-ing through the build context
|
|
305
|
+
|
|
306
|
+
## Related
|
|
307
|
+
|
|
308
|
+
- Use `/code-review` when reviewing Dockerfile / Compose file changes
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docs-site
|
|
3
|
+
description: A pattern library for building and operating a documentation site with Docusaurus. Covers i18n (ja/en) structure, automatic OpenAPI generation from NestJS, wiring OpenAPI/TypeDoc output into Docusaurus, type and client generation with openapi-typescript / orval, templates for hand-written reference pages, and CI freshness checks. Use this when setting up a new documentation site, adding pages, updating API references, syncing translations, or building a type-generation pipeline.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# docs-site: Docusaurus documentation site patterns
|
|
7
|
+
|
|
8
|
+
This skill is a pattern library focused on "how to build it." The authoritative source for the conventions themselves lives in `rules/documentation/` (aimed at repository maintainers); this skill assumes those conventions are already settled and provides the implementation steps.
|
|
9
|
+
|
|
10
|
+
## Baseline conventions assumed here
|
|
11
|
+
|
|
12
|
+
- Locales are `ja` (default) + `en`. Hand-written guides must exist in both languages. Auto-generated reference pages exist only in the default locale.
|
|
13
|
+
- NestJS APIs are documented by auto-generating from OpenAPI (hand-transcribing is not allowed). For TS libraries, prefer TypeDoc auto-generation. Python / PHP are handled with hand-written templates plus a consistency check.
|
|
14
|
+
- Hand-written reference pages must include a `source: src/path/file.ts#functionName` key in the frontmatter.
|
|
15
|
+
- Links to tests use a repository-absolute URL anchored to the default branch (never pin to a commit SHA).
|
|
16
|
+
|
|
17
|
+
## 1. Basic Docusaurus i18n setup
|
|
18
|
+
|
|
19
|
+
### docusaurus.config.js
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// docusaurus.config.js (excerpt)
|
|
23
|
+
module.exports = {
|
|
24
|
+
title: 'Docs',
|
|
25
|
+
url: 'https://docs.example.com',
|
|
26
|
+
baseUrl: '/',
|
|
27
|
+
i18n: {
|
|
28
|
+
defaultLocale: 'ja', // default locale is ja
|
|
29
|
+
locales: ['ja', 'en'],
|
|
30
|
+
},
|
|
31
|
+
themeConfig: {
|
|
32
|
+
navbar: {
|
|
33
|
+
// always include a locale-switcher dropdown
|
|
34
|
+
items: [{ type: 'localeDropdown', position: 'right' }],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Directory structure
|
|
41
|
+
|
|
42
|
+
The source-of-truth content for the default locale (ja) lives under `docs/`, and the en translation mirrors the same structure under `i18n/en/`.
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
docs/
|
|
46
|
+
guides/getting-started.md # ja original (source of truth)
|
|
47
|
+
i18n/
|
|
48
|
+
en/
|
|
49
|
+
docusaurus-plugin-content-docs/
|
|
50
|
+
current/
|
|
51
|
+
guides/getting-started.md # en translation (same relative path as docs/)
|
|
52
|
+
code.json # translations for UI strings
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Translation workflow
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 1. Generate translation files for UI strings / sidebar labels (for en)
|
|
59
|
+
npm run write-translations -- --locale en
|
|
60
|
+
|
|
61
|
+
# 2. Place the en translation of a hand-written guide under i18n/en/.../current/
|
|
62
|
+
# at the same relative path, then translate it
|
|
63
|
+
cp docs/guides/getting-started.md \
|
|
64
|
+
i18n/en/docusaurus-plugin-content-docs/current/guides/getting-started.md
|
|
65
|
+
|
|
66
|
+
# 3. Run the dev server per locale to check it (dev server serves one locale at a time)
|
|
67
|
+
npm run start -- --locale en
|
|
68
|
+
|
|
69
|
+
# 4. A production build verifies all locales together
|
|
70
|
+
npm run build
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Whenever the original (ja) content is updated, update the en translation in the same PR. Auto-generated reference pages (from OpenAPI / TypeDoc) exist only in the default locale — do not copy them into `i18n/en/`.
|
|
74
|
+
|
|
75
|
+
## 2. Generating OpenAPI output from NestJS
|
|
76
|
+
|
|
77
|
+
Prepare a generation-only script that doesn't depend on the server actually listening. It's important that this can run in CI without opening a port.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
// scripts/generate-openapi.ts
|
|
81
|
+
import { NestFactory } from '@nestjs/core';
|
|
82
|
+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
83
|
+
import { writeFileSync } from 'fs';
|
|
84
|
+
import { AppModule } from '../src/app.module';
|
|
85
|
+
|
|
86
|
+
async function generate() {
|
|
87
|
+
// Build only the application context, without calling listen()
|
|
88
|
+
const app = await NestFactory.create(AppModule, { logger: false });
|
|
89
|
+
|
|
90
|
+
const config = new DocumentBuilder()
|
|
91
|
+
.setTitle('API').setVersion('1.0.0').addBearerAuth()
|
|
92
|
+
.build();
|
|
93
|
+
|
|
94
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
95
|
+
writeFileSync('openapi.json', JSON.stringify(document, null, 2));
|
|
96
|
+
|
|
97
|
+
await app.close();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
generate();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```jsonc
|
|
104
|
+
// package.json (excerpt)
|
|
105
|
+
{ "scripts": { "openapi:generate": "ts-node scripts/generate-openapi.ts" } }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Enabling the `@nestjs/swagger` CLI plugin lets it auto-infer `@ApiProperty` from DTO properties, which reduces missing-decorator mistakes.
|
|
109
|
+
|
|
110
|
+
```jsonc
|
|
111
|
+
// nest-cli.json
|
|
112
|
+
{
|
|
113
|
+
"sourceRoot": "src",
|
|
114
|
+
"compilerOptions": {
|
|
115
|
+
"plugins": [
|
|
116
|
+
{ "name": "@nestjs/swagger", "options": { "introspectComments": true } }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 3. Pulling OpenAPI into Docusaurus
|
|
123
|
+
|
|
124
|
+
Use `docusaurus-plugin-openapi-docs` to generate MDX pages from `openapi.json`.
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
// docusaurus.config.js (excerpt)
|
|
128
|
+
module.exports = {
|
|
129
|
+
plugins: [
|
|
130
|
+
[
|
|
131
|
+
'docusaurus-plugin-openapi-docs',
|
|
132
|
+
{
|
|
133
|
+
id: 'api',
|
|
134
|
+
docsPluginId: 'classic',
|
|
135
|
+
config: {
|
|
136
|
+
myApi: {
|
|
137
|
+
specPath: 'openapi.json', // the generated spec
|
|
138
|
+
outputDir: 'docs/api', // output location (default locale only)
|
|
139
|
+
sidebarOptions: {
|
|
140
|
+
groupPathsBy: 'tag', // group by tag
|
|
141
|
+
categoryLinkSource: 'tag',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
],
|
|
148
|
+
themes: ['docusaurus-theme-openapi-docs'],
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
// sidebars.js (excerpt): pull in the generated sidebar
|
|
154
|
+
module.exports = {
|
|
155
|
+
docs: [
|
|
156
|
+
{ type: 'category', label: 'Guides', items: [{ type: 'autogenerated', dirName: 'guides' }] },
|
|
157
|
+
{ type: 'category', label: 'API Reference', items: require('./docs/api/sidebar.js') },
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Regenerate (run this every time the API changes; clear old pages before rebuilding)
|
|
164
|
+
npm run openapi:generate # in the API repository
|
|
165
|
+
npx docusaurus clean-api-docs myApi # remove existing generated output
|
|
166
|
+
npx docusaurus gen-api-docs myApi # regenerate
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The generated `docs/api/` content is served only in the default locale (the ja site) — do not create a translated copy under `i18n/en/`, and do not hand-edit it.
|
|
170
|
+
|
|
171
|
+
## 4. Generating Next.js types and clients from OpenAPI
|
|
172
|
+
|
|
173
|
+
Use `openapi-typescript` if you only need types, or `orval` if you also want a fetch client. In both cases, standardize the output location as `src/generated/`.
|
|
174
|
+
|
|
175
|
+
```jsonc
|
|
176
|
+
// package.json (excerpt): generate types only
|
|
177
|
+
{ "scripts": { "codegen:types": "openapi-typescript ../api/openapi.json -o src/generated/api-types.ts" } }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
// orval.config.ts: generate both types and client
|
|
182
|
+
import { defineConfig } from 'orval';
|
|
183
|
+
|
|
184
|
+
export default defineConfig({
|
|
185
|
+
myApi: {
|
|
186
|
+
input: '../api/openapi.json',
|
|
187
|
+
output: {
|
|
188
|
+
target: 'src/generated/api-client.ts',
|
|
189
|
+
client: 'fetch', // use 'react-query' if you're using React Query
|
|
190
|
+
clean: true, // remove stale files on regeneration
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Enforce the "no hand-editing" rule with a two-pronged approach: exclude generated files from lint, and detect regeneration drift in CI (see section 7).
|
|
197
|
+
|
|
198
|
+
```jsonc
|
|
199
|
+
// .eslintrc.json (excerpt): don't lint generated output — this signals it isn't meant to be hand-edited
|
|
200
|
+
{
|
|
201
|
+
"ignorePatterns": ["src/generated/**"]
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`src/generated/` should still be committed (so reviewers can see the diff), but a PR containing manual edits to it will fail the CI freshness check.
|
|
206
|
+
|
|
207
|
+
## 5. Pulling TypeDoc into Docusaurus
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
// docusaurus.config.js (excerpt)
|
|
211
|
+
module.exports = {
|
|
212
|
+
plugins: [
|
|
213
|
+
[
|
|
214
|
+
'docusaurus-plugin-typedoc',
|
|
215
|
+
{
|
|
216
|
+
entryPoints: ['../packages/sdk/src/index.ts'],
|
|
217
|
+
tsconfig: '../packages/sdk/tsconfig.json',
|
|
218
|
+
out: 'docs/sdk', // output location (default locale only)
|
|
219
|
+
sidebar: { autoConfiguration: true },
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
// sidebars.js (excerpt): pull in the sidebar generated by TypeDoc
|
|
228
|
+
module.exports = {
|
|
229
|
+
docs: [
|
|
230
|
+
{ type: 'category', label: 'SDK Reference', items: require('./docs/sdk/typedoc-sidebar.cjs') },
|
|
231
|
+
],
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Embed links to tests via `@see` in TSDoc comments. Links should use an absolute URL anchored to the default branch (never pin to a SHA).
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
/**
|
|
239
|
+
* Calculates the order total including tax.
|
|
240
|
+
* @param items - array of order line items
|
|
241
|
+
* @see {@link https://github.com/example-org/sdk/blob/main/test/order.spec.ts | order.spec.ts test cases}
|
|
242
|
+
*/
|
|
243
|
+
export function calcOrderTotal(items: OrderItem[]): number { /* ... */ }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 6. Full template for a hand-written function reference
|
|
247
|
+
|
|
248
|
+
For languages such as Python / PHP where auto-generation isn't available. The `source` frontmatter key is required so the consistency check (`/update-docs`) can trace back to the implementation.
|
|
249
|
+
|
|
250
|
+
```markdown
|
|
251
|
+
---
|
|
252
|
+
id: calc-order-total
|
|
253
|
+
title: calc_order_total
|
|
254
|
+
source: src/services/order.py#calc_order_total
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Overview
|
|
258
|
+
|
|
259
|
+
Calculates the tax-inclusive total from an array of order line items.
|
|
260
|
+
|
|
261
|
+
## Signature
|
|
262
|
+
|
|
263
|
+
\`\`\`python
|
|
264
|
+
def calc_order_total(items: list[OrderItem], tax_rate: Decimal = Decimal("0.10")) -> int
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
## Parameters
|
|
268
|
+
|
|
269
|
+
| Name | Type | Required | Description |
|
|
270
|
+
| --- | --- | --- | --- |
|
|
271
|
+
| items | list[OrderItem] | Yes | Array of order line items. An empty array is allowed |
|
|
272
|
+
| tax_rate | Decimal | No | Tax rate. Defaults to 0.10 |
|
|
273
|
+
|
|
274
|
+
## Output
|
|
275
|
+
|
|
276
|
+
The tax-inclusive total (in yen, as an int). Returns 0 for an empty array.
|
|
277
|
+
|
|
278
|
+
## Errors
|
|
279
|
+
|
|
280
|
+
| Exception | Code | Condition |
|
|
281
|
+
| --- | --- | --- |
|
|
282
|
+
| ValueError | E_NEGATIVE_PRICE | A line item has a negative unit price |
|
|
283
|
+
| ValueError | E_INVALID_TAX_RATE | tax_rate is less than 0 or greater than or equal to 1 |
|
|
284
|
+
|
|
285
|
+
## Example
|
|
286
|
+
|
|
287
|
+
\`\`\`python
|
|
288
|
+
total = calc_order_total(items, tax_rate=Decimal("0.10")) # => 1100
|
|
289
|
+
\`\`\`
|
|
290
|
+
|
|
291
|
+
## Tests
|
|
292
|
+
|
|
293
|
+
- [tests/services/test_order.py](https://github.com/example-org/shop-api/blob/main/tests/services/test_order.py)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
As with hand-written guides, this kind of reference page should also be prepared in both ja and en. When the implementation changes, use the `source` key as the anchor for finding and updating the corresponding page.
|
|
297
|
+
|
|
298
|
+
## 7. Example CI freshness check implementation
|
|
299
|
+
|
|
300
|
+
The principle is: "regenerate it, and fail if there's a diff." Apply this to both `openapi.json` and the generated client.
|
|
301
|
+
|
|
302
|
+
```yaml
|
|
303
|
+
# .github/workflows/docs-freshness.yml
|
|
304
|
+
name: docs-freshness
|
|
305
|
+
on: [pull_request]
|
|
306
|
+
|
|
307
|
+
jobs:
|
|
308
|
+
freshness:
|
|
309
|
+
runs-on: ubuntu-latest
|
|
310
|
+
steps:
|
|
311
|
+
- uses: actions/checkout@v4
|
|
312
|
+
- uses: actions/setup-node@v4
|
|
313
|
+
with: { node-version: 20, cache: npm }
|
|
314
|
+
- run: npm ci
|
|
315
|
+
# Regenerate openapi.json and check it matches what's committed
|
|
316
|
+
- run: npm run openapi:generate
|
|
317
|
+
- run: git diff --exit-code openapi.json
|
|
318
|
+
# Regenerate the client/types and check src/generated/ has no diff
|
|
319
|
+
- run: npm run codegen:types && npx orval
|
|
320
|
+
- run: git diff --exit-code src/generated/
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
The same idea applies in GitLab CI — just line up "regenerate → `git diff --exit-code`" as script steps in the job.
|
|
324
|
+
|
|
325
|
+
This check catches both failure modes: (a) a PR that changed the API but forgot to update `openapi.json`, and (b) a PR that hand-edited generated output.
|
|
326
|
+
|
|
327
|
+
## 8. Anti-patterns
|
|
328
|
+
|
|
329
|
+
| Anti-pattern | Problem | Do this instead |
|
|
330
|
+
| --- | --- | --- |
|
|
331
|
+
| Hand-writing API response types | Falls out of sync with API changes; types and reality drift apart | Generate into `src/generated/` with openapi-typescript / orval |
|
|
332
|
+
| Hand-editing generated output (`docs/api`, `src/generated/`) | Gets wiped out on the next regeneration, and fails the CI freshness check anyway | Edit the source of truth (decorators, TSDoc, the spec) and regenerate |
|
|
333
|
+
| Test links pinned to a commit SHA | Keeps pointing at old code and drifts from the current state | Use an absolute URL anchored to the default branch |
|
|
334
|
+
| Leaving translations behind (only updating the ja original) | English readers keep seeing stale information | Update ja and en together in the same PR for hand-written guides |
|
|
335
|
+
| Orphan pages missing from the sidebar | Unreachable except via search; nobody knows they exist | Always register pages in sidebars.js (either autogenerated or explicit) |
|
|
336
|
+
|
|
337
|
+
## Related
|
|
338
|
+
|
|
339
|
+
- Source of truth for conventions: `rules/documentation/` (for repository maintainers)
|
|
340
|
+
- API design guidance itself: the api-design skill
|
|
341
|
+
- Running the implementation/documentation consistency check: `/update-docs`
|