Monorepo of reusable VS Code devcontainer features. Each feature lives under src/<feature>/ with a devcontainer-feature.json, install.sh, a README.md, and optional stubs/ (files deployed to consumer repos, merged into an existing file at the same path when one exists), config/ (data files a script reads at runtime, e.g. JSON Schemas), bin/ (scripts installed onto PATH), and tests/ (bats test suites).
Tests that guard a convention across the whole repo rather than one feature (e.g. how features reference each other, or how stubs/ files are laid out) live in tests/ instead — see tests/README.md. Modeled on tomgrv/actions/self-repo-syntax.
Every tool-specific path is a symlink into .agents/ (the single source of truth). See .agents/README.md for the full layout.
- Skills (
/.agents/skills/, symlinked into/.github/skills/for Copilot and/.claude/skills/for Claude Code):caveman*,cavecrew,caveman-stats,feature-ai-coding,pm/*. Invoke them proactively via the Skill tool — don't wait until stuck. - Instructions (
/.github/instructions/*.instructions.md— Copilot'sapplyTo-glob convention; Claude doesn't auto-load these but they document repo conventions):general,repo,commit.
This repo dogfoods the ai-coding harness (.agents/skills/harness). State lives in .agents/progress.md and .agents/features.json: the SessionStart hook prints them with the branch's forge CI status, then work one feature at a time, mark passing only with recorded evidence, and update the progress log before stopping. The rules in this file and in .agents/instructions/ stay authoritative; the SessionStart hook lists them.
- Feature install (local):
npx tomgrv/devcontainer-features -- add <feature>→ callsfeature-install→ copiessrc/<feature>/stubs/viacp -a(symlinks preserved) into target. - Feature configure (devcontainer):
feature-configure <feature>→ deploys stubs files + symlinks. Both commands come fromtomgrv/scripts(fetched viazz-useinsrc/common-utils/install.sh). - This repo dogfoods its own features — root
.github/workflows/,.github/skills/,.claude/skills/are the installed output of the ai-coding feature. Edit canonical content under.agents/skills/orsrc/ai-coding/stubs/, not the symlinks. - Prettier: run
npm installthennpx prettier --writeon new/edited.md/.yml/.jsonfiles before committing. - Commits: Conventional Commits + devmoji emoji required — e.g.
feat(scope): ✨ description. Validated by commitlint onreview_requested. - PR base: always
develop, notmain. - CI workflow naming: any workflow that runs code tests (bats or otherwise) is named
test-<scope>.yamlunder.github/workflows/(e.g.test-common-utils.yaml). Non-test workflows (validate-*,check-*for non-test checks,manage-*,publish-*,release-*,update-*) keep their existing prefixes.
Each feature follows this structure (use src/scripting/ as the minimal reference — just the standard entrypoints plus stubs/; src/githooks/ or src/larasets/ for a fuller example with all four optional subdirectories):
src/<feature>/
devcontainer-feature.json # id, version, dependsOn, postCreateCommand
install.sh # runs: feature-install $0
package.json # npm workspace registration
README.md
configure-*.sh # optional lifecycle hooks, invoked by name (not on PATH)
install-*.sh # optional extra install-time scripts
stubs/ # files deployed as-is to consumer repos; merged into an
# existing file at the same path (JSON via json-merge,
# otherwise git merge-file) when one already exists.
# A basename starting with "_" marks a qualifier segment
# (up to the first ".") to strip, so several fragments
# (_foo.package.json, _bar.package.json) can all resolve
# to and accumulate into the same target (package.json).
.agents/skills/<name>/ # canonical real files
.github/skills/<name> # symlink → ../../.agents/skills/<name>
.claude/skills/<name> # symlink → ../../.agents/skills/<name>
.clean # optional, anywhere under stubs/: retires legacy
# files on deploy, one directive per line, paths
# relative to repo root — "RMV <path>" untracks
# from git (kept on disk), "DEL <path>" deletes
# and untracks, "KEY <json-or-yaml-file> <json-array-path>"
# drops a key before the merge, so json-merge/yaml-merge
# (which keep the consumer's existing value) write the
# stub's current one, e.g.
# KEY package.json ["lint-staged","old"] or
# KEY .github/workflows/x.yml ["jobs","a","if"]; a path step
# may be {"name":"..."} to select a list item.
# Never deployed as a stub itself.
config/ # optional: data files a script reads at runtime
# (JSON Schemas, alias/config maps, dependency manifests)
# — never deployed to consumers, never merged
bin/ # optional: scripts installed onto PATH by feature-install
# (no leading underscore — directory location alone marks
# a file as a PATH script, unlike stubs/'s qualifier convention)
tests/ # optional: *.bats + helpers.bash, run via `bats src/<feature>/tests/`
Change only what the task requires. Don't touch package-lock.json, src/githooks/bin/pre-commit.sh mode, or unrelated features unless the task explicitly calls for it.
Never implement Copilot feedback or PR comments automatically. Claude acts only on explicit requests—if you want me to address Copilot/GitHub feedback, mention @claude in a comment or message. This prevents well-intentioned but incorrect automated suggestions from landing in the codebase.
Default base branch is develop, never main. Only open PRs against main if:
- Explicitly asked ("create a PR against main")
- Marked as a hotfix (in commit message or request:
hotfix/...,@hotfix)
If no branch is specified in a request, start from develop.
PR titles must follow Conventional Commits format with scope matching the affected workspace/feature. Format: <type>(devcontainer-features-<workspace>): <emoji> <description>.
Valid scopes (deduced from npm workspace names): devcontainer-features-act, devcontainer-features-ai-coding, devcontainer-features-common-utils, devcontainer-features-gateway, devcontainer-features-githooks, devcontainer-features-gitutils, devcontainer-features-gitversion, devcontainer-features-larasets, devcontainer-features-minikube, devcontainer-features-scripting.
Example: fix(devcontainer-features-githooks): 🔧 Add conditional skip when GITLEAKS_LICENSE not set.
Validated by tomgrv/actions/check-pr-format@v5 on PR open/sync. Scope must use full devcontainer-features-<workspace> format; multi-workspace changes use the primary feature modified.
Breaking renames and major bumps follow UPGRADING.md (order of operations, pins, pitfalls). Read it before touching script names or tomgrv/actions@<major> / scripts-ref pins.