AGENTS.md · diff
git:20260529.4ff3466 to git:20260602.2d56708
52 added, 17 removed. Audit A to A.
# 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.
+ `inflow.depositAddresses` (IDepositAddressResource), `inflow.x402` (IX402), `inflow.mpp` (IMpp). 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.
+ - **No stub comments.** Don't leave `TODO`, `phase 2`, or `refactor later` notes in shipped code — describe what the
+ code does now, or delete the comment. This is about comments, not scope: splitting a task, deferring genuinely
+ out-of-scope work to a separate change, or stopping to confirm direction is expected, not a violation.
- **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.
+ - **Write to the current state, not the change.** Comments and docs address a reader who has only the current tree —
+ never a prior version they can't see. Don't phrase a fact relative to what changed: avoid "now", "no longer",
+ "previously", "used to", "removed", "renamed", "added behavior", "prior/pre-X behavior", "reshaped". State the fact
+ directly — not "the flag no longer defaults to true" but "the flag defaults to false". This bans the framing, not the
+ fact: documenting a notable absence is fine. Change-relative narration belongs in the Changeset and commit message,
+ not in code or docs.
- **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.
+ ### Non-negotiables
+
+ These three come before the pressure to finish quickly. When they conflict with "get it done," they win.
+
+ - **Check the contract before you build on it.** Before you rely on anything across a boundary — an endpoint's audience,
+ authentication, and response shape; what a function or framework actually does; what another package exports — read
+ the authoritative source and cite where you found it (file and line) in your report. Two things looking alike by name
+ is not proof: a seller "config" endpoint is not the buyer "supported" endpoint just because both describe
+ capabilities. If the right target does not exist, or the instruction is ambiguous, stop and ask.
+ - **Pausing to confirm is never a failure.** Shipping on an unchecked assumption is. You may stop at any point — to
+ confirm context, check a fact, or ask for direction — and you are encouraged to do so at a low threshold, before you
+ have committed to an approach.
+ - **Do not trust a check that fakes the thing you are unsure about.** A test or stand-in that imitates the exact
+ behavior you have not verified proves nothing about the real thing. Confirm against the real implementation.
+
### 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.
+ - **Confirm you have the right context before doing the work.** Surface missing facts before writing — and treat a fact
+ you have not checked against the source as missing. Knowable facts here include which command group, which output mode
+ (interactive or agent), which environment, and which credential model. If a fact is knowable by reading the code, read
+ it and cite where you found it before relying on it. When you are unsure about scope, intent, or whether you have
+ enough to proceed, stop and ask. A low bar for asking is preferred over guessing.
- **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.
+ - **You are the architect; the user decides.** For how to structure, name, or pattern something, propose and recommend
+ with the tradeoffs. When the choice is genuinely the user's — a public interface or output shape, scope, anything
+ touching money or credentials, or anything where their words are ambiguous — ask. A short question that lays out the
+ options and your recommendation is the right move, not a failure; only the bare, analysis-free "what do you want?" is
+ discouraged. Ask in the chat as a numbered list — each item with a little context or an example, any options to choose
+ from, and your recommendation — rather than a tool that limits the number of questions or the space to read them.
- **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.
+ - **No abbreviations.** Spell things out in replies, comments, and docs. Don't use abbreviations or acronyms the reader
+ may not know (for example, don't write "DoD" for "definition of done"). Names this codebase already uses are fine.
### 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.
+ - **Cross-repository work.** When a change depends on another repository's behavior, confirm that behavior in that
+ repository before writing code against it.
+ - **Don't silently drop work.** If something you would treat as out of scope is actually needed to finish the agreed
+ goal, do not skip it without a word — surface it and ask how to proceed.
- **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.
+ - **Run the real checks before you say it works.** Run the full gate set this repo defines — `tsc` against both the
+ source config and the test config, lint, tests, and `pnpm typedoc` when the public surface or a documentation link
+ changed — not a subset. In your report, name each command you ran and its result. Never write "done", "passing", or
+ "verified" for a check you did not actually run; if you could not run one (for example, the environment cannot), say
+ so plainly and hand it off — do not imply it passed. Do not claim tests or coverage pass without running the suite.
- **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.
+ - **Show your work in the report.** List the files you changed, the exact commands you ran with their outcomes, and mark
+ each assumption as either checked-against-its-source or not-yet-checked. The reader should be able to see what is
+ verified and what is not without rerunning anything.