Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .cursor/BUGBOT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Bugbot review guide — @amplitude/developer-api

This repo is the published `amp` CLI: a thin, generated client over the
Amplitude Developer API. It is a **public distribution**, so review for clarity
and safety, not just correctness. Prioritize the areas below.

## Public hygiene

- This source is public and read by external users. Flag anything that leaks
internal context: private repo names or paths, internal infrastructure
hostnames, internal issue-tracker IDs, internal-only tooling, or employee
names — in code, comments, docs, or commit-touched fixtures.
- README/docs/help must make sense to someone who only has the published
package (no upstream/monorepo context).

## Credential & token safety

- Credential files must be written with `0600` permissions. Flag any write that
loosens this.
- Never log, print, or echo tokens, PATs, or `Authorization` headers (including
in error messages or debug output).
- Don't weaken the documented credential precedence
(`--token` > `AMP_TOKEN` > `--profile` > `AMP_PROFILE` > active profile).

## Destructive actions

- DELETE / destructive commands must confirm (interactive `y/N`) or require
`--yes`. Flag any destructive path that skips the gate.
- `--dry-run` must never perform a write — flag a dry-run path that mutates.

## Generated & bundled artifacts

- `src/generated/**` and `openapi/bundled/**` are generated. Flag hand-edits;
changes belong upstream and are regenerated.

## Human/agent output contract

- Non-interactive / piped output must stay compact, lossless, and
machine-parseable. Flag ANSI colors, spinners, prompts, banners, or truncation
leaking onto the non-TTY path.
- `--json` output must remain valid, parseable JSON.

## Input validation & portability

- Validate untrusted input (API responses, saved credentials, user-supplied
`--body-json`) with schemas, not ad-hoc coercion.
- This package ships standalone: flag imports from any server/monorepo source or
new heavyweight dependencies.
53 changes: 53 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Build & Test

on:
push:
branches:
- main
pull_request:

permissions:
contents: read

jobs:
build:
name: Build, test, and package smoke
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Install pnpm
# Version comes from the "packageManager" field in package.json — do not
# also set `version:` here or action-setup fails on the conflict.
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
with:
run_install: false

- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm test:typescript

- name: Unit tests
run: pnpm test

- name: Build
run: pnpm build

# publint catches packaging mistakes (missing files entries, broken bin,
# bad exports) before they ship to npm.
- name: publint
run: pnpm dlx publint

# No-network smoke: pack -> install the tarball into a throwaway global
# prefix -> assert the `amp` bin loads. Validates the packaged artifact,
# not just the source tree. Needs no PAT and does not hit the Amplitude API.
- name: Packaging smoke
run: bash scripts/smoke-cli.sh
78 changes: 78 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: release-please

on:
push:
branches:
- main

permissions:
contents: write
pull-requests: write
issues: write
id-token: write

jobs:
release-please:
runs-on: ubuntu-24.04
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}
steps:
# Mint a GitHub App installation token so release-please's PRs trigger
# downstream workflows (GITHUB_TOKEN-authored events do not, by design,
# which would leave required checks unrun and the Release PR BLOCKED).
- uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
id: app-token
with:
app-id: ${{ secrets.RELEASE_PLEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }}

- uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0
id: release
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

publish:
name: Publish to npm
runs-on: ubuntu-24.04
needs: release-please
if: needs.release-please.outputs.releases_created == 'true'
environment: npm-publish
steps:
- name: Checkout the repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- name: Install pnpm
# Version comes from the "packageManager" field in package.json — do not
# also set `version:` here or action-setup fails on the conflict.
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
with:
run_install: false

- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
# Match build.yml and package.json engines (^22.22.0).
node-version: 22
registry-url: https://registry.npmjs.org

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build the package
run: pnpm build

