From d4aab7de5bdf26b790f4f425ed83438b1fca80d1 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Thu, 10 Sep 2026 09:11:30 -0700 Subject: [PATCH 1/3] Add CLAUDE.md agent guidance Document the module's purpose, verified Makefile/CI commands, package layout, how command registration wires STEP_* env vars and defaults.json, and the conventions (urfave/cli v1, pkg/errors, testify) contributors and coding agents should follow. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01JuSZFSx1cTEnqY55oquacV --- CLAUDE.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8241bb5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,80 @@ +# CLAUDE.md + +This file provides guidance to Claude Code when working with code in this repository. + +## Overview + +`cli-utils` (module `github.com/smallstep/cli-utils`) is a small Go library of shared building blocks for Smallstep's `urfave/cli`-based command-line tools. Its main consumers are [`step`](https://github.com/smallstep/cli) and [`step-ca`](https://github.com/smallstep/certificates), which pin it at a tagged version. It is a public, Apache-2.0 library but is not a stable API: the README warns that other projects should not depend on it and that the API can change at any time. There is no binary here, only packages. + +## Commands + +```bash +make bootstrap # install golangci-lint, govulncheck, gotestsum +make test # unit tests via gotestsum (-short, coverage); this is what `make ci` runs +make race # unit tests with the race detector +make lint # golangci-lint (config fetched from smallstep/workflows) + govulncheck +make fmt # goimports -l -w on all .go files +make # lint + test +``` + +Plain `go` works too and needs no special environment (no private modules, no Docker, no `go generate`): + +```bash +go build ./... +go test -short ./... +go test -run TestParse ./token/ # single test +``` + +`make test` and `make lint` require the tools from `make bootstrap` on `$PATH`. `make lint` needs network access to download the shared golangci config. CI (`.github/workflows/ci.yml`) calls the shared `goCI` reusable workflow with `run-build: false`, so tests and lint are what gate a PR; CodeQL runs with `go build ./...`. + +## Architecture + +``` +cli-utils/ +├── command/ # Global command registry for urfave/cli apps +│ ├── command.go # Register/Retrieve commands; ActionFunc captures the ctx; IsForce() +│ └── version/ # `version` command, registered in init() +├── errs/ # Error constructors with user-facing messages for flag/argument misuse +├── fileutil/ # File writes with overwrite prompts (WriteFile, WriteSnippet, AppendNewLine, ...) +├── step/ # $STEPPATH layout, contexts (profile + authority), defaults.json flag loading +│ ├── config.go # Path(), Home(), BasePath(), Version(), file-location helpers +│ └── context.go # Context/CtxState, contexts.json, SetEnvVar, getConfigVars +├── token/ # JWT claim builders (token.Options) and parsing for step provisioning tokens +│ └── provision/ # provision.Token: builds a signed JWT from token.Options +├── ui/ # promptui-based interactive prompts, validators, colored output to stderr +├── usage/ # Custom help templates and renderer (markdown-ish help text, HTML export) +└── pkg/blackfriday/ # Vendored fork of russross/blackfriday v2 used by usage/ (own LICENSE.txt) +``` + +### How the pieces fit + +- A CLI registers each `cli.Command` with `command.Register`. That calls `step.SetEnvVar`, which gives every flag an `EnvVar` of `STEP_` (uppercased, `-` to `_`) unless one is already set, and installs `getConfigVars` as the command's `Before` hook so unset flags are filled from the active context's `defaults.json`. Set a flag's `EnvVar` to `step.IgnoreEnvVar` to opt it out of both. +- `step` resolves the config root from `STEPPATH` (default `$HOME/.step`) once, via `sync.Once`. With contexts enabled, per-authority config lives under `authorities//` and profiles under `profiles//`; `contexts.json` and `current-context.json` sit at the root. Call `step.Init()` before using these helpers. +- `usage` overrides urfave/cli's `help` command and templates. Command `Description`/`UsageText` strings use a lightweight markdown dialect (`**bold**`, `'''` fenced blocks, `## SECTIONS`) that `usage.Render` turns into terminal output via `pkg/blackfriday`; `step help --html ` exports the same content as HTML. +- `fileutil.WriteFile` and friends consult `command.IsForce()`; without `--force` they prompt through `ui` before overwriting. +- `ui` prints prompts and messages to stderr (never stdout) so command output stays pipeable; `ui_windows.go`/`ui_other.go` are build-tagged for console-mode handling. + +## Conventions + +**CLI framework**: `urfave/cli` v1 (`github.com/urfave/cli`), not v2. Errors returned to users go through `errs` constructors so messages are consistent across `step` commands. + +**Error wrapping**: `github.com/pkg/errors` throughout (`errors.Errorf`, `errors.Wrapf`); `errs.Wrap` normalizes causes for display. Do not introduce `fmt.Errorf("%w")` in a file that otherwise uses `pkg/errors`. + +**Logging**: none. Output goes to `ui.Print*` (stderr) or `fmt.Print*` (stdout) as appropriate. + +**Testing**: `testify` (`assert`/`require`) for new tests; a few older tests still use `github.com/smallstep/assert`. Tests that touch `$STEPPATH` use `t.TempDir()` plus `t.Setenv(step.HomeEnv, ...)` to stay hermetic. Fixtures live in `token/testdata/` (certificates and keys) and `pkg/blackfriday/testdata/` (markdown/HTML pairs). `command/`, `fileutil/`, and `usage/` have no tests. + +**Vendored code**: `pkg/blackfriday/` is a copy of an upstream library with its own license and README. Keep changes there minimal and clearly motivated; the rest of the repo is where Smallstep-specific behavior belongs. + +**Compatibility**: `step` and `step-ca` are the callers. Renaming or changing the signature of an exported symbol breaks them on their next dependency bump, so prefer additive changes and check both consumers before removing anything. + +## Environment Variables + +- `STEPPATH` — root of the step configuration directory (default `$HOME/.step`) +- `HOME` — used to derive the default `STEPPATH`; falls back to `os/user` +- `STEP_` — auto-derived per-flag overrides for any command registered via `command.Register` +- `STEP_IGNORE_ENV_VAR` — sentinel value, not a variable to set: assign it to a flag's `EnvVar` to disable env and defaults.json lookup for that flag + +## Releases + +Versions are git tags (`v0.12.x`). After tagging, bump the dependency in `step` and `step-ca`; there is no release workflow in this repo. From a82013d7b141249cf3f6d04b28130dd8df8b82e8 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Thu, 10 Sep 2026 09:19:06 -0700 Subject: [PATCH 2/3] Move guidance to AGENTS.md, import it from CLAUDE.md AGENTS.md is the vendor-neutral file read by other coding agents. Claude Code does not read it natively, so CLAUDE.md becomes a one-line @AGENTS.md import that loads the same content. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01JuSZFSx1cTEnqY55oquacV --- AGENTS.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 81 +------------------------------------------------------ 2 files changed, 81 insertions(+), 80 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d4055e9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,80 @@ +# AGENTS.md + +Guidance for AI coding agents working in this repository. Claude Code loads it through the one-line `@AGENTS.md` import in `CLAUDE.md`. + +## Overview + +`cli-utils` (module `github.com/smallstep/cli-utils`) is a small Go library of shared building blocks for Smallstep's `urfave/cli`-based command-line tools. Its main consumers are [`step`](https://github.com/smallstep/cli) and [`step-ca`](https://github.com/smallstep/certificates), which pin it at a tagged version. It is a public, Apache-2.0 library but is not a stable API: the README warns that other projects should not depend on it and that the API can change at any time. There is no binary here, only packages. + +## Commands + +```bash +make bootstrap # install golangci-lint, govulncheck, gotestsum +make test # unit tests via gotestsum (-short, coverage); this is what `make ci` runs +make race # unit tests with the race detector +make lint # golangci-lint (config fetched from smallstep/workflows) + govulncheck +make fmt # goimports -l -w on all .go files +make # lint + test +``` + +Plain `go` works too and needs no special environment (no private modules, no Docker, no `go generate`): + +```bash +go build ./... +go test -short ./... +go test -run TestParse ./token/ # single test +``` + +`make test` and `make lint` require the tools from `make bootstrap` on `$PATH`. `make lint` needs network access to download the shared golangci config. CI (`.github/workflows/ci.yml`) calls the shared `goCI` reusable workflow with `run-build: false`, so tests and lint are what gate a PR; CodeQL runs with `go build ./...`. + +## Architecture + +``` +cli-utils/ +├── command/ # Global command registry for urfave/cli apps +│ ├── command.go # Register/Retrieve commands; ActionFunc captures the ctx; IsForce() +│ └── version/ # `version` command, registered in init() +├── errs/ # Error constructors with user-facing messages for flag/argument misuse +├── fileutil/ # File writes with overwrite prompts (WriteFile, WriteSnippet, AppendNewLine, ...) +├── step/ # $STEPPATH layout, contexts (profile + authority), defaults.json flag loading +│ ├── config.go # Path(), Home(), BasePath(), Version(), file-location helpers +│ └── context.go # Context/CtxState, contexts.json, SetEnvVar, getConfigVars +├── token/ # JWT claim builders (token.Options) and parsing for step provisioning tokens +│ └── provision/ # provision.Token: builds a signed JWT from token.Options +├── ui/ # promptui-based interactive prompts, validators, colored output to stderr +├── usage/ # Custom help templates and renderer (markdown-ish help text, HTML export) +└── pkg/blackfriday/ # Vendored fork of russross/blackfriday v2 used by usage/ (own LICENSE.txt) +``` + +### How the pieces fit + +- A CLI registers each `cli.Command` with `command.Register`. That calls `step.SetEnvVar`, which gives every flag an `EnvVar` of `STEP_` (uppercased, `-` to `_`) unless one is already set, and installs `getConfigVars` as the command's `Before` hook so unset flags are filled from the active context's `defaults.json`. Set a flag's `EnvVar` to `step.IgnoreEnvVar` to opt it out of both. +- `step` resolves the config root from `STEPPATH` (default `$HOME/.step`) once, via `sync.Once`. With contexts enabled, per-authority config lives under `authorities//` and profiles under `profiles//`; `contexts.json` and `current-context.json` sit at the root. Call `step.Init()` before using these helpers. +- `usage` overrides urfave/cli's `help` command and templates. Command `Description`/`UsageText` strings use a lightweight markdown dialect (`**bold**`, `'''` fenced blocks, `## SECTIONS`) that `usage.Render` turns into terminal output via `pkg/blackfriday`; `step help --html ` exports the same content as HTML. +- `fileutil.WriteFile` and friends consult `command.IsForce()`; without `--force` they prompt through `ui` before overwriting. +- `ui` prints prompts and messages to stderr (never stdout) so command output stays pipeable; `ui_windows.go`/`ui_other.go` are build-tagged for console-mode handling. + +## Conventions + +**CLI framework**: `urfave/cli` v1 (`github.com/urfave/cli`), not v2. Errors returned to users go through `errs` constructors so messages are consistent across `step` commands. + +**Error wrapping**: `github.com/pkg/errors` throughout (`errors.Errorf`, `errors.Wrapf`); `errs.Wrap` normalizes causes for display. Do not introduce `fmt.Errorf("%w")` in a file that otherwise uses `pkg/errors`. + +**Logging**: none. Output goes to `ui.Print*` (stderr) or `fmt.Print*` (stdout) as appropriate. + +**Testing**: `testify` (`assert`/`require`) for new tests; a few older tests still use `github.com/smallstep/assert`. Tests that touch `$STEPPATH` use `t.TempDir()` plus `t.Setenv(step.HomeEnv, ...)` to stay hermetic. Fixtures live in `token/testdata/` (certificates and keys) and `pkg/blackfriday/testdata/` (markdown/HTML pairs). `command/`, `fileutil/`, and `usage/` have no tests. + +**Vendored code**: `pkg/blackfriday/` is a copy of an upstream library with its own license and README. Keep changes there minimal and clearly motivated; the rest of the repo is where Smallstep-specific behavior belongs. + +**Compatibility**: `step` and `step-ca` are the callers. Renaming or changing the signature of an exported symbol breaks them on their next dependency bump, so prefer additive changes and check both consumers before removing anything. + +## Environment Variables + +- `STEPPATH` — root of the step configuration directory (default `$HOME/.step`) +- `HOME` — used to derive the default `STEPPATH`; falls back to `os/user` +- `STEP_` — auto-derived per-flag overrides for any command registered via `command.Register` +- `STEP_IGNORE_ENV_VAR` — sentinel value, not a variable to set: assign it to a flag's `EnvVar` to disable env and defaults.json lookup for that flag + +## Releases + +Versions are git tags (`v0.12.x`). After tagging, bump the dependency in `step` and `step-ca`; there is no release workflow in this repo. diff --git a/CLAUDE.md b/CLAUDE.md index 8241bb5..43c994c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,80 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code when working with code in this repository. - -## Overview - -`cli-utils` (module `github.com/smallstep/cli-utils`) is a small Go library of shared building blocks for Smallstep's `urfave/cli`-based command-line tools. Its main consumers are [`step`](https://github.com/smallstep/cli) and [`step-ca`](https://github.com/smallstep/certificates), which pin it at a tagged version. It is a public, Apache-2.0 library but is not a stable API: the README warns that other projects should not depend on it and that the API can change at any time. There is no binary here, only packages. - -## Commands - -```bash -make bootstrap # install golangci-lint, govulncheck, gotestsum -make test # unit tests via gotestsum (-short, coverage); this is what `make ci` runs -make race # unit tests with the race detector -make lint # golangci-lint (config fetched from smallstep/workflows) + govulncheck -make fmt # goimports -l -w on all .go files -make # lint + test -``` - -Plain `go` works too and needs no special environment (no private modules, no Docker, no `go generate`): - -```bash -go build ./... -go test -short ./... -go test -run TestParse ./token/ # single test -``` - -`make test` and `make lint` require the tools from `make bootstrap` on `$PATH`. `make lint` needs network access to download the shared golangci config. CI (`.github/workflows/ci.yml`) calls the shared `goCI` reusable workflow with `run-build: false`, so tests and lint are what gate a PR; CodeQL runs with `go build ./...`. - -## Architecture - -``` -cli-utils/ -├── command/ # Global command registry for urfave/cli apps -│ ├── command.go # Register/Retrieve commands; ActionFunc captures the ctx; IsForce() -│ └── version/ # `version` command, registered in init() -├── errs/ # Error constructors with user-facing messages for flag/argument misuse -├── fileutil/ # File writes with overwrite prompts (WriteFile, WriteSnippet, AppendNewLine, ...) -├── step/ # $STEPPATH layout, contexts (profile + authority), defaults.json flag loading -│ ├── config.go # Path(), Home(), BasePath(), Version(), file-location helpers -│ └── context.go # Context/CtxState, contexts.json, SetEnvVar, getConfigVars -├── token/ # JWT claim builders (token.Options) and parsing for step provisioning tokens -│ └── provision/ # provision.Token: builds a signed JWT from token.Options -├── ui/ # promptui-based interactive prompts, validators, colored output to stderr -├── usage/ # Custom help templates and renderer (markdown-ish help text, HTML export) -└── pkg/blackfriday/ # Vendored fork of russross/blackfriday v2 used by usage/ (own LICENSE.txt) -``` - -### How the pieces fit - -- A CLI registers each `cli.Command` with `command.Register`. That calls `step.SetEnvVar`, which gives every flag an `EnvVar` of `STEP_` (uppercased, `-` to `_`) unless one is already set, and installs `getConfigVars` as the command's `Before` hook so unset flags are filled from the active context's `defaults.json`. Set a flag's `EnvVar` to `step.IgnoreEnvVar` to opt it out of both. -- `step` resolves the config root from `STEPPATH` (default `$HOME/.step`) once, via `sync.Once`. With contexts enabled, per-authority config lives under `authorities//` and profiles under `profiles//`; `contexts.json` and `current-context.json` sit at the root. Call `step.Init()` before using these helpers. -- `usage` overrides urfave/cli's `help` command and templates. Command `Description`/`UsageText` strings use a lightweight markdown dialect (`**bold**`, `'''` fenced blocks, `## SECTIONS`) that `usage.Render` turns into terminal output via `pkg/blackfriday`; `step help --html ` exports the same content as HTML. -- `fileutil.WriteFile` and friends consult `command.IsForce()`; without `--force` they prompt through `ui` before overwriting. -- `ui` prints prompts and messages to stderr (never stdout) so command output stays pipeable; `ui_windows.go`/`ui_other.go` are build-tagged for console-mode handling. - -## Conventions - -**CLI framework**: `urfave/cli` v1 (`github.com/urfave/cli`), not v2. Errors returned to users go through `errs` constructors so messages are consistent across `step` commands. - -**Error wrapping**: `github.com/pkg/errors` throughout (`errors.Errorf`, `errors.Wrapf`); `errs.Wrap` normalizes causes for display. Do not introduce `fmt.Errorf("%w")` in a file that otherwise uses `pkg/errors`. - -**Logging**: none. Output goes to `ui.Print*` (stderr) or `fmt.Print*` (stdout) as appropriate. - -**Testing**: `testify` (`assert`/`require`) for new tests; a few older tests still use `github.com/smallstep/assert`. Tests that touch `$STEPPATH` use `t.TempDir()` plus `t.Setenv(step.HomeEnv, ...)` to stay hermetic. Fixtures live in `token/testdata/` (certificates and keys) and `pkg/blackfriday/testdata/` (markdown/HTML pairs). `command/`, `fileutil/`, and `usage/` have no tests. - -**Vendored code**: `pkg/blackfriday/` is a copy of an upstream library with its own license and README. Keep changes there minimal and clearly motivated; the rest of the repo is where Smallstep-specific behavior belongs. - -**Compatibility**: `step` and `step-ca` are the callers. Renaming or changing the signature of an exported symbol breaks them on their next dependency bump, so prefer additive changes and check both consumers before removing anything. - -## Environment Variables - -- `STEPPATH` — root of the step configuration directory (default `$HOME/.step`) -- `HOME` — used to derive the default `STEPPATH`; falls back to `os/user` -- `STEP_` — auto-derived per-flag overrides for any command registered via `command.Register` -- `STEP_IGNORE_ENV_VAR` — sentinel value, not a variable to set: assign it to a flag's `EnvVar` to disable env and defaults.json lookup for that flag - -## Releases - -Versions are git tags (`v0.12.x`). After tagging, bump the dependency in `step` and `step-ca`; there is no release workflow in this repo. +@AGENTS.md From 31d970008fc033501f32b30c18a2898b23b7fdd7 Mon Sep 17 00:00:00 2001 From: Carl Tashian Date: Thu, 10 Sep 2026 09:38:35 -0700 Subject: [PATCH 3/3] Move Claude Code stub to .claude/CLAUDE.md Per review: keep the repo root to AGENTS.md and put the Claude Code import stub under .claude/, importing @../AGENTS.md. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01JuSZFSx1cTEnqY55oquacV --- .claude/CLAUDE.md | 1 + AGENTS.md | 2 +- CLAUDE.md | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 .claude/CLAUDE.md delete mode 100644 CLAUDE.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 0000000..dba71e9 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +@../AGENTS.md diff --git a/AGENTS.md b/AGENTS.md index d4055e9..302ab5e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md -Guidance for AI coding agents working in this repository. Claude Code loads it through the one-line `@AGENTS.md` import in `CLAUDE.md`. +Guidance for AI coding agents working in this repository. Claude Code loads it through the one-line `@../AGENTS.md` import in `.claude/CLAUDE.md`. ## Overview diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 43c994c..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -@AGENTS.md