AGENTS.md · git:20260529.4ff3466 · 2026-05-29 · sha256 7e73a9191c468ac5

AGENTS.md git:20260529.4ff3466A

Immutable. This exact content is served forever at /api/v1/blob/7e73a9191c468ac5.

# AGENTS.md

Operating notes for working in this repo. If anything below conflicts with the configs (`eslint.config.js`,
`typedoc.json`, `tsconfig.base.json`, `turbo.json`, `.changeset/config.json`, `.prettierrc.json`), the configs win — fix
the drift, don't paper over it.

## What this repo is

`inflow` is the agent-native and human-accessible command-line entry point to InFlow. It lets agentic buyers perform
agent-native payments via MPP and x402, and lets humans hit the same functionality from MCP-integrated assistants or the
raw CLI.

A pnpm + Turborepo monorepo with two packages:

- `@inflowpayai/inflow-core` (`packages/core`) — the headless InFlow client. One augmented handle per command group hung
  off the `Inflow` instance: `inflow.auth` (IAuth), `inflow.user` (IUser), `inflow.balances` (IBalanceResource),
  `inflow.depositAddresses` (IDepositAddressResource), `inflow.x402` (IX402). Each handle carries the typed HTTP
  primitives plus the command-shaped operations (reducers + async-iterable drivers for the stateful commands). No UI
  dependencies; an ESLint `no-restricted-imports` rule scoped to `packages/core/src/**` enforces this.
- `@inflowpayai/inflow` (`packages/cli`) — the published binary. Consumes the core package; renders Ink/React for TTY
  mode and structured output for agent mode. The CLI commands are thin render shells over the core flows.

## Repo map

- `packages/cli/` — `@inflowpayai/inflow`, the published binary.
- `packages/core/` — `@inflowpayai/inflow-core`, the typed HTTP client.
- `plugins/inflow/` — the plugin bundle: `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`.
- `skills/agentic-payments/SKILL.md` — the single skill file.
- `.claude-plugin/marketplace.json` — Claude Code marketplace entry.
- `.codex-plugin/plugin.json` — Codex marketplace entry.
- `.mcp.json` — raw MCP entry.
- `scripts/` — repo-level dev/CI scripts.
- `.changeset/` — pending version bumps.

Load-bearing root files — touch with care: `tsconfig.base.json`, `turbo.json`, `eslint.config.js`,
`.changeset/config.json`, `typedoc.json`, `.prettierrc.json`, `pnpm-workspace.yaml`.

## Before merging

Run all four. CI runs the same.

- `pnpm typecheck` — `tsc --noEmit` against both `tsconfig.json` (src) and `tsconfig.test.json` (src + test) per
  package.
- `pnpm lint` — eslint with `--max-warnings 0`.
- `pnpm test` — vitest with v8 coverage; per-package thresholds enforced; build fails below floor.
- `pnpm typedoc` — generates the core package's public API reference; catches broken `{@link}` and internal-type leakage
  into public signatures. Runs against `@inflowpayai/inflow-core` only; the binary has no public API.

Scope to one package with `pnpm --filter @inflowpayai/<name> <task>`.

The CLI's integration tests run against the built `packages/cli/dist/cli.js`. Run `pnpm build` before running them if
source has changed.

## Conventions

Rules the tooling can't enforce. Breaking them lands a regression.

- **ESM everywhere.** `"type": "module"` in every `package.json`. No CJS.
- **Node ≥ 22.0.0.** `engines.node` enforces. CI matrix tests 22 + 24. `.node-version` pinned to 22.
- **TypeScript strict mode.** Shared `tsconfig.base.json`. `noUncheckedIndexedAccess` is on.
- **No `any`, no `!` non-null, no `as unknown as`** except at documented type boundaries. Justify and comment every
  boundary cast.
- **No `console.*` in `packages/**/src/**`.** Publishable code throws typed errors and lets the caller decide what to
  log. `process.stderr.write` is the only exception, and only inside the CLI's top-level entry. `console.*` is fine in
  `scripts/` and `test/`.
- **The package barrel is the public surface.** Anything in `src/index.ts` is public API; anything else is
  implementation detail. Add `@internal` for exported-but-not-public symbols.
- **No emoji** in code, commits, or PR descriptions unless the request explicitly calls for them.
- **No "future work" / "phase 2" / "TODO: refactor later" comments.** Describe what the code does now, or delete the
  comment. Out-of-scope work becomes a separate change, not a stub.
- **Comments only for what the code can't say.** No restatement of behavior, no rationale-padding, no historical
  justification. Applies to every comment syntax — TSDoc, inline, YAML, shell, JSON-with-comments.
- **Minimal TSDoc.** This is a CLI, not a public-facing library. The README carries the long-form context. Default to
  **no TSDoc** unless the signature genuinely can't say it. The signature names parameters, the type names them, the
  function name names them — if you find yourself paraphrasing those, delete the comment.
- **Output formats are part of the contract.** Every command's `--format json` shape is specified to the field level.
  Don't drift the shape without bumping the changeset.
- **Two modes per command.** Interactive (Ink, TTY) and agent (`--format` set or non-TTY). Gate via the framework's
  `agent`/`formatExplicit` flags. Agent mode never renders Ink.
- **ANSI sanitization is non-negotiable.** All core-package responses pass through a recursive sanitizer via a
  resource-wrapping Proxy before reaching the renderer or the formatter. Don't add a resource that bypasses the Proxy.
- **Credential files are `0o600`.** Use `conf` for the auth file. Use `0o600` and `--force`-gated overwrite for any
  other file the CLI writes that contains a credential.
- **Schemas drive flags AND MCP tool input.** One `zod` schema per command, colocated with the command. The framework
  registers CLI flags from the schema; the same schema is the MCP tool input schema.
