From e489e4b26c3a57489dd84d5c5a16db5fbfb4c8f8 Mon Sep 17 00:00:00 2001 From: tytv2 Date: Tue, 7 Jul 2026 09:49:11 +0700 Subject: [PATCH] docs: add per-product CLAUDE.md (tier 2) and agent skills Two-tier agent context, mirroring greennode-mcp: - go/cmd/vks/CLAUDE.md and go/cmd/vserver/CLAUDE.md capture product-specific quirks (vks: 0-based pagination/pageSize, /v1, async kubeconfig, control-plane create; vserver: 1-based pagination/size, /v2/{projectID}, project_id required, noun-verb command style). - .claude/skills/new-cli-command and .claude/skills/release-cli guide agents through scaffolding/adding commands and the release-please release flow. --- .claude/skills/new-cli-command/SKILL.md | 46 +++++++++++++++++++++++++ .claude/skills/release-cli/SKILL.md | 37 ++++++++++++++++++++ go/cmd/vks/CLAUDE.md | 41 ++++++++++++++++++++++ go/cmd/vserver/CLAUDE.md | 38 ++++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 .claude/skills/new-cli-command/SKILL.md create mode 100644 .claude/skills/release-cli/SKILL.md create mode 100644 go/cmd/vks/CLAUDE.md create mode 100644 go/cmd/vserver/CLAUDE.md diff --git a/.claude/skills/new-cli-command/SKILL.md b/.claude/skills/new-cli-command/SKILL.md new file mode 100644 index 0000000..d2b5ecd --- /dev/null +++ b/.claude/skills/new-cli-command/SKILL.md @@ -0,0 +1,46 @@ +--- +name: new-cli-command +description: Use when adding a new command or a new product command group to greennode-cli ("add a command", "tạo CLI cho ", "implement grn ", "new subcommand"). Guides scaffolding and the command implementation flow. +--- + +# Adding to greennode-cli + +`grn` is a single Go binary; each product is a self-registering command group under +`go/cmd//`. Read the root `CLAUDE.md` (repo conventions) and the target +product's `go/cmd//CLAUDE.md` (API quirks) first. + +## New product CLI → scaffold + +```bash +./scripts/new-product # lowercase, valid Go package name (e.g. vdb) +``` + +Generates the package (parent command, example command, helpers, starter test, +product CLAUDE.md), docs, self-registration in `go/cmd/register.go`, and CODEOWNERS +lines. Then: add `_endpoint` to `internal/config` REGIONS, replace the +`list-examples` starter, and fill in the product CLAUDE.md. + +## New command in an existing product (TDD) + +1. Add `go/cmd//_.go`; register it in the product's parent + `init()`. +2. Follow the conventions (the `go/cmd/conventions_test.go` test enforces them): + - Name is `verb-noun` (canonical verbs: list/get/create/update/delete/configure/ + upgrade/generate/validate/wait). vServer uses `noun verb` nesting — match the + product's existing style. + - `validator.ValidateID(id, "flag")` before interpolating an ID into a URL. + - Struct-valued flags via `cli.ParseStructFlag` / `ParseStructFlagTyped` + (shorthand `k=v,k2=v2` or JSON). + - Destructive commands (`delete`, `stop`, `reboot`): add `--dry-run` and + `--force`; use `cli.DryRunNotice` / `cli.Confirm`. + - Output via `cli.Output` (product helper `outputResult`). +3. Write table-driven tests (use `httptest`); build/test with the macOS linker + workaround: `CGO_ENABLED=1 go test -ldflags='-linkmode=external' ./...`. +4. Add an AWS-CLI-style doc page under `docs/commands//` (type, Required, + Default, Possible values, Constraints; Members + Shorthand/JSON for struct flags) + and link it in that product's `index.md`. + +## PR + +Open a PR with a **Conventional Commit** title (`feat(...)`, `fix(...)`) — it becomes +the squash-merge commit release-please reads. Do not edit the version or CHANGELOG. diff --git a/.claude/skills/release-cli/SKILL.md b/.claude/skills/release-cli/SKILL.md new file mode 100644 index 0000000..6efde9d --- /dev/null +++ b/.claude/skills/release-cli/SKILL.md @@ -0,0 +1,37 @@ +--- +name: release-cli +description: Use when releasing greennode-cli, cutting a version, tagging, or publishing binaries ("release the CLI", "cut a release", "bump version", "publish grn"). Everything is release-please driven — never bump versions or tag by hand. +--- + +# Releasing greennode-cli + +Versioning and `CHANGELOG.md` are automated by **release-please**. **Never** edit +the version (`go/cmd/root.go` `cliVersion`), edit `CHANGELOG.md`, or create tags by +hand — release-please derives all of it from commit messages. + +## How a release happens + +1. `feat:` / `fix:` PRs merge to `main` (squash-merged; the PR title is the commit + message, enforced by the `Conventional Commits title` check). + - `fix:` → patch, `feat:` → minor, `feat!:` / `BREAKING CHANGE:` → major. +2. release-please opens/refreshes a **`chore: release main`** PR that bumps + `go/cmd/root.go` and updates `CHANGELOG.md`. +3. **Squash-merge that release PR** to publish: it tags `vX.Y.Z`, creates the GitHub + Release, and `release.yml` builds + attaches the multi-platform binaries. + +## Verify + +```bash +gh pr list # find the "chore: release main" PR +gh run list --workflow=release-please.yml # after merge: release + build +gh release view vX.Y.Z # binaries attached +``` + +## Troubleshooting + +- **Release PR checks don't start** → default `GITHUB_TOKEN` limitation; close & + reopen the PR once, or set a `RELEASE_PLEASE_TOKEN` PAT (contents + pull-requests + write) so checks auto-run. +- **Wrong version bump** → fix the offending PR title; never edit the version file. +- **Emergency/manual**: `release.yml` still accepts a tag push (`git tag vX.Y.Z && + git push --tags`) or `workflow_dispatch`. Prefer release-please for normal releases. diff --git a/go/cmd/vks/CLAUDE.md b/go/cmd/vks/CLAUDE.md new file mode 100644 index 0000000..96141ae --- /dev/null +++ b/go/cmd/vks/CLAUDE.md @@ -0,0 +1,41 @@ +# VKS CLI — product notes + +Tier-2 notes for the `grn vks` command group. The root `CLAUDE.md` has the +repo-wide conventions; this file records what is specific to VKS. + +## Client & endpoint + +- `createClient(cmd)` → `cli.NewClient(cmd, "vks")`. The endpoint is resolved from + `internal/config` REGIONS key `vks_endpoint` + (`https://vks.api.vngcloud.vn` HCM-3, `https://vks-han-1.api.vngcloud.vn` HAN). +- Auth: shared GreenNode IAM bearer token (see `internal/auth`). + +## API quirks + +- **Pagination is 0-based**: params `page` (from 0) + `pageSize`. The `list-*` + commands default `--page -1`, a sentinel meaning "auto-paginate all pages". +- Paths are under `/v1/clusters/...`. +- Cluster/node-group **status** values: `CREATING`, `ACTIVE`, `ERROR` (waiters key + on these; see `wait.go`). +- **Kubeconfig is asynchronous**: `generate-kubeconfig` (POST) requests generation, + then `update-kubeconfig` (GET) fetches + merges once status is `ACTIVE` + (`NONE`/`CREATING`/`ERROR` are the other states). + +## Command specifics + +- `create-cluster` creates the **control plane only**. The API's `nodeGroups` array + is deprecated — do not send it; add workers with `create-nodegroup`. +- `update-nodegroup`: all body fields optional. Labels/tags/taints are **deprecated** + on this endpoint — use `update-nodegroup-metadata`. +- `config-auto-upgrade` (the former `set-auto-upgrade-config` is a deprecated alias). +- `--k8s-version` values come from `list-cluster-versions`. +- Network types: `TIGERA`, `CILIUM_OVERLAY`, `CILIUM_NATIVE_ROUTING`; `--cidr` is + required for `TIGERA`/`CILIUM_OVERLAY`. OS images: `ubuntu`, `linux`, `rocky`. + +## Struct-valued flags (shorthand `k=v,k2=v2` or JSON) + +- `--auto-scale` → `autoScaleConfig` (int: `minSize`, `maxSize`) +- `--upgrade-config` → `upgradeConfig` (int: `maxSurge`, `maxUnavailable`; string `strategy`) +- `--placement-group` → `placementGroupConfigDto` (strings) +- `--auto-upgrade-config` → `autoUpgradeConfig` (`weekdays` — use JSON for multiple days; `time`) +- `--auto-healing-config` → `autoHealingConfig` (bool `enableAutoHealing`, int `timeoutUnhealthy`) diff --git a/go/cmd/vserver/CLAUDE.md b/go/cmd/vserver/CLAUDE.md new file mode 100644 index 0000000..63442d7 --- /dev/null +++ b/go/cmd/vserver/CLAUDE.md @@ -0,0 +1,38 @@ +# vServer CLI — product notes + +Tier-2 notes for the `grn vserver` command group. The root `CLAUDE.md` has the +repo-wide conventions; this file records what is specific to vServer. + +## Client & endpoint (differs from vks) + +- Build the client with **`vserverclient.BuildClient(cmd)`**, which returns + `(client, cfg, error)` — NOT `cli.NewClient`. Most helpers wrap it (see + `helpers.go` in each subpackage). +- Endpoint: `internal/config` REGIONS key `vserver_endpoint` + (`https://hcm-3.api.vngcloud.vn/vserver/vserver-gateway`). +- **`project_id` is required** and goes in the URL path. Get it via + `vserverclient.ProjectID(cfg)` (from `grn configure` or `GRN_DEFAULT_PROJECT_ID`); + it errors clearly if unset. + +## API quirks (differ from vks — do not copy vks assumptions) + +- **Pagination is 1-based**: params `page` (from **1**) + **`size`** (note: `size`, + not `pageSize`). +- Paths are under **`/v2/{projectID}/...`** (servers, volumes, networks, secgroups), + except **images** which are under `/v1/{projectID}/images/...`. +- Subnets carry both `uuid` and `id`; list/complete code prefers `uuid`, falling + back to `id`. + +## Command style (differs from vks) + +- vServer uses **noun-group + verb**: `grn vserver server create`, `volume delete`, + `secgroup rule create` — versus vks's flat `create-nodegroup`. Both satisfy the + conventions test (the leaf verb is canonical). Keep new vServer commands in this + nested style for consistency within the product. +- Destructive commands (`delete`, `server stop`/`reboot`) carry `--dry-run` + + `--force` (see the conventions test). + +## Output + +- List/detail commands render selected columns via `vserverclient.OutputWithColumns` + (see the per-subpackage `helpers.go` column definitions).