Imported from klmkyo/travel-tok (
AGENTS.md). Install upstream withnpx skills add klmkyo/travel-tok. Copyright stays with the author.
AGENTS.md
Instructions for agents working in the travel-tok monorepo. It covers every workspace package: apps/mobile is an Expo app and apps/backend is a Hono API server.
React Native, Uniwind, and Expo Router conventions are deliberately not here. They live in apps/mobile/AGENTS.md, which Pi only loads when the working directory is inside that package. Read it before changing anything under apps/mobile.
Repository layout
- pnpm workspace. Apps live in
apps/*and packages shared between them live inpackages/*. pnpm-workspace.yamlholds every pnpm setting, not just the package globs: thecatalogof shared dependency versions,minimumReleaseAge,allowBuilds, andpatchedDependencies. Check it before adding a dependency.workbench/is a gitignored scratchpad and deliberately not a workspace member. Nothing in it is imported, built, or kept.- Keep code in the app or package that uses it. Do not add anything to
packages/until a second workspace package needs it.
Before you finish
Run the project checks after changing code:
pnpm lint # `oxlint` once across every package
pnpm -r type-check # `tsc --noEmit` in every package
pnpm fmt:check # `oxfmt --check`
Those run from the repository root. From inside one package, pnpm check runs that package's lint then its type-check.
Do not start dev servers, or run native build commands, unless the user asks.
Lint and format configuration
- Always write Oxlint and Oxfmt configs as TypeScript, never JSON.
- The root
oxlint.config.mtsownsoptions.typeAware, and it is the only config that may. Oxlint hard-errors if a nested config setsoptions, so never add it to a package config. - Oxlint uses only the nearest config for a file and does not merge configs across directories.
extendscarriesrules,overrides,plugins,categories, andjsPlugins, but it does not carryignorePatterns,globals,env, orsettings, so a package config has to restate those. - A rule that comes from a JavaScript plugin can depend on the process working directory rather than on the config file.
eslint-plugin-no-relative-import-pathsdid: itsrootDiroption silently stopped matching whenever Oxlint ran repo-wide from the repository root, which is why the@/import rule uses Oxlint's built-inno-restricted-importsinstead. After adding a jsPlugin rule, confirm it still fires in a barepnpm lintfrom the repository root. pnpm-workspace.yamlenforces a three-dayminimumReleaseAge. A brand-new dependency version will fail to install by design; add the package tominimumReleaseAgeExcludeonly when there is a real reason.
TypeScript
- The root and
apps/backendusetypescript@7, the native compiler.apps/mobilepinstypescript@^6instead, because Expo's config loading andexpo-doctorboth expect the 6.x line. Do not unify them. - Shared compiler options live in
tsconfig.base.jsonand cover strictness only.module,moduleResolution,types, and emit settings are per-package decisions:apps/mobileextendsexpo/tsconfig.base, and the backend usesnodenextand compiles todist/. tsconfig.base.jsonsetsnoEmit: true. A package that compiles withtschas to override it.
Backend types
- Do not hand-write request or response types for the backend. Types come from the Hono RPC client, which infers them from the server's route definitions.
- Routes must therefore be chained, because an unchained route loses its type before the client sees it.
- Any package that imports
hono/clientneedshonoas a direct dependency; pnpm's isolatednode_moduleswill not resolve it from the backend's copy.
Git commits
Use Conventional Commits when writing commit messages.
- Prefer a feature or domain scope when the change is localized, such as
feat(gallery): ...orfix(camera): .... Match scopes to folders undersrc/features/<feature>/when possible. - Use the package name when a change is specific to one package but not to one feature, such as
chore(mobile): ...orfeat(backend): .... - Omit the scope when the change spans multiple features or is repo-wide, such as
chore: ...ordocs(AGENTS): ....
Project state
- The app is under active development. It has no production users or shipped releases.
- Do not add compatibility shims, migrations for old storage keys or data, or fallback paths for previous app versions unless the user asks.
- Prefer a clean breaking refactor over preserving old formats, deprecated keys, or compatibility layers.
Code conventions
- Use named exports unless a framework requirement, such as an Expo Router route, requires a default export.
- Write new components and helpers as
constarrow functions, except when Expo Router requires a default exported route component. - Do not add barrel files or modules that only re-export values. Import from the file that defines the value or type.
- Do not add a helper that only renames, re-exports, or lightly wraps another helper. When it has one real caller, use the underlying helper at that call site.
- Keep one-off values and configuration objects inline when that reads clearly. Extract a constant when code reuses it or when its name explains intent.
- Avoid
any. Use precise TypeScript types. - Use
import typefor type-only imports. - Use strict equality (
===and!==). Check nullable values explicitly withvalue === null || value === undefined, or use the inverse. - Name enum member keys with
UPPER_SNAKE_CASE, such asELocale.ENandELocalStorageKey.DEBUG_STORE. Do not use PascalCase. - For small collections or occasional membership checks, use an array with
.includes()or.some(). UseSetwhen the collection is larger or the code performs repeated lookups. - Do not compress code at the cost of readability. Prefer code that explains itself.
- Keep comments that still describe the code after a refactor, and move them with the code they describe.
- Add comments only to explain intent, edge cases, platform quirks, or trade-offs that the code does not make clear.
