feat(skills): add coding-dev.md and per-language rules under code-languages/
Generic coding rules and preferences distilled from existing agent rule files, project AGENTS.md/.rules, and saved memories: - strictness maximal; suppressions must be reported to the user - never filter build/lint/test output through grep/tail/head - reuse > fork > self-implement; generic code becomes its own package - 1000-line file / 100-line function limits, split by concern - regression test every fixed bug; never test against real user data - events over polling; scope discipline; honest reporting Per-language: rust, js-typescript, bash, python, go, csharp-dotnet, c-cpp. Each extends coding-dev.md rather than repeating it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba95f171b2
commit
57968904bf
9 files changed
+1499
-1
No files matched your search
@@ -0,0 +1,172 @@
|
||||
---
|
||||
name: bash
|
||||
description: >-
|
||||
Use this skill when writing or modifying Bash/shell scripts, build pipelines, release
|
||||
scripts, or when running shell commands as an agent. Covers strict mode, shellcheck,
|
||||
quoting, the no-wait-loops rule, build-script structure, and destructive-command safety.
|
||||
---
|
||||
|
||||
# Bash / Shell — Rules & Preferences
|
||||
|
||||
Extends [`../coding-dev.md`](../coding-dev.md). Everything there applies; this adds shell specifics.
|
||||
|
||||
Bash is for **orchestration** — sequencing tools, wiring a build pipeline, gluing CLIs.
|
||||
Once a script grows real data structures, arithmetic, or parsing, it wants to be Python or
|
||||
a compiled tool instead.
|
||||
|
||||
---
|
||||
|
||||
## 1. Strict Mode & Linting
|
||||
|
||||
Every script starts with:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
```
|
||||
|
||||
- `-e` exit on error, `-u` error on unset variable, `-o pipefail` so a failure mid-pipe is
|
||||
not masked by a successful final command.
|
||||
- **`shellcheck` must be clean** and belongs in the build gate. Do not silence a finding with
|
||||
a `# shellcheck disable=` without a comment saying why.
|
||||
- Format with `shfmt`.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `set -e` does **not** fire inside `if`, `&&`, `||`, or command substitution used in a
|
||||
> condition. Do not assume a failing command inside those aborts the script — check
|
||||
> explicitly where it matters.
|
||||
|
||||
---
|
||||
|
||||
## 2. Quoting & Expansion
|
||||
|
||||
- **Quote every expansion**: `"$var"`, `"${arr[@]}"`, `"$(cmd)"`. Unquoted expansion word-splits
|
||||
and glob-expands, which is how a path with a space becomes two broken arguments.
|
||||
- `"${var:-default}"` for defaults; `"${var:?message}"` for required values.
|
||||
- Use arrays for argument lists — never build a command as a string and `eval` it.
|
||||
- `[[ ]]` over `[ ]`. `$(…)` over backticks.
|
||||
- `local` every variable inside a function.
|
||||
- `printf` over `echo` for anything with escapes or leading dashes.
|
||||
|
||||
---
|
||||
|
||||
## 3. Never Write Wait Loops
|
||||
|
||||
> [!CAUTION]
|
||||
> **Do not write `until <check>; do sleep 2; done` or `while ! <check>; do sleep …; done`.**
|
||||
> They hang the terminal, and the only way out is an interrupt.
|
||||
|
||||
To wait for something:
|
||||
- Run the work in the **background** and wait for its completion signal.
|
||||
- To check external state, **take a single sample per invocation** rather than looping.
|
||||
- If a tool has a real blocking wait (`--wait`, `wait-for`, `systemctl is-active --wait`),
|
||||
use that instead of polling around it.
|
||||
|
||||
This mirrors the general events-over-polling preference: prefer a notification to a loop.
|
||||
|
||||
---
|
||||
|
||||
## 4. Never Filter Build Output
|
||||
|
||||
Repeating the global rule because shell is where it gets violated:
|
||||
|
||||
```bash
|
||||
# WRONG
|
||||
cargo build 2>&1 | grep -E "error|warning"
|
||||
npm run build | tail -20
|
||||
|
||||
# RIGHT
|
||||
cargo build
|
||||
npm run build
|
||||
```
|
||||
|
||||
Tee to a log if you need a copy — `cmd 2>&1 | tee build.log` preserves the full output — but
|
||||
never `grep`/`head`/`tail` it away. Narrow the command instead when output is genuinely huge.
|
||||
|
||||
---
|
||||
|
||||
## 5. Destructive Commands
|
||||
|
||||
- **Look at the target before deleting or overwriting it.** `ls` it, `stat` it, read it.
|
||||
- Never `rm -rf "$VAR"` without first asserting `$VAR` is non-empty and is what you think:
|
||||
```bash
|
||||
[[ -n "${BUILD_DIR:-}" && -d "$BUILD_DIR" ]] || { echo "refusing: BUILD_DIR unset" >&2; exit 1; }
|
||||
```
|
||||
- **A shared output directory belongs to nobody.** If two scripts write to `dist/`, neither
|
||||
may `rm -rf` it — each cleans only its own outputs.
|
||||
- Write scratch files to a `mktemp -d` and trap cleanup:
|
||||
```bash
|
||||
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
|
||||
```
|
||||
- Prefer a `--dry-run` flag on anything that deploys, releases, or deletes — and implement it
|
||||
honestly.
|
||||
|
||||
### `pgrep -f` matches its own shell
|
||||
|
||||
> [!WARNING]
|
||||
> The command line containing the pattern **is itself a process**, so `pgrep -f foo.conf`
|
||||
> matches the shell running it. Piping that into `kill` kills the running script — which reads
|
||||
> exactly like "the thing I was testing never happened".
|
||||
>
|
||||
> Write the pattern so it cannot match itself: `pgrep -f 'my[a]pp\.conf'`
|
||||
|
||||
---
|
||||
|
||||
## 6. Build Script Structure
|
||||
|
||||
A build script is the project's **quality gate**, and it should be exhaustive enough to catch
|
||||
mistakes before a human does.
|
||||
|
||||
- **Fixed action order regardless of flag order**, with any failure aborting the rest:
|
||||
```
|
||||
bump → lint → build → artifacts → stop → deploy → start → commit → push → release
|
||||
```
|
||||
- **A skipped test fails the gate.** Never let a skip report green.
|
||||
- **Print a summary with numbers**, not just a verdict — counts of warnings, suppressions, and
|
||||
file-size violations. A suppression count that has grown is worth seeing even on a pass.
|
||||
- Keep per-step logs in a known directory so a failure can be read after the fact.
|
||||
- **Never bypass a failed gate** by copying a binary or running the install step by hand. Fix
|
||||
the failing step; do not weaken the check to make it pass.
|
||||
- Support `--dry-run` for the full release path.
|
||||
- **Throttle heavy builds.** Detect when something latency-sensitive is running and drop to
|
||||
half the cores at `nice -n 10`, announcing that it did so. Provide an override env var.
|
||||
|
||||
---
|
||||
|
||||
## 7. Paths & Portability
|
||||
|
||||
- Resolve the script's own directory rather than assuming the caller's `cwd`:
|
||||
```bash
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
```
|
||||
- Prefer absolute paths in anything non-interactive.
|
||||
- **Entering a container resets the working directory** — chain `cd` inside the same command
|
||||
string: `container-run -- bash -c "cd /path && make"`.
|
||||
- Don't hardcode `/tmp` when the content must survive a cleanup; `/var/tmp` persists longer.
|
||||
- `command -v tool >/dev/null || { echo "tool required" >&2; exit 1; }` before using an
|
||||
optional dependency. **A silently skipped step is worse than a failure** — a packaging step
|
||||
that quietly skips when its tool is missing will ship an incomplete release.
|
||||
|
||||
---
|
||||
|
||||
## 8. Secrets
|
||||
|
||||
- Read secrets from an environment file or secret store; **never** inline them in a script.
|
||||
- Never `echo` a secret, and never pass one as a command-line argument — argv is world-readable
|
||||
in the process table. Use an env var or stdin.
|
||||
- Never commit a file containing real credentials; commit an example with empty values.
|
||||
|
||||
---
|
||||
|
||||
## 9. Script Placement
|
||||
|
||||
Standalone utility scripts belong in a **dedicated scripts directory**, symlinked into `PATH`
|
||||
rather than copied there. Never write a loose script directly into `~/.local/bin` or another
|
||||
`PATH` directory — the source of truth should be one versioned location.
|
||||
|
||||
---
|
||||
|
||||
## 10. Related
|
||||
- [`../coding-dev.md`](../coding-dev.md) — the general rules this extends.
|
||||
- [`python.md`](./python.md) — where a shell script should have become a Python script.
|
||||
Reference in new issue
Block a user