Help us shape the future of Go! Take the 2026 Go Developer Survey

devtools

module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2026 License: Apache-2.0

README

devtools

Shared agent tooling for tbhb repositories. The repo provides Go command-line tools for agent harnesses, plus the shared APM package of agent primitives.

APM package

Repositories across tbhb install the shared primitives with the APM CLI:

apm install tbhb-dev/devtools/packages/agents/claude#v0.11.0
apm install tbhb-dev/devtools/packages/agents/common#v0.11.0

The package deploys these primitives from sub-packages under packages/agents/: claude contains the skills below plus the guard-markdown hook declaration, and common contains the harness-neutral worktree-wip instructions.

Primitive Type Purpose
commit skill Group changes into one atomic commit, draft the message in COMMIT_AGENTMSG, review it, lint it, confirm it, then commit and rebase.
review-commit-message skill Review a drafted message against the staged diff as an independent agent, for what linting can't see.
worktree-wip instructions Stash and work-in-progress rules for repos that run more than one agent worktree session.
guard-markdown hook PreToolUse gate on Write and Edit that refuses Markdown whose paragraphs span more than one line.

Pinning the bare repo (apm install tbhb-dev/devtools#v0.11.0) resolves the root workspace manifest, whose dependencies on the four sub-packages under packages/agents/ are local paths that a remote consumer's apm resolves transitively, filtering each sub-package by its own targets. For a claude-only consumer, the bare pin gives exactly what the claude and common pins deliver. The codex and agy scaffolds skip on target intersection. This repo installs its own package the way a consumer does: apm install deploys the primitives into the local harness layout, and CI rejects drift between the packages/agents/*/.apm/ sources and the deployed copies.

Checks

A check is one rule enforced everywhere it matters. Because all three callers run the same binary, the rule can't drift between them:

Caller Invocation
Claude PreToolUse guard-markdown --hook reads the payload on stdin and denies the Write or Edit
pre-commit guard-markdown FILES reports the offending line ranges and exits nonzero
verification stack the same command from a mise task, alongside the other linters

.pre-commit-hooks.yaml publishes the pre-commit leg:

repos:
  - repo: https://github.com/tbhb-dev/devtools
    rev: v0.11.0
    hooks:
      - id: guard-markdown

Note that the two legs install separately. prek builds its own copy from language: golang. The Claude hook instead resolves guard-markdown on PATH, which comes from go install. A consumer wanting both adds the hook and runs the install.

Pass --fix to collapse paragraphs someone already wrapped.

Go tools

Each tool under cmd/ builds as a standalone binary:

  • agenthooks manages shared agent hooks.
  • agentstore stores shared agent state.
  • agentcontext assembles shared agent context.
  • guard-markdown refuses Markdown whose paragraphs span more than one line.

Install one directly:

go install github.com/tbhb-dev/devtools/cmd/agenthooks@latest

Or build everything from a checkout:

mise run build

Development

mise pins the toolchain and runs the workflow: mise run bootstrap sets up a fresh clone, mise run lint runs the full lint suite, mise run test runs the tests, and mise run build compiles the binaries into bin/. On a machine with no mise, the committed shim at tools/mise-bootstrap runs in place of mise and installs the pinned release on first use. The pins live in mise.toml and the drop-ins under .config/mise/conf.d/, locked by digest in the committed mise.lock files. See AGENTS.md for the agent-facing contributor guide.

Language tasks

Copy the vendir manifest to vendir.yml, set both refs to a release containing these task sets, and run vendir sync. The manifest syncs the whole .devtools directory, including task files, scripts, and config templates. It defines that directory's contents. Keep local adaptations outside it. Existing consumers syncing only .devtools/tasks must expand their manifest to .devtools/** with newRootPath: .devtools. The shared vendir.toml drift gate checks that whole directory, and pins.toml updates both manifest refs without adding a pin for each task file.

Select the languages in the consumer's mise.toml:

[task_config]
includes = [
  ".devtools/tasks/go.toml",
  ".devtools/tasks/python.toml",
  ".devtools/tasks/vendir.toml",
  ".devtools/tasks/pins.toml",
]

Run mise run devtools:lint-go or mise run devtools:test-go. Omit either language file when unused. Local wrappers such as test-go can run mise run devtools:test-go. In devtools, these wrappers keep task names without a namespace. Add devtools:check-vendored and devtools:check-pins to the consumer's combined lint task. Pin vendir in the consumer before the first sync. The shared conf.d pins supply the Go executables. Install Python tools in the consumer's uv dependency groups and lockfile. Go tasks using go tool also need the corresponding tool dependencies in the consumer's go.mod.

Set overrides in [env]: DEVTOOLS_GO_PACKAGES defaults to ./..., DEVTOOLS_GO_BIN_PACKAGES to ./cmd/..., and DEVTOOLS_GO_MODULES_MODE to readonly (use vendor for a vendored module). Build metadata derives DEVTOOLS_GO_MODULE from go list -m. DEVTOOLS_GO_BUILDMETA defaults to that module's internal/buildmeta package, with string variables Version, Commit, and Date. Go ignores those linker assignments when the package is absent. Python source roots default to src: set DEVTOOLS_PYTHON_DIRS for pylint, complexipy, and vulture, DEVTOOLS_PYTHON_SOURCE_DIRS for bandit and coverage, and DEVTOOLS_PYTHON_PROPERTY_DIRS for fuzzing (default tests/property). Directory lists use shell word splitting and glob expansion, so paths must contain no whitespace. Ruff, pyrefly, pytest, and cosmic-ray read the consumer's own config for their scope and policy. Both complexipy and vulture also read local policy.

Copy the needed templates from .devtools/config to the project root, then adapt these fields. These are starting points. Syncing updates the templates without overwriting local policy.

File Consumer adaptations
go/.golangci.yml → .golangci.yml Replace example.com/project in the prefix for gci, the module-path for gofumpt, and the local-prefixes for goimports. Set the license and copyright template for goheader to your authors. Review dependency bans and thresholds.
go/.go-arch-lint.yml → .go-arch-lint.yml Replace components' in paths and deps edges with your layers. Adjust exclude for your tooling and fixtures.
go/.testcoverage.yml → .testcoverage.yml Replace local-prefix with your module path. Choose thresholds and excluded entry points. Keep profile: coverage.out for these tasks.
go/.gremlins.yaml → .gremlins.yaml Adjust unleash.exclude-files for generated or third-party code. The default excludes vendor.
python/ruff.toml → ruff.toml Adjust exclude and the test/script patterns under lint.per-file-ignores. Choose your rules and documentation string convention. Existing [tool.ruff] settings in pyproject.toml can stay there instead.
python/pyrefly.toml → pyrefly.toml Set project-includes and project-excludes. Keep the diagnostic catalog aligned with your pinned pyrefly version. Existing [tool.pyrefly] settings can stay in pyproject.toml.
.devtools/scripts/go-ldflags.sh No edits: set the module or metadata package through env if the defaults differ from your layout. tools/go-ldflags.sh in this repo forwards to it.

Releases

cocogitto cuts vX.Y.Z tags from the Conventional Commit history. One tag serves both consumer paths, with APM installs pinning tbhb-dev/devtools#vX.Y.Z and Go installs pinning @vX.Y.Z.

Python

packages/devtools holds the Python side, which is deliberately small for now. Its gates run through mise run lint-py-all and mise run cover-py, which enforce ruff at its full ruleset, pyrefly's strict preset, and 100% branch coverage. uv sync provisions everything from uv.lock.

License

Apache-2.0. See LICENSE.

Private identifier gate

Sync .devtools/scripts/ alongside .devtools/tasks/ in your consumer's vendir.yml (or sync .devtools/ whole). Include .devtools/tasks/leaks.toml in mise's task configuration and add mise run devtools:lint-leaks to your lint task. This complements gitleaks with private network addresses, *.ts.net host names other than example.ts.net and its subdomains, password-manager references, and private-key headers.

Findings report the location and rule without printing matched text. Sensitive locations become opaque identifiers.

Add the leaks-staged, leaks-commit-msg, and leaks-pre-push hook IDs from this repository to prek. Run mise run devtools:prek-install after syncing: it installs a wrapper before prek's pre-push hook so the scan receives Git's ref lines. The pre-push hook ID verifies the wrapper installation. The wrapper scans the exact outgoing objects before prek consumes the ref lines. In CI, fetch full history and run mise run --quiet devtools:lint-leaks -- --range "$BASE..$HEAD" for the pull request's base and head commits.

Keep private literals in mise.local.toml, which Git ignores:

[env]
PROJECT_PRIVATE_STRINGS = "private-account-name, private-directory-name"

Every exported variable ending in _PRIVATE_STRINGS supplies comma-separated, case-insensitive fixed strings. The gate trims whitespace around entries and skips entries shorter than four characters with a warning. Commas and newlines inside a literal are unsupported. Run through mise so it loads the local environment. CI checks publishable patterns without these private values. Never commit the local file.

Add extra case-insensitive extended regular expressions to .leak-patterns, one per line. The gate ignores blank lines and lines starting with #. For legitimate fixtures, document anchored path expressions in .leak-allow, with a comment explaining each exception. These exceptions apply to pattern findings only: the gate checks private literals even in allowed paths. Treat changes to both files as security configuration requiring review. The gate scans working files by default, index blobs with --staged, explicit file arguments for commit messages, and complete new Git objects with --range or pre-push. Encoded or split values are outside its scope.

Directories

Path Synopsis
cmd
agentcontext command
Command agentcontext provides one of the shared agent tools for tbhb repositories.
Command agentcontext provides one of the shared agent tools for tbhb repositories.
agenthooks command
Command agenthooks provides one of the shared agent tools for tbhb repositories.
Command agenthooks provides one of the shared agent tools for tbhb repositories.
agentstore command
Command agentstore provides one of the shared agent tools for tbhb repositories.
Command agentstore provides one of the shared agent tools for tbhb repositories.
guard-markdown command
Command guard-markdown refuses Markdown whose paragraphs span more than one line.
Command guard-markdown refuses Markdown whose paragraphs span more than one line.
internal
buildmeta
Package buildmeta exposes build-time information injected via ldflags, with a fallback that consults the runtime/debug build info when no ldflags apply (such as go install directly from source).
Package buildmeta exposes build-time information injected via ldflags, with a fallback that consults the runtime/debug build info when no ldflags apply (such as go install directly from source).
hookio
Package hookio decodes Claude Code hook payloads and writes decisions.
Package hookio decodes Claude Code hook payloads and writes decisions.
markdown
Package markdown detects hard-wrapped prose in Markdown documents.
Package markdown detects hard-wrapped prose in Markdown documents.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL