actualize claude instructions
This commit is contained in:
parent
1479022388
commit
8e3a9f2cbd
28
.claude/commands/new-package.md
Normal file
28
.claude/commands/new-package.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Scaffold a new internal domain package named `$ARGUMENTS`.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Create `internal/$ARGUMENTS/$ARGUMENTS.go`:
|
||||||
|
- Package declaration `package $ARGUMENTS`
|
||||||
|
- One exported concrete type named after the package's responsibility (e.g. `Service`, `Store`, `Client`, `Parser`)
|
||||||
|
- Constructor: `func New(...) *<Type>` — only accept dependencies the type actually needs; leave the signature empty if none are obvious yet
|
||||||
|
- At least one exported method stub representing the package's primary operation; return `result.Expect[T]` if the operation can fail
|
||||||
|
- Doc comment on every exported symbol (the linter enforces this)
|
||||||
|
|
||||||
|
2. Create `internal/$ARGUMENTS/$ARGUMENTS_test.go`:
|
||||||
|
- Package: `package $ARGUMENTS_test` (black-box)
|
||||||
|
- Declare a minimal interface covering only the methods the test calls
|
||||||
|
- Write a manual fake struct that satisfies that interface (no code generation)
|
||||||
|
- One table-driven test using `t.Run` and helpers from `gitea.djmil.dev/go/template/pkg/testutil`
|
||||||
|
- Use `logger.NewNop()` from `gitea.djmil.dev/go/template/pkg/logger` if logging is needed
|
||||||
|
|
||||||
|
## Rules (do not break these)
|
||||||
|
|
||||||
|
- Never define an interface inside the package itself — consumers define interfaces
|
||||||
|
- Never call `.Expect()`, `.Must()`, or `.Expectf()` inside the package — only return `result.Expect[T]`
|
||||||
|
- No third-party imports
|
||||||
|
- No hard-coded configuration values
|
||||||
|
|
||||||
|
## After scaffolding
|
||||||
|
|
||||||
|
Run `make lint test` to verify the files compile and the stub test passes. Report what was created and suggest what the caller should wire in `cmd/app/main.go`.
|
||||||
8
.claude/settings.json
Normal file
8
.claude/settings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(go test *)",
|
||||||
|
"Bash(go vet *)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
31
CLAUDE.md
31
CLAUDE.md
@ -8,15 +8,12 @@ Keep it concise — the agent needs signal, not essays.
|
|||||||
## Project overview
|
## Project overview
|
||||||
|
|
||||||
Go 1.25 template for PoC, hobby projects, and small publishable packages.
|
Go 1.25 template for PoC, hobby projects, and small publishable packages.
|
||||||
Demonstrates: structured logging (slog), config (flag), consumer-defined interfaces + manual fakes,
|
Stack: structured logging (slog), config (flag), consumer-defined interfaces + manual fakes,
|
||||||
result type (happy-path error handling), linting (golangci-lint), security scanning (gosec, govulncheck),
|
result type (happy-path error handling), linting (golangci-lint), security scanning (gosec, govulncheck).
|
||||||
git hooks, devcontainer, VSCode tasks.
|
|
||||||
|
|
||||||
Key constraint: `go.mod` stays free of dev tool deps (tools are pinned in `tools.versions` and run via
|
Key constraint: `go.mod` stays free of dev tool deps (tools are pinned in `tools.versions` and run via
|
||||||
`go run tool@version`) so packages published from this repo have a clean module graph for consumers.
|
`go run tool@version`) so packages published from this repo have a clean module graph for consumers.
|
||||||
|
|
||||||
Module: `gitea.djmil.dev/go/template` — update this when you fork.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Design philosophy
|
## Design philosophy
|
||||||
@ -39,11 +36,13 @@ Key factors that shape this codebase most directly:
|
|||||||
## Project structure
|
## Project structure
|
||||||
|
|
||||||
```
|
```
|
||||||
cmd/app/main.go composition root — wires deps, no logic here
|
cmd/app/ CLI entrypoint: parses flags, wires dependencies, and expresses
|
||||||
internal/config/ flag-based config loader (config.Load)
|
what the program does in high-level readable steps — calls internal/
|
||||||
internal/logger/ slog wrapper with WithField / WithFields
|
and pkg/ packages; no domain logic lives here
|
||||||
internal/greeter/ Example domain package (delete or repurpose)
|
internal/ domain-specific packages; logical grouping of substantial code into
|
||||||
pkg/result/ Example publishable package (Result/Expect types)
|
digestible types and interfaces; not importable outside this module
|
||||||
|
pkg/ generic, publishable packages reusable across projects; no
|
||||||
|
assumptions about the calling application
|
||||||
tools.versions Pinned tool versions (sourced by Makefile and pre-push hook)
|
tools.versions Pinned tool versions (sourced by Makefile and pre-push hook)
|
||||||
.golangci.yml Linter rules
|
.golangci.yml Linter rules
|
||||||
.githooks/pre-push Runs gofmt + go vet + golangci-lint + gosec before push
|
.githooks/pre-push Runs gofmt + go vet + golangci-lint + gosec before push
|
||||||
@ -53,8 +52,8 @@ tools.versions Pinned tool versions (sourced by Makefile and pre-push
|
|||||||
|
|
||||||
## Project rules
|
## Project rules
|
||||||
|
|
||||||
- **Module imports** — always use the full module path `gitea.djmil.dev/go/template/...`
|
- **Module imports** — always use the full module path from `go.mod` (never relative imports)
|
||||||
- **Packages** — keep `cmd/` thin (wiring only); business logic belongs in `internal/`
|
- **Packages** — `cmd/` owns CLI parsing, dependency wiring, and high-level orchestration; domain logic belongs in `internal/`
|
||||||
- **Types** — expose concrete types from constructors (`New(...) *Type`); never wrap in an interface at the implementation site. Consumers define their own interfaces if they need one (Go's implicit satisfaction makes this free)
|
- **Types** — expose concrete types from constructors (`New(...) *Type`); never wrap in an interface at the implementation site. Consumers define their own interfaces if they need one (Go's implicit satisfaction makes this free)
|
||||||
- **Errors** — `pkg/result` is a convenience tool for removing error-threading clutter from application logic; use it as follows:
|
- **Errors** — `pkg/result` is a convenience tool for removing error-threading clutter from application logic; use it as follows:
|
||||||
- `pkg/` libraries **only return** `result.Expect[T]` — never call `.Expect()`, `.Must()`, or `.Expectf()` inside library code; those methods exit the goroutine via `runtime.Goexit` and are only safe in application-layer code protected by a boundary
|
- `pkg/` libraries **only return** `result.Expect[T]` — never call `.Expect()`, `.Must()`, or `.Expectf()` inside library code; those methods exit the goroutine via `runtime.Goexit` and are only safe in application-layer code protected by a boundary
|
||||||
@ -65,7 +64,7 @@ tools.versions Pinned tool versions (sourced by Makefile and pre-push
|
|||||||
- use `result.StackTrace(err)` to retrieve the capture-site stack from a caught error
|
- use `result.StackTrace(err)` to retrieve the capture-site stack from a caught error
|
||||||
- still use `fmt.Errorf("context: %w", err)` when wrapping errors *before* constructing a `result.Fail`
|
- still use `fmt.Errorf("context: %w", err)` when wrapping errors *before* constructing a `result.Fail`
|
||||||
- **Logging** — logs go to `stderr` (structured, machine-readable, per 12-factor XI); human output goes to `stdout` via `fmt.Print*`. Use `log.WithField("key", val)` for structured context; never `fmt.Sprintf` in log messages; `log/slog` is the backend
|
- **Logging** — logs go to `stderr` (structured, machine-readable, per 12-factor XI); human output goes to `stdout` via `fmt.Print*`. Use `log.WithField("key", val)` for structured context; never `fmt.Sprintf` in log messages; `log/slog` is the backend
|
||||||
- **Config** — all configuration through `internal/config` (flag-parsed); no hard-coded values in logic packages
|
- **Config** — all configuration parsed in `cmd/app/config.go` (flags); no hard-coded values in `internal/` or `pkg/` packages
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -77,7 +76,6 @@ tools.versions Pinned tool versions (sourced by Makefile and pre-push
|
|||||||
- Constructors: `New(deps...) *Type` pattern
|
- Constructors: `New(deps...) *Type` pattern
|
||||||
- Comment every exported symbol (golangci-lint will warn if missing)
|
- Comment every exported symbol (golangci-lint will warn if missing)
|
||||||
- Max line length: 120 chars (configured in `.golangci.yml`)
|
- Max line length: 120 chars (configured in `.golangci.yml`)
|
||||||
- Prefer explicit over clever; PoC code should be readable first
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -91,7 +89,7 @@ tools.versions Pinned tool versions (sourced by Makefile and pre-push
|
|||||||
- Table-driven tests with `t.Run("description", ...)` for multiple cases
|
- Table-driven tests with `t.Run("description", ...)` for multiple cases
|
||||||
- The race detector is enabled in CI (`make test-race`); don't introduce data races
|
- The race detector is enabled in CI (`make test-race`); don't introduce data races
|
||||||
- Never use `time.Sleep` in tests; use channels or `t.Cleanup`
|
- Never use `time.Sleep` in tests; use channels or `t.Cleanup`
|
||||||
- Use `internal/testutil` helpers instead of manual checks — `ResultOk`, `ResultOkNotNil`, `ResultErr` for `result.Expect[T]`; `NoError`, `Error`, `ErrorContains`, `Equal` for plain values
|
- Use `gitea.djmil.dev/go/template/pkg/testutil` helpers instead of manual checks — `ResultOk`, `ResultOkNotNil`, `ResultErr` for `result.Expect[T]`; `NoError`, `Error`, `ErrorContains`, `Equal` for plain values
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -111,9 +109,6 @@ make release # list releases, or tag+push after full checks (make release
|
|||||||
make clean # remove bin/
|
make clean # remove bin/
|
||||||
```
|
```
|
||||||
|
|
||||||
VSCode: `Ctrl+Shift+B` = build, `Ctrl+Shift+T` = test.
|
|
||||||
Debug: use launch config "Debug: app" (F5).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Adding new features (checklist)
|
## Adding new features (checklist)
|
||||||
|
|||||||
10
README.md
10
README.md
@ -74,6 +74,16 @@ make release # list tags, or run full checks then tag+push (make release V
|
|||||||
|
|
||||||
> **Keyboard shortcut (VSCode):** `Ctrl+Shift+B` → build, `Ctrl+Shift+T` → test.
|
> **Keyboard shortcut (VSCode):** `Ctrl+Shift+B` → build, `Ctrl+Shift+T` → test.
|
||||||
|
|
||||||
|
### Claude Code commands
|
||||||
|
|
||||||
|
If you use [Claude Code](https://claude.ai/code), the repo ships a custom slash command:
|
||||||
|
|
||||||
|
| Command | What it does |
|
||||||
|
|---|---|
|
||||||
|
| `/new-package <name>` | Scaffolds `internal/<name>/` with a concrete type, constructor, doc comments, and a black-box test file — all wired to project conventions |
|
||||||
|
|
||||||
|
Commands live in `.claude/commands/` and are available to anyone who clones the repo.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Project structure
|
## Project structure
|
||||||
|
|||||||
16
rename.sh
16
rename.sh
@ -10,7 +10,7 @@
|
|||||||
# **/*.go import paths
|
# **/*.go import paths
|
||||||
# .devcontainer/devcontainer.json name field
|
# .devcontainer/devcontainer.json name field
|
||||||
# README.md heading + module path references
|
# README.md heading + module path references
|
||||||
# CLAUDE.md Module line
|
# CLAUDE.md internal import references (pkg/* preserved)
|
||||||
# .golangci.yml goimports local-prefixes
|
# .golangci.yml goimports local-prefixes
|
||||||
# git tags all template tags deleted
|
# git tags all template tags deleted
|
||||||
# git history squashed into one INIT commit
|
# git history squashed into one INIT commit
|
||||||
@ -126,17 +126,17 @@ sedi() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Helper: rename module path in a file, preserving pkg/result imports ───────
|
# ── Helper: rename module path in a file, preserving all pkg/* imports ────────
|
||||||
# pkg/result is a standalone publishable package; its import path must not
|
# pkg/ packages are standalone publishable packages from this template repo;
|
||||||
# change when the consuming project is renamed.
|
# their import paths must not change when a consuming project is renamed.
|
||||||
RESULT_PKG="${OLD_MODULE}/pkg/result"
|
PKG_BASE="${OLD_MODULE}/pkg/"
|
||||||
PLACEHOLDER="__RESULT_PKG_PLACEHOLDER__"
|
PLACEHOLDER="__TEMPLATE_PKG_BASE__"
|
||||||
|
|
||||||
rename_module_in() {
|
rename_module_in() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
sedi "s|${RESULT_PKG}|${PLACEHOLDER}|g" "$file"
|
sedi "s|${PKG_BASE}|${PLACEHOLDER}|g" "$file"
|
||||||
sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" "$file"
|
sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" "$file"
|
||||||
sedi "s|${PLACEHOLDER}|${RESULT_PKG}|g" "$file"
|
sedi "s|${PLACEHOLDER}|${PKG_BASE}|g" "$file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Apply substitutions ───────────────────────────────────────────────────────
|
# ── Apply substitutions ───────────────────────────────────────────────────────
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user