This file is for coding agents working in this repository. It summarizes the commands, workflows, and code conventions that are visible in the current codebase.
This repository uses the Shared Context Engineering (SCE) approach for AI-assisted software delivery with explicit, versioned context: https://sce.crocoder.dev/
- Root repo contains three main working areas:
cli/- Rust CLI (sce)config/- generated agent config, skills, and Pkl sourcescontext/- shared context docs, plans, decisions, and handovers
- Root agent guidance lives in this
AGENTS.md. - Bash command policies live in
.sce/config.jsonunderpolicies.bash.custom(enforced by the SCE bash-policy plugin). - No
.cursor/rules/directory was found. - No
.cursorrulesfile was found. - No
.github/copilot-instructions.mdfile was found. - If any of those files are added later, update this document to fold their instructions in.
Default rule: host coreutils / POSIX basics are fine as-is. Everything else must go through Nix — do not assume rg, jq, bun, python3, node, cargo, etc. are on the host PATH.
Use the host shell for ordinary shell built-ins and common coreutils-style tools, for example:
- shell:
cd,export,true,false, pipelines, redirections - coreutils-ish:
ls,cat,cp,mv,rm,mkdir,chmod,ln,head,tail,sort,uniq,wc,cut,tr,tee,echo,printf,test,[,basename,dirname,realpath,pwd,env,xargs,find(when already available) - git (repo workflows), and
nixitself
For any other CLI (rg, fd, jq, yq, bun, node, python3, shellcheck, actionlint, hyperfine, language toolchains, etc.), run it via one of:
| Pattern | When to use | Example |
|---|---|---|
nix develop -c … |
Repo flake tools (Rust toolchain, Bun, Pkl from this project’s dev shell) | nix develop -c sh -c 'cd cli && cargo build' |
nix shell nixpkgs#pkg -c … |
One-shot tool from nixpkgs (preferred for policy-satisfied ad-hoc tools) | nix shell nixpkgs#ripgrep -c rg pattern path |
nix run nixpkgs#pkg -- … |
Alternate one-shot form | nix run nixpkgs#jq -- . file.json |
nix run .#attr -- … |
Flake apps / checks defined in this repo | nix run .#pkl-check-generated |
nix flake check |
Default full verification | Prefer this over raw cargo test / cargo check / cargo fmt --check |
Repo bash policy (.sce/config.json) already blocks bare invocations of several tools and steers agents to Nix. Known policy-covered tools:
- Cargo verification: prefer
nix flake checkover barecargo test,cargo check, andcargo fmt --check. Keepcargo fmt(autofix) only throughnix develop. - Ad-hoc tools via Nix:
rg→nixpkgs#ripgrep,jq→nixpkgs#jq,python3→nixpkgs#python3,node→nixpkgs#nodejs,bun→nixpkgs#bun(or reponix develop),fd→nixpkgs#fd,shellcheck→nixpkgs#shellcheck,actionlint→nixpkgs#actionlint,yq→nixpkgs#yq-go,hyperfine→nixpkgs#hyperfine.
If a tool is not listed above but is not coreutils, still run it through nix shell / nix run / nix develop the same way. Do not install host packages to work around missing binaries.
- Repo work (Rust CLI, Bun plugin, flake tools):
nix develop -c sh -c '…'from repo root. - Single external utility:
nix shell nixpkgs#<pkg> -c <cmd> …ornix run nixpkgs#<pkg> -- …. - Validation / CI parity:
nix flake check(andnix run .#pkl-check-generatedwhen touching generated config).
- Nix is the primary reproducible entrypoint at repo root.
- Root
flake.nixprovides Bun, TypeScript, Pkl, jq, and the Rust toolchain. - Root
flake.nixdefines Crane-based Rust packaging and check derivations for the CLI. - Run Cargo via Nix, not directly from the host shell. Prefer
nix develop -c sh -c 'cd cli && <cargo command>'. - For validation, prefer
nix flake checkand avoid runningcargo test/cargo check/cargo fmt --checkdirectly unless a user explicitly requests it. - Optional local Nix tuning can live in user-level
~/.config/nix/nix.conf; recommended values aremax-jobs = autoandcores = 0. auto-optimise-store = trueis intentionally treated as a system-level/etc/nix/nix.confsetting, not a repo-managed user setting.- Bun is used for repo-owned config/plugin workflows; prefer Bun rather than npm or pnpm scripts when working in those areas. Always invoke Bun through Nix (
nix developornix shell nixpkgs#bun). - Rust edition is
2021. - TypeScript is still used in repo-owned config/plugin sources and should remain strict-mode friendly.
- Enter dev shell:
nix develop - Run all flake checks visible at root:
nix flake check - Run generated-output parity check:
nix run .#pkl-check-generated
Run these through Nix from repo root unless noted otherwise.
- Build CLI:
nix develop -c ./scripts/run-cli-cargo.sh build --manifest-path cli/Cargo.toml - Run CLI:
nix develop -c ./scripts/run-cli-cargo.sh run --manifest-path cli/Cargo.toml -- --help - Build packaged CLI output:
nix build .#default - Run packaged CLI app:
nix run .#sce -- --help - Preferred repo-level verification:
nix flake check - Run a single Rust test by exact name when explicitly needed:
nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml parser_routes_mcp -- --exact - Run Rust tests in one module/file pattern when explicitly needed:
nix develop -c ./scripts/run-cli-cargo.sh test --manifest-path cli/Cargo.toml setup - Run pre-Cargo wrapper tests:
bash scripts/test-run-cli-cargo.sh - Run ignored? none were found; do not assume ignored-test flows exist.
- Rust format verification is covered by
nix flake check - Auto-format only (not verification):
nix develop -c sh -c 'cd cli && cargo fmt' - Rust lint verification is covered by
nix flake check
Run from repo root through Nix (do not call bare bun on the host). Working directory for the plugin tests is config/lib/bash-policy-plugin/.
- Run plugin/runtime test suite:
nix develop -c sh -c 'cd config/lib/bash-policy-plugin && bun test' - Run a single Bun test by name:
nix develop -c sh -c 'cd config/lib/bash-policy-plugin && bun test -t "<test name>"' - One-shot Bun without the full flake shell:
nix shell nixpkgs#bun -c bun test(run aftercdinto the plugin dir)
- Preferred repo validation from repo root:
nix flake check - Config/plugin validation from repo root:
nix develop -c sh -c 'cd config/lib/bash-policy-plugin && bun test' - Generated-config validation from repo root:
nix run .#pkl-check-generated
- Ripgrep:
nix shell nixpkgs#ripgrep -c rg <pattern> <path> - jq:
nix shell nixpkgs#jq -c jq . file.json - Python:
nix shell nixpkgs#python3 -c python3 script.py - Node:
nix shell nixpkgs#nodejs -c node script.js - fd:
nix shell nixpkgs#fd -c fd <pattern> - shellcheck:
nix shell nixpkgs#shellcheck -c shellcheck script.sh - actionlint:
nix shell nixpkgs#actionlint -c actionlint - yq:
nix shell nixpkgs#yq-go -c yq . file.yaml - hyperfine:
nix shell nixpkgs#hyperfine -c hyperfine '<command>'
- Rust tests live inline in source files and in module test files such as
cli/src/services/setup/tests.rs. - Repository Cargo builds must use
scripts/run-cli-cargo.sh; it generates a fresh temporary Pkl payload, passesSCE_CLI_GENERATED_INPUT_DIRto Cargo, and cleans up the payload afterward. - Rust/Cargo commands should be executed through
nix develop, even for one-off builds, tests, fmt, and clippy runs. - Prefer
nix flake checkfor routine verification and avoid barecargo test/cargo check/cargo fmt --checkunless the user explicitly asks. - Rust single-test selection uses standard Cargo substring matching; add
-- --exactfor deterministic one-test runs. - Bun tests use
bun:testand support-tname filtering; always launch Bun via Nix. - Bun/plugin tests under
config/lib/bash-policy-plugin/are lighter-weight repo validation and remain part of the flake check surface.
- Release builds generate assistant payloads from canonical
config/pkl/sources; crates.io and Flatpak stage packaging-only fallbacks in temporary or ignored locations. - Root
flake.nixpackagesscethrough Crane'sbuildDepsOnly+buildPackagepipeline and runscli-tests,cli-clippy, andcli-fmtthrough Crane-backed checks. - Changes to Pkl generation inputs require
nix run .#pkl-check-generated; generated OpenCode, Claude, and Pi target trees are not committed.
- Follow existing local patterns before introducing new abstractions.
- Keep changes scoped and incremental.
- Prefer deterministic behavior and stable output text; this matters in CLI tests.
- Use explicit constants for repeated strings, timeouts, intervals, exit codes, and numeric formatting.
- Prefer small helper functions when they improve readability of branching or setup code.
- Avoid introducing framework-heavy patterns; this repo is mostly plain Rust, Bun, shell, and config assets.
- Group imports in this order: standard library, third-party crates, then
crate::...imports. - Use grouped
stdimports such asuse std::path::{Path, PathBuf};. - Prefer explicit imported items over wildcard imports.
- Keep import lists stable and reasonably compact.
- Use ESM
importsyntax only. - Keep imports grouped: Node builtins, external packages, then local files.
- Use
typeimports inline where appropriate, for exampleimport { foo, type Bar } from "pkg";. - Use explicit relative file paths like
./test-setup.
- Rust formatting is delegated to
rustfmt; do not hand-format against it. - Rust uses 4-space indentation.
- TypeScript uses 2-space indentation, semicolons, trailing commas where multiline, and double-quoted strings in the repo's remaining TS-owned areas.
- Shell scripts use
#!/usr/bin/env bashandset -euo pipefail. - Quote shell expansions unless you intentionally need word splitting.
- Prefer readable multi-line expressions over dense one-liners.
- In Rust, prefer strong enums and structs for command requests, runtime state, and result payloads.
- Derive common traits explicitly; common order in this repo is
Clone, Copy, Debug, Eq, PartialEqwhen applicable. - In TypeScript, prefer named
typealiases for payloads and test result structures. - Keep strict-mode friendliness: handle
undefined, use narrow unions, and avoid implicit any. - Prefer explicit return types on exported TypeScript helpers.
- Keep data structures serialization-friendly when they are written to JSON or surfaced by CLI output.
- Rust types and enums:
UpperCamelCase. - Rust functions, modules, and variables:
snake_case. - Rust constants:
SCREAMING_SNAKE_CASE. - TypeScript types:
PascalCase. - TypeScript variables and functions:
camelCase. - Test names are descriptive, behavior-oriented, and usually sentence-like with underscores in Rust.
- Prefer names that encode intent, not implementation trivia.
- Rust uses
anyhow::Resultbroadly for service-layer operations. - Add context to I/O and process failures with
Context/with_context. - Use
bail!andanyhow!for concise early exits when appropriate. - Preserve user-facing diagnostics as stable strings when tests assert on them.
- Separate machine classification from rendered messages when the CLI contract cares about exit codes.
- In TypeScript, throw
Errorwith direct, actionable messages. - Convert unknown thrown values with helper functions like
getErrorMessagebefore logging or persisting.
- Keep stdout reserved for intended command payloads.
- Keep errors on stderr and preserve stable prefixes/codes when existing code does so.
- Do not casually rewrite help text, error phrasing, or JSON field names; tests may depend on exact wording.
- Prefer deterministic ordering in rendered collections, embedded asset lists, and discovered file paths.
- Add unit tests close to the code they exercise.
- Match the repo's current pattern of focused behavioral test names.
- Assert on exact output when the CLI contract is supposed to be stable.
- For filesystem or manifest checks, sort collected paths before asserting.
- Keep tests isolated; clean up temporary state and abort long-running resources in teardown.
- Shell scripts should fail fast, validate prerequisites early, and print concrete remediation steps.
- Prefer staging-and-swap workflows for generated config updates instead of in-place mutation.
- Treat repository-root
.opencode/as runtime-managed and keepconfig/.opencode,config/.claude,config/.pi, andcli/assets/generated/absent. - Edit canonical Pkl and
config/libauthoring sources rather than temporary generated outputs.
- Check for unrelated worktree changes before broad edits.
- Avoid destructive git commands unless the user explicitly asks for them.
- When touching canonical generation inputs, verify ephemeral output with
nix run .#pkl-check-generated; do not regenerate target trees into the repository. - When verifying changes, prefer
nix flake checkinstead of barecargo test/cargo check. - When changing Bun-owned config/plugin code, run the narrowest Bun test or script that covers the change — always through Nix.
- Do not run non-coreutils CLIs from the host PATH; use
nix develop,nix shell, ornix run(see How to run commands).
- Default verification for code changes:
nix flake check - Bun/TypeScript config-plugin change:
nix develop -c sh -c 'cd config/lib/bash-policy-plugin && bun test -t "<test name>"' - Generated config or Pkl change:
nix run .#pkl-check-generated - Cross-cutting repo change:
nix flake check
README.mdflake.nixcontext/context-map.mdcontext/overview.mdcli/Cargo.tomlcli/src/app.rscli/src/services/setup/tests.rsconfig/lib/bash-policy-plugin/package.jsonconfig/lib/bash-policy-plugin/bash-policy-runtime.test.tsconfig/lib/bash-policy-plugin/opencode-bash-policy-plugin.ts