Go package manager for WordPress plugins and themes. Cobra-based CLI, binary
name wpm. See @README.md for the user-facing overview.
This is a Go project. package.json, bun.lock, and node_modules/ exist
only to vendor oxfmt for Markdown/JSON/YAML formatting. Never propose
JavaScript for runtime code, and never add a Node dep for anything but
formatting.
- Build:
go build -o build/wpm ./cmd/wpm(or./scripts/build/binary) - Run without building:
go run ./cmd/wpm <command> - Format Go:
golangci-lint fmt— never callgo fmt,gofmt, orgofumptdirectly. Projectgciorder,gofumptrules, andinterface{} → anyrewrite all flow through this command. - Lint Go:
golangci-lint run - Format non-Go:
bunx format - Test single package:
go test ./pkg/pm/installer/... - Test with race detector (required for
pkg/pm/installerorpkg/pm/resolutionwork):go test -race ./... - Regenerate CLI reference docs:
./scripts/docs/generate-md
Three-layer separation — do not collapse layers for convenience:
cmd/wpm/— entrypoint and signal handlingcmd/docgen/— regenerates marker regions indocs/cli/*.mdcli/— arg parsing, output, error→exit-code mapping. Commands registered incli/command/commands/commands.go. Thecli/layer must not contain package-manager logic — it builds an options struct and delegates topkg/.pkg/pm/{wpmjson,wpmlock,wpmignore,registry,resolution,installer,workspace,signatures}— the package-manager enginepkg/api/— registry HTTP client and cachepkg/archive/— tar/zip extraction (handles untrusted content)pkg/output/,pkg/progress/,pkg/streams/— CLI UI primitivespkg/{config,version,wp,jsonpretty,asciisanitizer,unsafeconv}— utilities
- No
fmt.Printf/Printlninpkg/. Return values or usepkg/output/pkg/progressso the CLI can honor--verboseand future JSON output.forbidigoenforces. - Errors: never
panic()outsidemain. Wrap with%w. Inspect witherrors.Is/errors.As— never string-match error messages. - Concurrency: use
golang.org/x/sync/errgroup. Always honorcontext.Contextcancellation. - Logging: use
github.com/rs/zerolog(via the package-levellogglobal), not stdliblog(depguard enforces). Stdlibio/ioutilis denied — useos/io. - CLI reference docs (
docs/cli/*.md) have an auto-generated marker block (<!---MARKER_GEN_START-->/<!---MARKER_GEN_END-->). Never edit inside the markers. Only## Descriptionand## Examplesare read by the generator — don't add other top-level##headings. Full conventions and workflow:@docs/README.md.
-
Append tests to the existing
_test.gofor the code you're changing. Create a new test file only when adding a new source file with no natural test home. -
Verify the test actually tests your change. A passing test that would also pass on
origin/mainisn't testing your fix:git stash go test -run TestName ./path/to/pkg # should FAIL git stash pop go test -run TestName ./path/to/pkg # should PASS
If it passes in both states, rewrite the test.
-
Never use
time.Sleepto wait for a condition. Use channels,sync.WaitGroup,contextdeadlines, or bounded polling against the actual condition. -
Mock the registry with
net/http/httptest. Existing patterns:pkg/api/,pkg/pm/registry/. -
Use
t.TempDir(),t.Setenv(),t.Helper()(lint-enforced). -
For CLI output assertions, drive through
cli/command.Cliand captureOut()/Err(). Don't touch global state.
- Explicit ask → do it. "Commit", "push", "open a PR" — perform the operation and report what was done. Don't re-ask.
- Ambiguous ask → confirm first. If changes were described but no push/PR/tag word appeared, confirm before any remote-write operation. Local commits on a working branch don't need confirmation.
- Never push directly to
main. Always open a PR. - Never bypass safety controls: no
--no-verify, no force-push to shared branches, no skipping signature verification. - Branch names: any reasonable name. Attribution belongs in the PR body.
Don't assume functions, interfaces, fields, methods, flags, or config keys exist. Search the repo or read the file first. Common traps:
- CLI flags →
cli/flags/andcli/command/<name>/ wpm.jsonfields →pkg/pm/wpmjson/types/types.go- Command names / aliases →
cli/command/commands/commands.go Cliinterface methods →cli/command/cli.go- Issue / PR numbers (in tests, comments, commits) — must be real, never placeholders
Project documentation is hosted at https://wpm.so/docs. Do not preload these; fetch on demand when the task touches the area.
-
Discover what's available: https://wpm.so/docs/sitemap.xml
-
Read a page as markdown — transform the URL:
page: https://wpm.so/docs/fundamentals/dependencies md: https://wpm.so/docs/llms.mdx/docs/fundamentals/dependencies/content.md
Typical triggers: wpm.json schema or scripts work → fundamentals docs;
registry/auth/tokens → registry docs; resolution or lockfile semantics →
resolution docs; signature verification → signatures docs.
- Project root: don't assume
os.Getwd()is thewpm.jsonroot in nested package logic. Walk up, or accept the root as a parameter. wpm.jsonfield changes require all of: updatepkg/pm/wpmjson/types/types.go(andvalidator/if constrained), updateREADME.mdanddocs/, add a manifest test case.- Untrusted archives:
pkg/archiveandpkg/pm/installerextract registry content. Zip Slip is a real risk — reuse the existing extraction helpers, don't reimplement.