diff --git a/.claude/rules/repo-wide.md b/.claude/rules/repo-wide.md new file mode 100644 index 00000000000..169afd22fc3 --- /dev/null +++ b/.claude/rules/repo-wide.md @@ -0,0 +1,5 @@ +# Repo-wide review rules (Claude loader) + +Loads the same repo-wide review rules Copilot uses, so Claude applies them during implementation. Edit `.github/copilot-instructions.md` — not this file. + +@../../.github/copilot-instructions.md diff --git a/.claude/rules/tests.md b/.claude/rules/tests.md new file mode 100644 index 00000000000..aa96671af41 --- /dev/null +++ b/.claude/rules/tests.md @@ -0,0 +1,11 @@ +--- +paths: + - "src/**/__tests__/**" + - "features/**" +--- + +# Test review rules (Claude loader) + +Loads the same test review rules Copilot applies to vitest unit tests under `src/**/__tests__/**` and Cucumber E2E under `features/**`. Edit `.github/instructions/tests.instructions.md` — not this file. + +@../../.github/instructions/tests.instructions.md diff --git a/.claude/rules/typescript.md b/.claude/rules/typescript.md new file mode 100644 index 00000000000..9f997b0e959 --- /dev/null +++ b/.claude/rules/typescript.md @@ -0,0 +1,10 @@ +--- +paths: + - "src/**/*.ts" +--- + +# TypeScript review rules (Claude loader) + +Loads the same TypeScript review rules Copilot applies to `src/**/*.ts`. Edit `.github/instructions/typescript.instructions.md` — not this file. + +@../../.github/instructions/typescript.instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000000..c6c8c39a475 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,47 @@ +# Copilot review instructions — cli-kintone + +cli-kintone is the official kintone CLI (TypeScript, Node `>=20`, pnpm), **public OSS** under `kintone/`. Review PRs in this priority order. TS-specific and test-specific rules live in `.github/instructions/*.instructions.md`. + +## Correctness + +- Flag missing handling for rate-limit responses (HTTP 429). Surface a typed error and exit non-zero; do not swallow-and-retry forever. +- Flag generic "request failed" error messages that don't tell the user what to do. Map auth, not-found, and validation failures to actionable messages. +- Flag boundary / off-by-one bugs in pagination, batching, CSV / JSON row handling. + +## Security + +- Flag code that logs, prints, or returns API tokens, passwords, `Authorization` headers, basic-auth credentials, or session cookies. Redact before stdout / stderr / logs / error messages / thrown errors. +- Flag credentials, hostnames, customer data, or internal information committed to source, tests, fixtures, snapshots, or `features/`. Repo is public. +- Flag `child_process.exec` / shell concatenation from user input. Use `spawn` with argv. +- Flag path traversal: user-supplied paths must be normalized within the expected base. +- Flag disabled TLS (`rejectUnauthorized: false`, `NODE_TLS_REJECT_UNAUTHORIZED=0`) without an explicit user-facing flag. + +## Code quality + +- Flag hard-coded `/` in user-facing paths; use `path.join` / `path.resolve` (Windows is supported). +- Flag unused exports, dead code, `console.log` in non-CLI paths. +- Flag a new runtime dependency without justification in the PR description (`pkg` is sensitive to bundle size / native modules). + +## Tests + +- Behavior changes in `src/{record,customize,plugin,kintone}/` need a vitest test in the colocated `__tests__/`. +- User-visible CLI behavior needs a Cucumber feature / step update under `features/`. +- Flag tests that mock the unit under test, mock pure functions, or assert on mock-internal calls instead of observable behavior. +- Flag committed credentials or real kintone hostnames in fixtures. + +## PRs + +- PRs must fill in `.github/PULL_REQUEST_TEMPLATE.md` (Why / What / How to test / Checklist). + +## Review style + +- Concrete: cite file and line, name the risk, suggest the change. Avoid vague "consider improving readability". +- State **observed** impact ("breaks CSV import on BOM"), not a hypothetical edge case. +- Scope to the diff. Refactor ideas on surrounding code go in `suggestion:` only. +- Comment in English. Do not block on issues already enforced by ESLint / Prettier in CI. +- Use one of these prefixes (default = no prefix = blocking): + - **(no prefix)** — blocking. Bugs, security, type or test failures, public-OSS hygiene breaks. Must be addressed before merge. + - **`nit:`** — non-blocking polish (style, typo, naming). Author can ignore. + - **`question:`** — clarification of intent. May become blocking depending on the answer. + - **`suggestion:`** — non-blocking improvement, alternative, or refactor idea on surrounding code. +- Provide a GitHub-flavored `suggestion` code block when a concrete fix exists; `question:` needs none. diff --git a/.github/instructions/tests.instructions.md b/.github/instructions/tests.instructions.md new file mode 100644 index 00000000000..efb1fadd180 --- /dev/null +++ b/.github/instructions/tests.instructions.md @@ -0,0 +1,45 @@ +--- +applyTo: "src/**/__tests__/**,features/**" +--- + +# Test review rules — vitest unit tests & Cucumber E2E + +Apply in addition to `.github/copilot-instructions.md`. + +## Layering + +- vitest — colocated as `src/**/__tests__/*.test.ts`. Use for unit / integration of pure logic, parsers, formatters, and feature modules in `src/{record,customize,plugin,kintone}/`. +- Cucumber — `features/`. Use for user-visible CLI behavior: option parsing, exit codes, stdout / stderr format, file I/O against a real kintone environment. +- A behavior change that affects what the user sees on the command line needs a `features/` update, not only a vitest test. + +## What to flag in vitest + +- Mocking the unit under test, mocking pure functions, or asserting on mock call counts when the public output already encodes the behavior. + + ```ts + // ✗ over-mocked: asserts on the mock, not the behavior + const fn = vi.fn(); + myCommand({ logger: fn }); + expect(fn).toHaveBeenCalledWith("done"); + + // ✓ assert on the observable result + const result = await myCommand({ ... }); + expect(result.status).toBe("ok"); + ``` + +- Wide `vi.mock("module")` calls when a narrow stub on the dependency would do. +- Snapshots of large objects without a clear reason — prefer field-level assertions. +- Tests that pass by mocking the kintone API to return exactly what the code expects without exercising any branch. +- Skipped (`.skip`) or focused (`.only`) tests left in the diff. + +## What to flag in Cucumber (`features/`) + +- New scenarios that hard-code credentials, real kintone subdomains, or app IDs. Use the env-driven setup in `features/supports/` (credentials loaded via env vars per `e2e-credentials-schema.json`). +- Step definitions that reach into internal modules in `src/`. Steps should drive the CLI as a black box (spawn the binary, assert stdout / stderr / exit code / file outputs). +- Background steps duplicated across scenarios that could be lifted to `Background:` blocks. + +## Common to both + +- Tests must not write outside their tmp dir, leak network calls in unit tests, or depend on test ordering. +- Failure messages should identify what was being verified, not just `expected true to be false`. +- New domain logic in `src/{record,customize,plugin}/` needs at least one vitest test; new CLI-visible behavior needs a `features/` scenario. Flag missing coverage. diff --git a/.github/instructions/typescript.instructions.md b/.github/instructions/typescript.instructions.md new file mode 100644 index 00000000000..812f99f80f5 --- /dev/null +++ b/.github/instructions/typescript.instructions.md @@ -0,0 +1,56 @@ +--- +applyTo: "src/**/*.ts" +--- + +# TypeScript review rules — `src/**` + +Apply in addition to `.github/copilot-instructions.md`. + +## Types + +- Prefer types from `@kintone/rest-api-client` for kintone records, fields, app schemas, and API responses. Do not re-declare equivalent shapes locally — re-declaration drifts from the SDK on every update. Import from the top-level package; do not reach into internal subpaths. + + ```ts + // ✗ ad-hoc shape that drifts from the SDK + type Record = { $id: { value: string }; [k: string]: { value: unknown } }; + + // ✓ reuse SDK types via the public entry point + import type { KintoneRestAPIClient } from "@kintone/rest-api-client"; + import { KintoneRestAPIError } from "@kintone/rest-api-client"; + ``` + +- Flag new `any`, `as any`, `as unknown as`, `@ts-ignore`, `@ts-expect-error` without an inline justification. +- Prefer `unknown` over `any` at boundaries (parsed JSON, file content), then narrow with a type guard. +- Use discriminated unions for command results instead of optional fields that are "always set together". + +## Errors + +- Command handlers in `src/cli/**` must not throw raw `Error`. Wrap with a typed error class (see `src/record/error/`) so the CLI can emit a useful exit code and message. +- `catch (e)` must narrow before use: + + ```ts + // ✓ + } catch (e) { + if (e instanceof KintoneRestAPIError) { ... } + throw e; + } + ``` + +- Error messages must not contain API tokens, passwords, or `Authorization` headers, even when re-throwing. + +## Async & I/O + +- All new I/O should be `async` with `node:fs/promises`. Flag `*Sync` calls outside of startup / scripts. +- Prefer `Promise.all` for independent calls. Flag accidental sequential `await` in loops over independent items. + +## CLI options (`src/cli/**`) + +- Option names are kebab-case (`--api-token`, `--app-id`, `--base-url`). Camel-case option names are wrong. +- Reuse shared builders from `src/cli/authOptions/`, `src/cli/connectionOptions.ts`, `src/cli/logOption.ts` instead of redeclaring `--base-url`, `--username`, `--password`, `--api-token`, `--basic-auth-*`, `--proxy`. +- New flags need a description suitable for `--help` and corresponding docs in `website/docs/reference/`. + +## Imports & boundaries + +- Use `import type { ... }` for type-only imports. +- No deep imports across feature boundaries (e.g. files under `src/record/` should not import private submodules of `src/customize/`). Use the feature's public entry (`src//index.ts` style) or lift the shared code into `src/utils/` / `src/kintone/`. +- Domain logic does not live in `src/cli/**`. That layer wires yargs and delegates; logic lives in `src/{record,customize,plugin,kintone}/`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..a947335ea4d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,77 @@ +# cli-kintone + +Official CLI for kintone. Bundles record / customize / plugin subcommands and ships as a Node.js package plus standalone executables (Linux x64, macOS arm64, Windows x64) produced via `ncc` + Node SEA (`pkg`). + +For the documentation site (Docusaurus), see [`website/CLAUDE.md`](./website/CLAUDE.md). + +## Repository layout + +``` +src/ +├── cli/ # Entry points and yargs command wiring (main.ts is the entry) +│ ├── record/ # `cli-kintone record ...` +│ ├── customize/ # `cli-kintone customize ...` +│ ├── plugin/ # `cli-kintone plugin ...` +│ ├── authOptions/ # Shared --base-url / --username / --password / --api-token / --basic-auth-... flags +│ └── connectionOptions.ts, logOption.ts, stability.ts +├── record/ # Record import/export/delete domain logic +├── customize/ # JS customize apply/export/init logic +├── plugin/ # Plugin pack / upload / keygen / info logic +├── kintone/ # kintone API client wrappers (built on @kintone/rest-api-client) +└── utils/ + +features/ # Cucumber E2E (step_definitions/, supports/, plugin/, customize/, record/) +scripts/ # Build helpers (compress-to-zip-file.ts, update-contributors.ts) +plugin-templates/ # Scaffolding emitted by `cli-kintone plugin init` +website/ # Docusaurus docs (see website/CLAUDE.md) +.github/ # Workflows, PR template, Copilot review instructions +``` + +Unit tests live next to source as `__tests__/` directories using vitest. + +## Toolchain + +- Node `>=20` (`mise.toml` pins Node 24 for development) +- Package manager: **pnpm** — version pinned by `packageManager` in `package.json` (`mise.toml` also pins a pnpm version for development) +- Bundler: `@vercel/ncc` for single-file build, `pkg` (Node SEA) for native executables +- Linter / formatter: ESLint (`eslint.config.mjs`) + Prettier (`*.{json,md,yml,yaml}`) +- Tests: vitest (unit) + Cucumber (E2E, `features/`) +- Release: release-please with **Conventional Commits** (see `release-please-config.json`) + +## Commands + +| Command | Purpose | +| --- | --- | +| `pnpm build` | Clean `lib/` then `tsc --build tsconfig.build.json` | +| `pnpm start` | `build` in watch mode | +| `pnpm build:all` | Build + native executables + license analysis + zip | +| `pnpm typecheck` | `tsc --noEmit` against `tsconfig.typecheck.json` | +| `pnpm test` | vitest (unit) | +| `pnpm test:ci` | vitest with CI config | +| `pnpm test:e2e` | Cucumber E2E. Requires kintone credentials via env (see `features/supports/`) | +| `pnpm test:e2e:dev` | `test:e2e --fail-fast` | +| `pnpm lint` | `lint:eslint` + `lint:prettier` in parallel | +| `pnpm fix` | Auto-fix eslint + prettier | +| `pnpm doc:*` | Delegate to `website/` workspace | +| `./cli.js` | Run the locally built CLI | + +Before submitting a change, run: `pnpm lint && pnpm typecheck && pnpm test`. + +## Testing policy + +- **Unit (vitest)**: colocated in `src/**/__tests__/`. Default to real implementations; mock only at external boundaries (HTTP, fs, kintone API). +- **E2E (Cucumber)**: lives in `features/`. Step definitions in `features/step_definitions/`. Each subcommand has its own subfolder. E2E runs against a real kintone environment using credentials from `e2e-credentials-schema.json`-shaped env vars; do not commit credentials. +- Add a vitest test when changing domain logic in `src/{record,customize,plugin,kintone}/`. Add or update a feature file when changing user-visible CLI behavior. + +## Cross-repo dependencies + +- Depends on `@kintone/rest-api-client` from `kintone/js-sdk`. When the SDK ships a breaking change, bump here and verify against `pnpm test:e2e`. +- Public docs live in `website/` and at . + +## AI agent files + +- `CLAUDE.md` (this file): implementation context for Claude. +- `website/CLAUDE.md`: Docusaurus-specific guidance for the docs workspace. +- `.github/copilot-instructions.md`: repo-wide review rules for GitHub Copilot. Single source of truth for review-time rules. +- `.github/instructions/*.instructions.md`: path-scoped Copilot review rules (`applyTo` frontmatter). +- `.claude/rules/*.md`: Claude-side path-scoped rule loaders. Each file declares `paths:` frontmatter and `@import`s the matching Copilot file so Claude follows the same rules without duplicating content. The Copilot files under `.github/` are the canonical source — edit them, not the loaders.