From d7c8814a5ac5a09571cb9151202b01cabe46759f Mon Sep 17 00:00:00 2001 From: Paul O'Reilly Date: Fri, 13 Mar 2026 10:14:53 +1300 Subject: [PATCH] Initial project setup for small-scripts Spec-driven utility script collection with dryrun support and automated testing. Includes project docs, test runner, and directory structure. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 4 +++ CLAUDE.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ FUTURE.md | 15 ++++++++++ MEMORY.md | 3 ++ README.md | 33 +++++++++++++++++++++ tests/run-all.sh | 41 +++++++++++++++++++++++++++ 6 files changed, 170 insertions(+) create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 FUTURE.md create mode 100644 MEMORY.md create mode 100644 README.md create mode 100755 tests/run-all.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485cdab --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +local_secrets/ +*.swp +*.swo +*~ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c5bf9f9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,74 @@ +# CLAUDE.md — small-scripts + +## Overview + +A collection of small, standalone utility scripts designed to be symlinked into `~/sbin`. Development follows an **agent-driven, spec-first workflow** using OpenSpec — this project is the initial testbed for that approach. + +## Key Principles + +1. **Spec-first development.** Every script starts with an OpenSpec in `specs/`. No implementation without a spec. +2. **Dryrun by default.** Every script MUST support a `--dryrun` (or `-n`) flag that previews all actions without making changes. Dryrun output should clearly show what *would* happen. +3. **Test via dryrun.** Each script has a corresponding test script in `tests/` that exercises the main script's `--dryrun` mode to verify behaviour matches the spec. +4. **Symlink deployment.** Scripts are linked to `~/sbin` for PATH availability — the repo is the source of truth, not the installed copy. + +## Repository Structure + +``` +small-scripts/ +├── CLAUDE.md # This file — project conventions +├── MEMORY.md # Thin index linking to memory/ topic files +├── FUTURE.md # Backlog of ideas +├── README.md # Human-readable docs +├── specs/ # OpenSpec files (one per script) +│ └── .spec.md +├── scripts/ # The actual scripts +│ └── +├── tests/ # Test scripts (one per main script) +│ └── test-.sh +└── memory/ # Tiered memory topic files + └── log/ # Session logs +``` + +## Workflow + +### Adding a new script + +1. **Write the spec** — Create `specs/.spec.md` using OpenSpec format +2. **Implement the script** — Create `scripts/`, ensuring `--dryrun` support +3. **Write the test** — Create `tests/test-.sh` that validates dryrun output against spec expectations +4. **Verify** — Run the test, confirm pass +5. **Symlink** — `ln -sf "$(pwd)/scripts/" ~/sbin/` + +### Script conventions + +- Scripts should be idempotent and safe to re-run +- Exit non-zero on failure +- Use colour output for status messages where appropriate +- Include a `--help` flag with usage information +- Shebang line should specify the interpreter explicitly (e.g., `#!/usr/bin/env bash`, `#!/usr/bin/env python3`) +- `--dryrun` / `-n` must be supported — it previews actions without side effects + +### Test conventions + +- Test scripts live in `tests/` and are named `test-.sh` +- Tests invoke the main script with `--dryrun` and assert on output/exit codes +- Tests should be runnable standalone: `./tests/test-.sh` +- Use colour output (green/red) for pass/fail +- Exit non-zero if any assertion fails +- A top-level `./tests/run-all.sh` runs every test and summarises results + +### OpenSpec format + +Specs define: +- **Purpose** — What the script does, in one sentence +- **Usage** — CLI interface, flags, arguments +- **Behaviour** — Step-by-step description of what the script does +- **Dryrun behaviour** — What `--dryrun` outputs (explicitly) +- **Edge cases** — Known boundary conditions and expected handling +- **Examples** — Concrete input/output examples + +## Environment + +- Scripts target Linux (Ubuntu/Debian primarily) +- Bash is the default language unless complexity warrants Python +- `~/sbin` is in the user's PATH diff --git a/FUTURE.md b/FUTURE.md new file mode 100644 index 0000000..5308222 --- /dev/null +++ b/FUTURE.md @@ -0,0 +1,15 @@ +# Future Ideas + +## Script runner / installer + +- **Problem:** Manually symlinking each script to `~/sbin` is tedious +- **Idea:** A `scripts/install.sh` that symlinks all scripts in `scripts/` to `~/sbin`, with `--dryrun` support naturally +- **Open questions:** Should it handle removal of stale symlinks? Version checking? +- **Depends on:** Having at least 2-3 scripts to justify the automation + +## CI test runner + +- **Problem:** Tests are only run manually +- **Idea:** Gitea CI action that runs `tests/run-all.sh` on push +- **Open questions:** What runner is available on Gitea? Docker-based? +- **Depends on:** Gitea CI being available (cluster-bootstrap M10) diff --git a/MEMORY.md b/MEMORY.md new file mode 100644 index 0000000..0ac8e64 --- /dev/null +++ b/MEMORY.md @@ -0,0 +1,3 @@ +# Memory Index + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..11bd572 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# small-scripts + +A collection of small, standalone utility scripts for daily use. Scripts are symlinked into `~/sbin` for PATH availability. + +## Approach + +This project uses **spec-driven development** (OpenSpec) as a testbed for agent-driven development and refinement: + +1. Every script starts with a spec in `specs/` +2. Every script supports `--dryrun` to preview actions without side effects +3. Every script has a test in `tests/` that validates dryrun behaviour against the spec + +## Scripts + +| Script | Purpose | Status | +|--------|---------|--------| +| *(none yet)* | | | + +## Quick Start + +```bash +# Run all tests +./tests/run-all.sh + +# Symlink a script into ~/sbin +ln -sf "$(pwd)/scripts/" ~/sbin/ +``` + +## Milestones + +| # | Description | Status | +|---|-------------|--------| +| M1 | Project setup, first script | In progress | diff --git a/tests/run-all.sh b/tests/run-all.sh new file mode 100755 index 0000000..3b134a5 --- /dev/null +++ b/tests/run-all.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Run all test scripts and summarise results +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PASS=0 +FAIL=0 +FAILED_TESTS=() + +for test_script in "$SCRIPT_DIR"/test-*.sh; do + [ -f "$test_script" ] || continue + name="$(basename "$test_script")" + printf "Running %s... " "$name" + if bash "$test_script"; then + printf "\033[32mPASS\033[0m\n" + PASS=$((PASS + 1)) + else + printf "\033[31mFAIL\033[0m\n" + FAIL=$((FAIL + 1)) + FAILED_TESTS+=("$name") + fi +done + +echo "" +echo "Results: $PASS passed, $FAIL failed" + +if [ "$FAIL" -gt 0 ]; then + echo "" + echo "Failed tests:" + for t in "${FAILED_TESTS[@]}"; do + printf " \033[31m✗\033[0m %s\n" "$t" + done + exit 1 +fi + +if [ "$PASS" -eq 0 ]; then + echo "No tests found." + exit 0 +fi + +printf "\033[32mAll tests passed.\033[0m\n"