- **Behaviors are designed up front, per command.** State-mutating commands (login, logout, pay, cancel, retry) document
  the failure mode of every step, both TTY and agent modes, the `--format json` output shape, and the exact error
  `code`/`message` strings. Retrofit-style behavior design is a regression.

## Adding a package

For a new package: follow the structure of the existing packages — `packages/<name>/{src,test/unit}`, `package.json`
with the standard fields (`peerDependencies` if needed, `publishConfig.access: public`,
`publishConfig.provenance: true`), `tsconfig.json` + `tsconfig.test.json`, `tsup.config.ts`, `vitest.config.ts`,
`README.md`. Then `pnpm install` to refresh the lockfile.

New packages should be rare. If you're adding one, the change should justify why a new package boundary is needed
instead of a new module inside `packages/cli` or `packages/core`.

## Writing docs

- **The README is the long-form doc.** It carries usage examples, integration recipes, and the conceptual map. Keep it
  accurate; the published `README.md` ships in the npm package.
- **TSDoc is minimal.** Default to none. Add a TSDoc block only when the signature can't say it and there's a
  non-obvious sentence the caller needs. The linter does not require TSDoc on every export.
- **Use `{@link Foo}` only for symbols re-exported from the package barrel** — links must resolve from the published API
  reference.
- **`@internal` for symbols not re-exported** from the barrel.
- **Skills live in `skills/agentic-payments/SKILL.md`.** YAML frontmatter is single-line (the embedded agent parser is
  line-based — multi-line frontmatter values break it).

## Branch model, commits, releases

- Short-lived branches off `main`. Conventional Commits, scoped by package: `feat(cli): …`, `fix(sdk): …`.
- PRs touching `packages/**` need a Changeset (`pnpm changeset`). CI fails without one.
- Release flow uses `changesets/action` from `.github/workflows/release.yml`. Only `@inflowpayai/inflow` publishes to
  npm — `@inflowpayai/inflow-core` is private (workspace-only).
- One npm channel: `latest`.
- The skill version in `skills/agentic-payments/SKILL.md` frontmatter is auto-synced from `packages/cli/package.json`
  via `scripts/align-skill-version.js` during `pnpm build`. Don't hand-edit the skill's `version:` line.
- CI runs `pnpm --filter @inflowpayai/inflow publish --dry-run --no-git-checks` on every push to `main` to catch
  publishability regressions before they bite.

## When stuck

- Project overview, install, and usage: root `README.md`.
- Core package API reference: `packages/core/README.md` and the generated TypeDoc.
- CLI command reference: `packages/cli/README.md`.
- Tool configuration: the configs themselves (`turbo.json`, `tsconfig.base.json`, `eslint.config.js`,
  `.prettierrc.json`, per-package `tsup.config.ts` and `vitest.config.ts`).

## Working as an agent

These rules apply to LLM agents picking up tasks in this repo. They aren't enforceable by CI; the cost of breaking them
is wasted reviewer cycles or a regression that ships.

### Interaction

- **Ask when the task is underspecified.** Surface missing **facts** before writing: which command group, which output
  mode (interactive vs. agent), which environment, which credential model. These are knowable — don't guess. For design
  choices, see the architect rule below.
- **Don't execute on questions, ideas, or plans until the user explicitly says so.** A question is a question; a plan is
  a plan. Wait for an unambiguous "go" / "do it" / "yes" before writing code or files. Surfacing options is not approval
  to pick one.
- **You are the architect; the user is the decision maker.** For **design choices** — how to structure something, which
  pattern to apply, what to name a thing — propose, recommend, and surface the tradeoffs. Don't punt them back as
  open-ended questions ("what would you like to do?"), and don't make them unilaterally. The user approves or redirects.
- **No hand waving.** Be concrete and specific. No "should generally", "consider whether", "this might work" — if you
  have a recommendation, make it; if you don't, name what you'd need to know to form one.
- **When explaining, ground the explanation.** Don't state a rule, a tradeoff, or a behavior without the referent —
  point at the file, quote the call site, sketch the example or the solution. A claim without a referent is noise.

### Code work

- **Don't guess at signatures or behavior.** If you don't know what a function does, read it. If you don't know what a
  type exports, check the barrel.
- **Don't fabricate.** Never claim a function exists, a type is exported, or a behavior is implemented without
  verifying. If something looks like it should exist but doesn't, surface that — don't invent it.
- **Don't improvise patterns.** If a similar problem is already solved in this repo, follow the existing pattern. Adding
  a new helper, util, or dependency without justifying why the existing pattern doesn't cover the case is rejected on
  review.
- **Research before writing.** Read the related code first. Then grep for the symbol in question to see how it's used
  elsewhere. Then write.
- **Minimal diffs.** Change as little as possible to achieve the goal. Don't reformat unrelated lines, don't sweep style
  fixes across files outside your scope, don't bump dependencies unless the task is the bump.
- **Comments are part of the diff.** A 14-line comment above a 9-line code change is not a minimal diff. See the comment
  rule under [Conventions](#conventions).

### Done

- **Verify before claiming done.** Run `pnpm typecheck && pnpm lint && pnpm test` (and `pnpm typedoc` if the core
  package's public surface or any `{@link}` changed) before reporting success. "It looks right" is not verification.
- **Surface conflicts; don't paper over them.** If the request would require breaking a convention above, stop and say
  so. Don't reach for `eslint-disable`, `@ts-ignore`, or `as any` to make a check pass.
- **No "TODO" / "phase 2" escape hatches.** If a piece of work is out of scope, drop it cleanly and note it — don't
  leave a stub or a comment promising future cleanup.
- **Report what you actually did.** Files touched (with line counts), verification commands run with outcomes, and any
  surprises that should land in a separate change.