Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/setup-repo-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ jobs:
bash tests/setup-repo/test-python-assets.sh
bash tests/setup-repo/test-ci-assets.sh
bash tests/setup-repo/test-node-assets.sh
bash tests/setup-repo/test-precommit-assets.sh
bash tests/setup-repo/test-scaffold.sh
2 changes: 1 addition & 1 deletion plugins/core/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "core",
"version": "0.4.0",
"version": "0.5.0",
"description": "Core Offworld Labs skills, commands, agents, and hooks shared across all repos.",
"author": {
"name": "Offworld Labs"
Expand Down
10 changes: 8 additions & 2 deletions plugins/core/skills/setup-repo/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,20 @@ interactive parts and the report.
- **ts-frontend / ts-backend:** run `npm install` (this generates
`package-lock.json` — remind the user to commit it, since the CI workflow uses
`npm ci`).
- **pre-commit (python & ts stacks):** after the stack deps are installed,
register the git hook so the scaffolded `.pre-commit-config.yaml` runs on
every commit: `uvx pre-commit install` (or `pipx run pre-commit install`, or
`pip install pre-commit && pre-commit install`). Skip with a note if
pre-commit/uv is unavailable.
Report the command and result; if the toolchain is unavailable, skip and note it
rather than failing.

5. **Flesh out CLAUDE.md.** The stub was just written. Ask the user for a
one-or-two-line description of what this repo does, then fill in the
`Project Overview`, `Build & Test Commands`, and `Local Architecture`
sections from their answer plus what was scaffolded (stack, `ruff check .`,
`ruff format --check .`, `pytest`). If they skip, leave the stub as-is. Keep
sections from their answer plus what was scaffolded (stack; lint/format via
`pre-commit run --all-files`; tests via `pytest` or `npm test`). If they skip,
leave the stub as-is. Keep
CLAUDE.md under the 200-line ceiling noted in the template.

6. **Report and follow-ups.** Summarise files written vs skipped, then list the
Expand Down
14 changes: 11 additions & 3 deletions plugins/core/skills/setup-repo/assets/ci/ci-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,19 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Pre-commit
run: uvx pre-commit run --all-files --show-diff-on-failure

- name: Typecheck
run: npm run typecheck

- name: Lint
run: npm run lint

- name: Test
run: npm test
7 changes: 2 additions & 5 deletions plugins/core/skills/setup-repo/assets/ci/ci-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@ jobs:
- name: Install dependencies
run: uv pip install --system -r requirements.txt -r requirements-dev.txt

- name: Ruff lint
run: ruff check .

- name: Ruff format check
run: ruff format --check .
- name: Pre-commit
run: uvx pre-commit run --all-files --show-diff-on-failure

- name: Pytest
run: pytest
16 changes: 16 additions & 0 deletions plugins/core/skills/setup-repo/assets/precommit/python.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-merge-conflict
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
18 changes: 18 additions & 0 deletions plugins/core/skills/setup-repo/assets/precommit/ts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-merge-conflict
- id: check-added-large-files
- repo: local
hooks:
- id: eslint
name: eslint
entry: npx eslint --fix
language: system
types_or: [ts, tsx]
pass_filenames: true
3 changes: 3 additions & 0 deletions plugins/core/skills/setup-repo/scripts/scaffold-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ case "$STACK" in
copy "$ASSETS/stack/python/gitignore" "$TARGET/.gitignore"
copy "$ASSETS/stack/python/tests/.gitkeep" "$TARGET/tests/.gitkeep"
copy "$ASSETS/ci/ci-python.yml" "$TARGET/.github/workflows/ci.yml"
copy "$ASSETS/precommit/python.yaml" "$TARGET/.pre-commit-config.yaml"
;;
ts-frontend)
copy "$ASSETS/stack/ts-frontend/package.json" "$TARGET/package.json"
Expand All @@ -62,6 +63,7 @@ case "$STACK" in
copy "$ASSETS/stack/ts-frontend/src/index.test.ts" "$TARGET/src/index.test.ts"
copy "$ASSETS/stack/ts-frontend/src/main.tsx" "$TARGET/src/main.tsx"
copy "$ASSETS/ci/ci-node.yml" "$TARGET/.github/workflows/ci.yml"
copy "$ASSETS/precommit/ts.yaml" "$TARGET/.pre-commit-config.yaml"
;;
ts-backend)
copy "$ASSETS/stack/ts-backend/package.json" "$TARGET/package.json"
Expand All @@ -72,6 +74,7 @@ case "$STACK" in
copy "$ASSETS/stack/ts-backend/src/index.ts" "$TARGET/src/index.ts"
copy "$ASSETS/stack/ts-backend/src/index.test.ts" "$TARGET/src/index.test.ts"
copy "$ASSETS/ci/ci-node.yml" "$TARGET/.github/workflows/ci.yml"
copy "$ASSETS/precommit/ts.yaml" "$TARGET/.pre-commit-config.yaml"
;;
none) ;;
esac
Expand Down
5 changes: 3 additions & 2 deletions tests/setup-repo/test-ci-assets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ assert on["push"]["branches"] == ["main"], on
assert "pull_request" in on, on
steps = doc["jobs"]["lint-and-test"]["steps"]
runs = "\n".join(s.get("run", "") for s in steps)
assert "ruff check ." in runs, runs
assert "ruff format --check ." in runs, runs
assert "pre-commit run --all-files" in runs, runs
assert "ruff check ." not in runs, runs # ruff now runs via pre-commit, not a direct CI step
assert "ruff format --check ." not in runs, runs
assert "pytest" in runs, runs
assert "requirements.txt" in runs and "requirements-dev.txt" in runs, runs
# org standard: install with uv, not pip
Expand Down
6 changes: 5 additions & 1 deletion tests/setup-repo/test-node-assets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ setup_node = [s for s in steps if str(s.get("uses", "")).startswith("actions/set
assert setup_node, uses
assert setup_node[0]["with"]["node-version"] == "20", setup_node
assert setup_node[0]["with"]["cache"] == "npm", setup_node
setup_py = [s for s in steps if str(s.get("uses", "")).startswith("actions/setup-python")]
assert setup_py and setup_py[0]["with"]["python-version"] == "3.12", setup_py
assert any(u.startswith("astral-sh/setup-uv") for u in uses), uses
runs = "\n".join(s.get("run", "") for s in steps)
assert "npm ci" in runs, runs
assert "npm run typecheck" in runs, runs
assert "npm run lint" in runs, runs
assert "pre-commit run --all-files" in runs, runs
assert "npm run lint" not in runs, runs # eslint now runs via pre-commit
assert "npm test" in runs, runs
print("ci-node.yml OK")
EOF
Expand Down
48 changes: 48 additions & 0 deletions tests/setup-repo/test-precommit-assets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
PC="$ROOT/plugins/core/skills/setup-repo/assets/precommit"

python3 - "$PC/python.yaml" "$PC/ts.yaml" <<'EOF'
import sys
try:
import yaml
except ModuleNotFoundError:
print("pyyaml missing; skipping precommit YAML parse"); sys.exit(0)
py = yaml.safe_load(open(sys.argv[1]))
ts = yaml.safe_load(open(sys.argv[2]))

def by_repo(doc):
return {r.get("repo"): r for r in doc["repos"]}

HYGIENE = "https://github.com/pre-commit/pre-commit-hooks"
RUFF = "https://github.com/astral-sh/ruff-pre-commit"
need_hygiene = {"trailing-whitespace", "end-of-file-fixer", "check-yaml",
"check-json", "check-merge-conflict", "check-added-large-files"}

pr = by_repo(py)
assert HYGIENE in pr, pr.keys()
assert RUFF in pr, pr.keys()
assert pr[HYGIENE]["rev"] == "v5.0.0", pr[HYGIENE].get("rev")
assert pr[RUFF]["rev"] == "v0.8.0", pr[RUFF].get("rev")
assert need_hygiene <= {h["id"] for h in pr[HYGIENE]["hooks"]}, pr[HYGIENE]
ruff_hooks = {h["id"]: h for h in pr[RUFF]["hooks"]}
assert {"ruff", "ruff-format"} <= ruff_hooks.keys(), ruff_hooks
assert ruff_hooks["ruff"].get("args") == ["--fix"], ruff_hooks["ruff"]
print("python.yaml OK")

tr = by_repo(ts)
assert HYGIENE in tr, tr.keys()
assert tr[HYGIENE]["rev"] == "v5.0.0", tr[HYGIENE].get("rev")
assert need_hygiene <= {h["id"] for h in tr[HYGIENE]["hooks"]}, tr[HYGIENE]
assert "local" in tr, tr.keys()
local_hooks = tr["local"]["hooks"]
eslint = next((h for h in local_hooks if h.get("id") == "eslint"), None)
assert eslint is not None, local_hooks
assert "npx eslint --fix" in eslint.get("entry", ""), eslint
assert eslint.get("language") == "system", eslint
assert eslint.get("types_or") == ["ts", "tsx"], eslint
assert eslint.get("pass_filenames") is True, eslint
print("ts.yaml OK")
EOF
echo "precommit assets OK"
7 changes: 4 additions & 3 deletions tests/setup-repo/test-scaffold.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ for f in .claude/settings.json CLAUDE.md .claude/rules/security.md \
.claude/rules/code-style.md .github/workflows/claude.yml \
.github/workflows/claude-code-review.yml .github/workflows/ci.yml \
.editorconfig pyproject.toml requirements.txt requirements-dev.txt \
.gitignore tests/.gitkeep; do
.gitignore tests/.gitkeep .pre-commit-config.yaml; do
test -e "$TMP/$f" || { echo "MISSING: $f" >&2; exit 1; }
done

Expand Down Expand Up @@ -41,6 +41,7 @@ for f in pyproject.toml requirements.txt requirements-dev.txt .gitignore \
tests/.gitkeep .github/workflows/ci.yml; do
test -e "$TMP_NONE/$f" && { echo "UNEXPECTED (none stack): $f" >&2; exit 1; }
done
test -e "$TMP_NONE/.pre-commit-config.yaml" && { echo "UNEXPECTED (none stack): .pre-commit-config.yaml" >&2; exit 1; }

# scaffolded python defaults are internally consistent
mkdir -p "$TMP/src"
Expand Down Expand Up @@ -84,7 +85,7 @@ bash "$ENGINE" "$TMP_TSF" ts-frontend
for f in .claude/settings.json CLAUDE.md .editorconfig \
package.json tsconfig.json eslint.config.js vite.config.ts .gitignore \
index.html src/index.ts src/index.test.ts src/main.tsx .github/workflows/ci.yml \
.github/workflows/claude.yml; do
.github/workflows/claude.yml .pre-commit-config.yaml; do
test -e "$TMP_TSF/$f" || { echo "MISSING (ts-frontend): $f" >&2; exit 1; }
done
for f in pyproject.toml requirements.txt requirements-dev.txt; do
Expand All @@ -100,7 +101,7 @@ trap 'rm -rf "$TMP" "$TMP_NONE" "$BOGUS" "$TMP_TSF" "$TMP_TSB"' EXIT
git -C "$TMP_TSB" init -q
bash "$ENGINE" "$TMP_TSB" ts-backend
for f in package.json tsconfig.json eslint.config.js vitest.config.ts .gitignore \
src/index.ts .github/workflows/ci.yml; do
src/index.ts .github/workflows/ci.yml .pre-commit-config.yaml; do
test -e "$TMP_TSB/$f" || { echo "MISSING (ts-backend): $f" >&2; exit 1; }
done
grep -q '"react"' "$TMP_TSB/package.json" && { echo "ts-backend should not have react" >&2; exit 1; }
Expand Down
Loading