Skip to content

feat(cmd): add subcommand docs to browse docs.stripe.com#1663

Merged
joelzwarrington merged 151 commits into
masterfrom
joelw/docs-subcommand
Jun 29, 2026
Merged

feat(cmd): add subcommand docs to browse docs.stripe.com#1663
joelzwarrington merged 151 commits into
masterfrom
joelw/docs-subcommand

Conversation

@joelzwarrington

Copy link
Copy Markdown
Contributor

Introduces stripe docs as a built-in subcommand. It was prototyped as a standalone plugin and is bundled into the CLI directly without requiring installation.

CleanShot.2026-06-11.at.15.43.08.mp4

Subcommands

stripe docs <path>

Opens a docs page by its path. In a terminal it launches an interactive browser; with --no-tui (or when called from an AI agent) it renders plain Markdown to stdout.

CleanShot.2026-06-11.at.15.44.00.mp4

stripe docs search <query>

Searches docs.stripe.com.

CleanShot.2026-06-11.at.15.45.09.mp4

stripe docs api <resource> — API reference lookup

Looks up API reference pages by resource name (product), HTTP method + path (GET /v1/products), or event name (product.created). Resolves the canonical reference page and renders it.

CleanShot.2026-06-11.at.15.46.12.mp4

stripe docs skills list — agent skills

Lists AI agent skills available from docs.stripe.com, with install instructions for connecting them to Claude Code, Cursor, Copilot, etc.

CleanShot.2026-06-11.at.15.47.13.mp4

As the subcommand was developed as a plugin outside of this repository, it contains several new packages at internal/. To keep this PR as simple as possible and easier to review, minimal changes have been done to migrate the code from https://github.com/stripe/stripe-cli-docs-plugin-go to here.

Post merge, I'll be cleaning up some of the duplicated packages. i.e. internal/agent is very similar to pkg/useragent.

joelzwarrington and others added 30 commits May 23, 2026 17:40
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
RootCommand wraps cobra.Command and exposes WithOptions() for
future dependency injection (e.g. HTTP client). Root() provides
access to the underlying cobra command for bootstrap and docgen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
- golangci-lint v2 config with opinionated linter set
- goreleaser v2 with ldflags version injection, cosign signing,
  and grouped changelog
- Taskfile with build/run/test/lint/docs/clean tasks
- GitHub Actions CI workflow with lint, build, and release jobs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
internal/tools/docgen generates per-command Markdown from the cobra
command tree, with optional YAML front matter for static sites.
Wired into Taskfile as `task docs`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
feat: bootstrap stripe docs plugin
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Introduces internal/docs package with a configurable HTTP client that
fetches documentation pages with filesystem caching and TTL-based eviction.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
…nt type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
- Wrap os errors with fmt.Errorf for wrapcheck compliance
- Add doc comments to exported Get and Set methods (revive)
- Tighten directory permissions to 0750 and file to 0600 (gosec)
- Explicitly discard os.Remove and resp.Body.Close errors (errcheck)
- Remove trailing blank line at end of cache.go (gofumpt)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Introduces a new `markdown` package which:
- uses goldmark for parsing the Markdown into an AST document
- uses glamour for rendering documents with style

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
feat(markdown): add parsing and rendering
Move request construction into callers so do() can be reused by future
endpoints (e.g. search) with different methods and accept types. Also
switch FetchPage to request text/plain and validate content-type
dynamically against the request's Accept header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Sort query parameters before using the resolved URL as a cache key so
that different orderings of the same params produce a cache hit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
feat(docs-client): add HTTP client for fetching docs.stripe.com pages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Move default initialization of the docs client and markdown renderer
from New() into WithOptions(), creating them only if not explicitly
provided. This simplifies main.go to just pass the version.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Strip ANSI codes and disable the pager when invoked by an AI coding
agent (Claude Code, Cursor, Codex, Gemini CLI, etc.) to save tokens
and avoid broken output in non-interactive contexts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
feat(cmd): wire page fetching into root command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
joelzwarrington and others added 4 commits June 11, 2026 22:14
- gofmt/goimports: reformat files and fix import group ordering
  (stripe-cli imports in their own group per goimports local-prefix config)
- misspell: cancelled→canceled, behaviour→behavior, initialised→initialized
- dogsled: suppress triple-blank-identifier in cache_test (unavoidable,
  test only cares about the error return)
- gocritic/unlambda: replace lambda wrapper with strings.ToLower directly
- gocritic/ifElseChain: rewrite two if-else chains in model.View as switch
- staticcheck/SA4006: drop two unused model assignments in model_test.go
- gocyclo: extract applyPage, handleSelected, and handleKey helpers from
  Model.Update to bring cyclomatic complexity from 53 down to within limit
