330 lines
13 KiB
Markdown
330 lines
13 KiB
Markdown
# 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: <https://github.com/koalaman/shellcheck#installing>
|
|
|
|
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' <path>` | `stat -c '%a' <path>` | `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.
|
|
|
|
**M3 update:** `pnpm run check` is now **fully green**. All pre-existing lint
|
|
violations have been fixed (2 ESLint errors, 7 shellcheck findings). The
|
|
`verify:generated` check is now a real implementation (was a stub in M2).
|
|
|
|
## pnpm workspace policy (updated in M3)
|
|
|
|
The `pnpm-workspace.yaml` at the repo root uses a **positive-include** policy
|
|
introduced in M3. There are no negative-glob exclusions.
|
|
|
|
**Canonical source packages** (never generated):
|
|
|
|
- `skills/atlassian/shared/scripts` — shared Atlassian TypeScript runtime
|
|
- `skills/web-automation/shared` — shared web-automation runtime template
|
|
|
|
**Generated agent-variant packages** (uniquely named, positively included):
|
|
|
|
- `skills/atlassian/{claude-code,codex,cursor,opencode,pi}/scripts`
|
|
→ names `@ai-coding-skills/atlassian-{claude-code,codex,cursor,opencode,pi}`
|
|
- `skills/web-automation/{claude-code,codex,cursor,opencode,pi}/scripts`
|
|
→ names `@ai-coding-skills/web-automation-{claude-code,codex,cursor,opencode,pi}`
|
|
- `pi-package/skills/atlassian/scripts`
|
|
→ name `@ai-coding-skills/atlassian-pi-mirror`
|
|
- `pi-package/skills/web-automation/scripts`
|
|
→ name `@ai-coding-skills/web-automation-pi-mirror`
|
|
|
|
**Why unique names matter:**
|
|
|
|
Each package in a pnpm workspace must have a distinct `name` field. In M1 all
|
|
generated agent-variant packages shared the same non-unique name as their
|
|
canonical source package. In M3 every package receives a scoped unique name
|
|
of the form `@ai-coding-skills/<skill>-<agent>`, enabling pnpm to include them
|
|
alongside canonical source packages without conflicts. The pi-package mirrors
|
|
use the `-mirror` suffix to distinguish them from the `pi` agent variants.
|
|
|
|
All generated packages are `"private": true` and are never published to any
|
|
registry.
|
|
|
|
After `pnpm install`, `git status` should show zero modifications to any
|
|
package.json file under any generated directory. If it does not, the workspace
|
|
config or generator 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.
|
|
|
|
## How variants are generated (M3)
|
|
|
|
Every agent-variant directory under `skills/<skill>/<agent>/` and every
|
|
`pi-package/skills/<skill>/` mirror is **generator-owned**. Do not edit
|
|
files inside these directories directly — edit the canonical source instead
|
|
and run `pnpm run sync:pi` to regenerate.
|
|
|
|
### Canonical sources (non-generated)
|
|
|
|
These directories are **never** generated and are **never** inside a generated
|
|
root. They are the single source of truth for all content.
|
|
|
|
| Skill | Canonical source |
|
|
|-------|------------------|
|
|
| `atlassian` | SKILL.md: `skills/atlassian/_source/<agent>/SKILL.md`; scripts: `skills/atlassian/shared/scripts/` |
|
|
| `web-automation` | SKILL.md: `skills/web-automation/_source/<agent>/SKILL.md`; scripts: `skills/web-automation/shared/` |
|
|
| `create-plan` | `skills/create-plan/_source/<agent>/` (SKILL.md + templates) |
|
|
| `do-task` | `skills/do-task/_source/<agent>/` (SKILL.md + templates) |
|
|
| `implement-plan` | `skills/implement-plan/_source/<agent>/SKILL.md` |
|
|
| `reviewer-runtime` (base) | `skills/reviewer-runtime/run-review.sh`, `notify-telegram.sh` |
|
|
|
|
The `_source/` directories and `shared/` directories sit under the skill root
|
|
**outside** every generated root. Stale-file detection (`verify:generated`)
|
|
never traverses them.
|
|
|
|
### Generated roots (authoritative list)
|
|
|
|
All paths are relative to the repo root.
|
|
|
|
```text
|
|
skills/atlassian/{claude-code,codex,cursor,opencode,pi}/
|
|
skills/web-automation/{claude-code,codex,cursor,opencode,pi}/
|
|
skills/create-plan/{claude-code,codex,cursor,opencode,pi}/
|
|
skills/do-task/{claude-code,codex,cursor,opencode,pi}/
|
|
skills/implement-plan/{claude-code,codex,cursor,opencode,pi}/
|
|
skills/reviewer-runtime/pi/
|
|
pi-package/skills/{atlassian,create-plan,do-task,implement-plan,web-automation}/
|
|
```
|
|
|
|
### Contributor workflow
|
|
|
|
1. **Edit the canonical source** in the appropriate `_source/<agent>/`, `shared/`, or
|
|
base `skills/reviewer-runtime/` directory.
|
|
2. **Run the generator:**
|
|
|
|
```bash
|
|
pnpm run sync:pi # regenerates all 31 generated roots
|
|
```
|
|
|
|
3. **Verify no drift:**
|
|
|
|
```bash
|
|
pnpm run verify:generated # must exit 0
|
|
```
|
|
|
|
4. **Stage both the canonical source AND the generated output** in the same commit.
|
|
Never commit a canonical change without regenerating.
|
|
|
|
### File-type-aware header policy
|
|
|
|
Every generated file (except JSON and `pnpm-lock.yaml`) receives a header
|
|
identifying it as generated and pointing to the canonical source. Headers are
|
|
inserted so they do not break parsers or tools:
|
|
|
|
| File type | Header form | Placement |
|
|
|-----------|-------------|----------|
|
|
| Markdown (with YAML front matter) | `<!-- ⚠️ GENERATED FILE ... -->` | After closing `---` |
|
|
| Markdown (no front matter) | `<!-- ⚠️ GENERATED FILE ... -->` | Top of file |
|
|
| Shell script | `# ⚠️ GENERATED FILE ...` | After `#!` shebang |
|
|
| TypeScript / JavaScript | `// ⚠️ GENERATED FILE ...` | After `#!` shebang if present, else top |
|
|
| YAML (non-lockfile) | `# ⚠️ GENERATED FILE ...` | Top of file |
|
|
| JSON | *(no header — recorded in `.generated-manifest.json`)* | — |
|
|
| `pnpm-lock.yaml` | *(no header — managed by pnpm)* | — |
|
|
| JSONC | `// ⚠️ GENERATED FILE ...` | Top of file |
|
|
|
|
### `.generated-manifest.json` contract
|
|
|
|
Each generated root contains a `.generated-manifest.json` that:
|
|
|
|
- Has a `$schema` marker and a `generator` field for identification.
|
|
- Lists every **other** generator-owned path in that root (relative path,
|
|
file mode, SHA-256 hash, kind).
|
|
- **Does NOT list itself** (non-self-referential).
|
|
- Is scoped to exactly **one** generated root — there is no manifest at
|
|
`skills/<skill>/`.
|
|
- Is verified structurally (not byte-for-byte) by `verify:generated`.
|
|
|
|
### Package metadata (M3 change)
|
|
|
|
Each generated agent-variant `package.json` carries:
|
|
|
|
- A **unique private name** in the form `@ai-coding-skills/<skill>-<agent>` (e.g.
|
|
`@ai-coding-skills/atlassian-claude-code`).
|
|
- `"private": true` to prevent accidental npm publication.
|
|
|
|
The pi-package mirrors (`pi-package/skills/atlassian/scripts` and
|
|
`pi-package/skills/web-automation/scripts`) use the `-mirror` agent suffix
|
|
(`@ai-coding-skills/atlassian-pi-mirror`, `@ai-coding-skills/web-automation-pi-mirror`)
|
|
to distinguish them from the `pi` agent variants in the workspace.
|
|
|
|
This replaces the previous non-unique `name` fields (`atlassian-skill-scripts`,
|
|
`web-automation-scripts`). These packages are private and are never published.
|
|
See `CHANGELOG.md` for the full rename list.
|
|
|
|
## Links
|
|
|
|
- [CLEANUP-BASELINE.md](./CLEANUP-BASELINE.md) — as-is quality baseline
|
|
- [README.md](../README.md) — project overview
|
|
- [docs/README.md](./README.md) — documentation index
|
|
- [CHANGELOG.md](../CHANGELOG.md) — milestone-by-milestone change record
|