# Authenticates to npm via OIDC Trusted Publishing (id-token: write at the
# workflow level). The Trusted Publisher for @amplitude/developer-api must
# be configured at
# https://www.npmjs.com/package/@amplitude/developer-api/access to trust
# repo=amplitude/developer-api, workflow=release-please.yml,
# environment=npm-publish. No NPM_TOKEN required.
#
# Pre-1.0 we publish to the `beta` dist-tag so `latest` stays unset until
# we promote at 1.0. Drop `--tag beta` when cutting 1.0.0.
- name: Publish the package to npm registry
run: pnpm publish --access public --no-git-checks --provenance --tag beta
Comment thread
ekim-amplitude marked this conversation as resolved.
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.1.0"
}
97 changes: 97 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# developer-api CLI — Agent Instructions

This package is the `amp` CLI: the human- and agent-facing surface over the
Amplitude Developer API. It is published as a standalone package, so treat
everything here as public and hold it to a high bar.

The Amplitude Developer API OpenAPI spec is the contract; this CLI is a thin,
generated client over it. The CLI manifest and bundled spec under
`src/generated/` and `openapi/bundled/` are generated artifacts — never edit
them by hand.

## Tenets

These break ties when a decision is ambiguous. They are deliberately aligned
with the Developer API golden standards.

### 1. Obvious by default

The best surface needs no explanation. A user should be able to guess the next
command and never get surprised by what one does.

**What good looks like**

- Discoverable help: `amp help` → surfaces → `amp <surface>` → `amp <cmd> --help`.
- Consistent verbs across surfaces (`list`, `get`, `create`, `update`, `archive`).
- Safe defaults: destructive actions confirm (interactive `y/N`, or `--yes` in
scripts); reads are free.
- Errors are actionable — they say what to do next, not just what failed.
- Conventions users already expect work: `--version`/`-v`, `--help`/`-h`.

**Anti-patterns**

- A flag whose effect you can't infer from its name (e.g. a `--dry-run` that
still performs the write).
- Requiring the README to use a command safely.

### 2. Code is marketing

This source is public. Readers judge Amplitude by it, so optimize for clarity,
not just function.

**What good looks like**

- Small, single-purpose modules with names that read like a table of contents.
- Public-facing code and docs use familiar, widely understood names instead of
coined internal jargon.
- Generated code is quarantined under `src/generated/` and never hand-edited.
- Validate untrusted input (API responses, saved credentials, user JSON) with
schemas, not ad-hoc coercion.

**Anti-patterns**

- A grab-bag module that mixes parsing, auth, transport, and routing.
- TODOs, dead code, or aspirational claims in shipped files or the README.

### 3. Test the seams that matter

Cover pure logic exhaustively and protect the safety-critical paths. Skip tests
whose only purpose is coverage of trivial glue.

**What good looks like**

- Pure functions (arg parsing, request building, output formatting) are unit
tested.
- Security-relevant behavior has explicit tests: token precedence, the
destructive-action gate, and credential file permissions (`0600`).
- Test files mirror their source module.

**Anti-patterns**

- Mocking away the exact branch you mean to verify.
- No coverage on the code that can delete data or leak a secret.

### 4. Two readers: humans and agents

Every command is read by a person at a terminal and by an agent over a pipe.
Optimize each for what it needs: humans want maximal visibility and
readability; agents want minimal characters.

**What good looks like**

- Interactive (TTY) output is formatted for scanning: aligned tables, short
summaries, collapsed redundant columns, and color used only as emphasis.
- Piped / non-interactive output is compact and lossless — the full data with
no decoration, no indentation, and nothing the caller must strip.
- The two formats carry the same information; only the presentation differs.
- `--json` always yields machine-parseable output; piping never changes the
meaning of a command, only its verbosity.

**Anti-patterns**

- Spending agent tokens on pretty-printing, banners, color codes, or
repeated values that carry no extra information.
- Truncating or dropping data on the piped path to save space (terseness must
never cost correctness).
- Human-only adornments (spinners, prompts, ANSI codes) leaking into
non-interactive output.
Loading
Loading