# Development Guide — ai-coding-skills This document covers prerequisites, how to run checks locally, the M1 quality tooling, the workspace policy, and the transitional `pnpm run check` contract. ## Prerequisites | Tool | Minimum version | Install | |------|----------------|---------| | Node.js | 20 | `fnm install 22` or `nvm install 22` | | pnpm | 10 | `npm install -g pnpm` | | `rg` (ripgrep) | any | `brew install ripgrep` / `apt-get install ripgrep` | | **shellcheck** | **any** | `brew install shellcheck` / `apt-get install shellcheck` | | `stat` (BSD or GNU) | any | pre-installed on macOS; GNU variant on Linux | | Python 3 | 3.8+ | pre-installed on most systems | **`shellcheck` is required.** The `pnpm run lint` script will exit 2 with a clear error message if `shellcheck` is not on `PATH`. Every contributor must install it before running any quality checks. ## Quick start ```bash # Install dependencies (workspace-aware, no nested package.json modifications) pnpm install # Run the full quality suite pnpm run check ``` ## Individual checks | Command | What it does | |---------|-------------| | `pnpm run sync:pi` | Mirror pi skill variants into `pi-package/skills/` | | `pnpm run verify:pi` | Assert pi resource and workflow invariants | | `pnpm run verify:reviewers` | Assert reviewer-runtime skill invariants | | `pnpm run test:installer` | Run root-level Node.js unit tests (22 tests) | | `pnpm run lint` | ESLint on root scripts + shellcheck on all `.sh` files | | `pnpm run lint:fix` | Auto-fix ESLint + Prettier (do not run on pre-existing code until M2) | | `pnpm run typecheck` | TypeScript `tsc --noEmit` in workspace packages | | `pnpm run test` | Run all tests (root + workspace packages) | | `pnpm run verify:docs` | markdownlint + offline link-check + docs-flow verifier | | `pnpm run verify:docs:online` | Same as `verify:docs` but with full external link checking | | `pnpm run verify:generated` | Assert generated output freshness (stub; fleshed out in M3) | | `pnpm run check` | Aggregate: run every gate above and report a summary | ## Quality tooling (added in M1) ### ESLint Root-level flat config in `eslint.config.mjs`. Covers `scripts/**/*.mjs` and `scripts/**/*.js`. Uses `@eslint/js` recommended rules and Node.js globals. Nested workspace packages are responsible for their own ESLint configuration. ### Prettier Config in `.prettierrc.json` (print-width 100, LF line endings). Ignore file at `.prettierignore` excludes generated agent-variant directories and `pnpm-lock.yaml`. ### markdownlint Config in `.markdownlint.jsonc` (rules) and `.markdownlint-cli2.jsonc` (file globs and ignores). Key overrides vs defaults: - `MD013` line-length relaxed to 120 chars (code blocks and tables excluded). - `MD033` (inline HTML) disabled. - `MD034` (bare URLs) disabled. - `MD041` (first-line heading) disabled. - `MD060` (table column style) disabled. Run `pnpm run verify:docs` to lint all `README.md`, `docs/*.md`, and `skills/**/SKILL.md` files (node\_modules excluded automatically). ### markdown-link-check Two configs: - `markdown-link-check.json` — **offline** mode (default): ignores all `http://` and `https://` links. Safe for local dev and CI. - `markdown-link-check.online.json` — **online** mode: checks external links with a 10 s timeout, 2 retries, and retry-on-429. Use `--online` flag on `scripts/lib/run-link-check.mjs`. `pnpm run verify:docs` uses the offline config by default. ### shellcheck Wrapper script at `scripts/lib/run-shellcheck.mjs`. Discovers every `*.sh` file under `scripts/` and `skills/` (excluding node\_modules and generated agent-variant directories) and runs shellcheck on each. **Installation:** - macOS: `brew install shellcheck` - Debian/Ubuntu: `sudo apt-get install shellcheck` - Other: The wrapper exits with code **2** (not 1) when shellcheck is missing, so CI can distinguish "shellcheck absent" from "shellcheck found violations". ## Cross-platform shell support (M2) All shell scripts under `scripts/` and `skills/reviewer-runtime/` that are exercised by `verify:pi`, `verify:reviewers`, `sync:pi`, and `test:installer` must work on both **macOS** (BSD userland) and **Ubuntu/Debian** (GNU userland) without modification. ### BSD vs GNU differences encountered | Feature | macOS (BSD) | Linux (GNU) | Portable form | |---------|-------------|-------------|---------------| | `stat` permissions | `stat -f '%Lp' ` | `stat -c '%a' ` | `portable_stat_perms` helper | | herestrings (`<<<`) | supported (bash) | supported (bash) | OK (scripts use `#!/usr/bin/env bash`) | | `find -E` (extended regex) | macOS-only | not available | use `grep` or POSIX `-name` | | `sed -i ''` | macOS form | use `sed -i` on Linux | detect or avoid in-place sed | | `date -j` (date arithmetic) | macOS-only | use `date -d` on Linux | Node helper or `date +%s` | | `readlink -f` | not on macOS by default | GNU standard | `realpath` or `cd && pwd` | ### `scripts/lib/portable.sh` A shared helper that abstracts the two known BSD/GNU divergences hit in this repo: ```bash # Source from any script that needs portable stat # shellcheck source=lib/portable.sh source "$(dirname "${BASH_SOURCE[0]}")/lib/portable.sh" # Returns octal permission string: e.g. "755" portable_stat_perms "$path" ``` The `shellcheck` wrapper passes `-x --source-path=SCRIPTDIR` so source directives resolve relative to the script file, not the working directory. ### How to run the Ubuntu smoke test locally ```bash # Requires Docker docker run --rm \ -v "$PWD:/w" \ -w /w \ node:20-bookworm \ bash -lc 'apt-get update -q && apt-get install -y -q shellcheck ripgrep python3 \ && corepack enable \ && pnpm install --frozen-lockfile \ && pnpm run check' ``` This runs the full `pnpm run check` suite (lint, typecheck, test, verify:pi, verify:reviewers, verify:docs, verify:generated) inside a clean Debian Bookworm container with Node 20. ## Transitional `check` contract (M1) `pnpm run check` may exit non-zero in M1. This is expected. The contract is: > `pnpm run check` exits non-zero **only** on issues recorded in > `docs/CLEANUP-BASELINE.md`. Previously-green checks remain green. No > violations are introduced by M1's own changes. **M2 update:** `verify:docs` is now green. `lint` still fails on the same 2 pre-existing ESLint violations and 7 pre-existing shellcheck findings documented in `docs/CLEANUP-BASELINE.md`. Those will be fixed in a dedicated cleanup pass. No violations were introduced by M2. ## pnpm workspace policy (M1 exclusion-only) The `pnpm-workspace.yaml` at the repo root implements the **non-mutating, exclusion-only** policy for M1: **Included** (canonical source packages): - `skills/atlassian/shared/scripts` — shared Atlassian runtime source - `skills/web-automation/codex/scripts` — provisional canonical copy; M3 will rename and/or consolidate **Excluded** (generated agent-variant directories): - `skills/atlassian/{codex,claude-code,cursor,opencode,pi}/scripts` - `skills/web-automation/{claude-code,cursor,opencode,pi}/scripts` - `pi-package/**` **Why exclusion-only in M1?** The generated agent-variant directories contain `package.json` files with the same `name` field as the canonical source. Including them in the workspace would cause pnpm to complain about duplicate package names. Renaming them to unique names (e.g. `atlassian-skill-scripts-codex`) requires a generator-driven update that touches every generated file — this is deferred to **M3** to keep M1 byte-identical for those files. After `pnpm install`, `git status` should show zero modifications to any file under the excluded directories. If it does not, the workspace config is broken. ## How to interpret the baseline report See `docs/CLEANUP-BASELINE.md` for the full as-is capture. When a CI run fails on a check, compare the output against the baseline: - If the failure matches a baseline entry → it is a known pre-existing issue. - If the failure does not appear in the baseline → it is a regression introduced by recent changes and must be fixed before merge. ## Links - [CLEANUP-BASELINE.md](./CLEANUP-BASELINE.md) — as-is quality baseline - [README.md](../README.md) — project overview - [docs/README.md](./README.md) — documentation index