- remove internal/tools/docgen (plugin-specific; can be added back later)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Windows does not honor Unix-style mode bits for read access, so
chmod 000 had no effect and the expected error was never returned.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Same reason as the unreadable-file skip: chmod 0555 has no effect on
Windows directory write access, so the expected error was never raised.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
@joelzwarrington joelzwarrington marked this pull request as ready for review June 12, 2026 03:11

@tomer-stripe tomer-stripe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple high level notes but my high level is let's make this conform better to the existing design patterns in the cli. few main things

  • let's reuse existing packages where we have them
  • we don't have a top-level internal command. let's create a docs specific one in pkg for anything there
  • i think we'll want to make skills its own top level command, not namespaced by docs. let's talk to folks about that one

Comment thread .github/workflows/canary-test.yml Outdated
Comment thread internal/agent/agent.go Outdated
Comment thread internal/agentskills/skills.go Outdated
Comment thread internal/browser/browser.go Outdated
Comment thread internal/browser/browser.go Outdated
Comment thread pkg/cmd/docs/root.go Outdated
Comment thread pkg/cmd/docs/root.go Outdated
Comment thread pkg/cmd/docs/root.go Outdated
Comment thread pkg/cmd/docs/root.go Outdated
Comment thread pkg/cmd/docs/skills.go Outdated
joelzwarrington and others added 15 commits June 18, 2026 17:38
The reviewer flagged that skills shouldn't be namespaced under `stripe
docs` — it belongs as a top-level command. Removing it here keeps the
docs subcommand focused and avoids shipping the wrong UX while the
correct placement is decided.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The internal/agent package duplicated logic already in pkg/useragent,
and the docs client was building its own user-agent string instead of
using the one the rest of the CLI sends. Consolidating ensures the docs
client is consistent with telemetry elsewhere in the CLI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The flag name --non-interactive better describes the intent (suppress all
interactive UI, not just the TUI specifically) and is more consistent with
conventions used elsewhere in the CLI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The internal/browser package duplicated browser-launching logic that
already existed in pkg/open. Keeping a separate package for a single
use-case added unnecessary indirection; consolidating reduces the
internal package count and puts the URL safety validation where it can
be shared by other callers if needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The rest of the CLI uses logrus for logging; the docs packages were using
log/slog with a custom charm.land handler. Consolidating on logrus removes
the extra dependency and makes log output consistent with the rest of the
CLI's formatting and level configuration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The rest of the CLI uses afero to decouple filesystem operations from
OS-specific behavior and to enable in-memory testing without temp
directories. Adopting it here removes the Windows-specific test skips
and eliminates the need to chmod files to simulate error conditions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
These were pulled in by the slog/charm logger removed in c579963.
Running go mod tidy after that change cleans up the now-unused entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The previous name read as if the function was acting on the page rather
than updating the model with new page data, which was misleading at the
call sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The reviewer flagged that new packages should live under pkg/ rather than
a top-level internal/ directory. Nesting under pkg/docs/ makes the
ownership clear and keeps docs-specific packages grouped together.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The palette component was an external dependency on a personal repo
(github.com/joelzwarrington/foam). Bringing it in-tree removes that
external dependency, making the codebase self-contained and avoiding
reliance on an upstream that could change or disappear.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The reviewer flagged that pinning to a specific patch release is
unnecessary and should be kept as a separate change from feature work.
Keeping it at 1.26.0 avoids mixing version bumps into this PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The files were not run through gofmt before the initial commit, causing
golangci-lint to flag them. Keeping formatting consistent avoids noise
in future diffs and keeps CI green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The vendored palette package carried British spellings and a View()
function with cyclomatic complexity above the project threshold. Fixing
these up-front keeps the linter clean and avoids the issues surfacing
in future review cycles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
The test was written when the docs command lived as a standalone plugin
with its own user-agent prefix. Now that it's part of the CLI, the
assertion needs to match the standard CLI user-agent format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Resolved three conflicts: took the newer go-git and sha1cd versions
from master, added the directory plugin hint while omitting docs (now
a built-in command), and updated the AI agent help string accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude

@vcheung-stripe vcheung-stripe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only blocking thing I have is to update the help text examples, otherwise lgtm!

Comment thread pkg/cmd/docs/root.go Outdated
Comment thread pkg/cmd/docs/root.go
Comment thread pkg/cmd/docs/search.go
- Prefix all help text examples with `stripe docs` instead of bare `docs`
- Make --no-pager imply non-interactive mode (skip TUI when --no-pager is set)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
@joelzwarrington joelzwarrington merged commit c88ffd0 into master Jun 29, 2026
14 checks passed
@joelzwarrington joelzwarrington deleted the joelw/docs-subcommand branch June 29, 2026 19:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants