| description | Conventions for developing on the stac-man (sm) repo itself — package layout, where each command lives, how to add new ones, testing rules, and Conventional Commits enforcement. |
|---|
This skill applies when an AI agent is writing code inside the stac-man repository (NOT when consuming the sm CLI from another project — for that, see the user-facing skill at docs/skills/stac-man/SKILL.md).
stac-man/
main.go # entrypoint: signal context → cmd.Execute
cmd/ # one cobra subcommand per file
root.go # root command + global flags + register()
create.go, log.go, … # each calls register(cmd) from init()
internal/
git/ # typed wrapper over the `git` binary
gh/ # typed wrapper over the `gh` binary
store/ # Store interface + gitconfig + memory impls
stack/ # Graph type, traversal, validation
service/ # orchestration: every user op is a method here
restack/ # rebase walk + on-disk resume state
config/ # optional ~/.config/stac-man/config.yaml
history/ # bounded undo log under .git/stac-man/history.json
ui/, ui/theme/ # neon-synthwave styling (lipgloss)
cmd/is thin. A subcommand parses flags, builds aservice.ServicevianewService(), and calls a single method. No domain logic in cobra files.internal/service/owns orchestration. Every user-facing operation is a method on*Service. A future TUI (v2) calls these directly without touching cmd/.internal/git/andinternal/gh/are the only packages allowed to shell out. Other packages talk to git/gh via these typed clients.internal/store/is the metadata source of truth (gitconfigimpl in production,memoryimpl in tests). Never read git config directly elsewhere.internal/stack/is pure: takes astore.Store, returns an in-memoryGraph. No I/O afterLoad.internal/restack/is the rebase engine — walks a topo-ordered queue and persists resume state to.git/stac-man/restack.json.internal/history/persists the undo log. Every mutating service method MUST calls.recordHistory(ctx, op, notes, branches)before mutating, listing every branch the op may touch.sm undorewinds the most recent entry.
- Add a file
cmd/<name>.go. Ininit()build the cobra.Command and callregister(cmd). - Inside the cobra
RunE, callnewService().<Method>(c.Context(), …). - Add the method in
internal/service/<area>.go. Keep everything testable: do all I/O through the*git.Client,*gh.Client, andstore.Storealready on*Service. - If the method needs a new git operation, add a typed wrapper to
internal/git/client.go. Don't shell out from service code directly. - Test the service method against
memory.Storeplus a fakegitrunner — never against the real binary. - Mutating methods record an undo entry: call
s.recordHistory(ctx, "<op>", "<notes>", []string{<every branch this op may touch>})BEFORE the first mutation. The list should include the current branch, any branches whose tip or parent metadata may move, and (for ops that delete or create branches) the branches being deleted/created. Snapshots are best-effort — don't fail the op if history write fails.
- Unit-test the service layer with
memory.Storeand fakeRunnerimplementations (internal/git/runner_test.goshows the pattern). - Don't shell out to real
gitorghfrom tests. Integration tests that need a real git repo go intests/integration/(temporary repos created witht.TempDir()). - Run
go test ./... && go vet ./...before every commit. - Use
ReadLintsafter substantive edits.
The commit-msg hook in .githooks/ enforces:
<type>(<scope>)!: <subject>
type∈ {build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test}scopeis optional, lower-case (e.g.feat(git): …,fix(restack): …)- The hook auto-skips merge / revert / fixup / squash / amend autosquash messages
Enable the hooks once per clone with:
git config core.hooksPath .githooks- Add "made with Cursor", "Co-authored-by: Cursor", or any AI-tool attribution to commit messages or code comments. The
.cursor/rules/no-ai-attribution.mdcrule covers this — read it. - Add narration-style comments like
// increment the counter— only document non-obvious intent. - Bypass the hooks via
--no-verifyfor normal work. Use it only for genuine emergencies. - Reach across layers (cmd → store directly, service → git internals, etc.). Add a method to the right wrapper instead.
# Build the binary into the repo root
go build -o sm .
# Run the full check
go test ./... && go vet ./...
# See what trunk stac-man auto-detected for this repo
git config --local --get stac-man.trunk