agent-harness-plugins
cboone/agent-harness-plugins/.github/copilot-instructions.md
For full project conventions, see AGENTS.md in the repository root. Files under commands/ directories and SKILL.md files inside skills/ directories are prompts consumed by an AI agent, not user-facing shell scripts.
Copilot instructions2 starsChanged 12 days ago
- Pipes a download into a shell
# GitHub Copilot Instructions
For full project conventions, see `AGENTS.md` in the repository root.
## Scoped instructions
- [Dependabot review instructions](instructions/dependabot.instructions.md) cover the Dependabot plugin, reference, and fixture paths listed in that file's `applyTo` field.
- [Branch review evidence instructions](instructions/branch-reviews.instructions.md) cover `docs/reviews/**/*.md`.
- [Plugin permission instructions](instructions/plugin-permissions.instructions.md) cover `plugins/**/README.md`.
- [Review checklist instructions](instructions/review-checklists.instructions.md) cover each style guide's `references/review-checklist.md`, `plugins/set-up-review-config/**`, and `bin/build-review-checklists`.
- [Shell script review instructions](instructions/shell.instructions.md) cover `**/scripts/**`, `**/bin/**`, and `**/tests/fixtures/**`.
- [Worktree naming review instructions](instructions/worktree-naming.instructions.md) cover `plugins/create-worktree/**`, `plugins/address-issue-in-worktree/**`, and their corresponding `dist/codex/plugins/` mirrors.
## PR review
- **Version bumps are selective**: Only plugins with actual code changes get version bumps. Do not flag missing version bumps on unchanged plugins.
- **`excludeAgent` follows GitHub's documented format and values**: GitHub documents a scalar value of `"code-review"` to exclude code review or `"cloud-agent"` to exclude the cloud agent. Do not replace `"cloud-agent"` with `"coding-agent"` or infer a list form; GitHub's repository-instructions documentation shows a scalar field and these two values.
- **PR descriptions may contain stale commit references**: PR descriptions are often auto-generated from commit messages and may reference superseded commits. Do not flag discrepancies between PR description bullet points and the actual diff.
- **Done plans are historical records**: Files in `docs/plans/done/` are completed plan documents preserved for reference. They may not match the final implementation. Do not flag discrepancies between done plan content and the actual codebase.
- **Scoped instruction aliases are committed symlinks**: Each directory-scoped `AGENTS.md` has a sibling `CLAUDE.md -> AGENTS.md` symlink. Confirm these entries with `git ls-tree` or `test -L`; file listings that omit symlinks do not show that an alias is absent.
- **`monitor-pr` deliberately does not gate on `BLOCKED` or `reviewDecision`**: The skill treats mergeability as clean when there are no conflicts and the branch is not behind. Requiring `mergeStateStatus` to be `CLEAN`, or waiting for an approving review, would never terminate on a repository whose branch protection requires a reviewer the skill cannot supply. `BLOCKED` is reported prominently instead, including in the terminal report. Do not flag this as too permissive.
## Skill and command files are AI agent prompts
Files under `commands/` directories and `SKILL.md` files inside `skills/` directories are prompts consumed by an AI agent, not user-facing shell scripts.
- **Code blocks are agent instructions, not scripts**: Code blocks in workflow steps are commands for the AI agent to execute and interpret; the agent reads command output directly and uses it programmatically. Do not suggest shell variable capture, output sanitization, or `tr -d '\n'` for commands whose output the agent handles natively.
- **Branch-name placeholders are not injection vectors**: Branch names in skill templates are always sanitized by prior slugification steps, and git itself rejects shell metacharacters in branch names. Do not flag these placeholders as shell injection vulnerabilities.
- **Bare command names are documented placeholders**: Bare command names like `resolve-copilot-threads` in code examples are documented placeholders for the script's quoted absolute path. A preamble in the "Script Setup" section explains this convention. Do not flag these as missing paths or broken commands.
- **`TOOL_BIN` is a documented placeholder**: In SKILL.md files and reference templates, `TOOL_BIN` is a placeholder that gets replaced with a tool-specific environment variable name (for example `BOPCA_BIN`, `MY_TOOL_BIN`) during execution. The naming convention (binary name uppercased, hyphens to underscores, suffixed with `_BIN`) is documented in each skill's workflow steps and in the SCRUT.md reference guide. Do not flag `TOOL_BIN` as a literal variable name or suggest replacing it with `<TOOL>_BIN` syntax.
- **Pipeline-style directives are semantic, not literal**: SKILL.md workflow steps are instructions for the agent, which reads command output and interprets it semantically. Directives like "filter SC3xxx codes from the output" mean the agent should ignore those codes when reporting results. Do not suggest adding shell filtering commands (`grep -Ev`, `sed`, `awk`) to skill instructions; the agent does not need pipeline-based filtering.
- **"Tool Overview" tables show simplified commands**: SKILL.md files contain a "Tool Overview" table that shows each tool's basic command form for quick reference. Full invocations with all flags (`--exclude`, `--check`, etc.) appear in the corresponding workflow sections below the table. Do not flag the table entries as incomplete or inconsistent with the workflow commands.
- **Capitalized "NOT" in prohibitions is intentional emphasis**: Some SKILL.md instructions use "Do NOT" instead of "Do not" for safety-critical prohibitions (for example, floating major version tags). The inconsistency with surrounding "Do not" phrasing is deliberate; the stronger emphasis signals higher-severity rules. Do not suggest normalizing these to sentence case.
## SHA pinning and template conventions
- **GitHub Actions are SHA-pinned by convention**: All scaffold templates, CI workflows, and reference docs pin third-party `uses:` refs to a 40-character commit SHA followed by a `# vX.Y.Z` comment matching the upstream tag (for example `actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1`). The leading `v` in the comment is optional when upstream omits it (`ludeeus/action-shellcheck@<sha> # 2.0.0`). A small number of actions release through moving channel aliases instead of versioned tags (`dtolnay/rust-toolchain@<sha> # stable`); the channel name is used in the comment for those refs and `bin/version-audit` does not track drift on them. The audit reference table in `plugins/refresh-project-scaffolding/skills/refresh-project-scaffolding/SKILL.md` tracks minimum baseline versions; SHA pins to the listed version or newer are compliant. Do not suggest reverting SHA pins to bare version tags or branch references, and do not flag the `# 2.0.0`, `# stable`, or other non-`vX.Y.Z` comments as inconsistent.
- **Reference template files SHA-pin third-party actions too**: Files under `references/` directories contain templates that generate starter files for users. Third-party `uses:` refs in these templates are SHA-pinned with a `# vX.Y.Z` comment (matching the convention above) so generated projects start from a hardened baseline. `bin/version-audit` keeps these pins in sync with upstream releases. Do not suggest reverting reference templates to bare version tags or branch references for the sake of readability; the SHA-plus-comment form is the project standard.
- **Scaffold templates reflect the owner's repo conventions**: The `scaffold-new-repo` and `scaffold-go-cli` skill templates (gitignore entries, GitHub Actions versions, GoReleaser config) are derived from analyzing the owner's actual repositories. Patterns and version choices are intentional conventions, not generic best practices. Do not suggest replacing them with standard templates.
## Tooling and CI conventions
- **Plugin manifests are the version source**: Marketplace entries and metadata do not contain version fields. A `.codex-plugin/plugin.json` mirrors its Claude manifest's version when present. Validate each manifest's SemVer and regenerate mirrors after plugin changes.
- **Every repository hook plugin requires Codex registration**: Validator rule 14 requires `.codex-plugin/plugin.json` with a non-empty `hooks` field whenever `hooks/hooks.json` exists, regardless of intended harness targets. Do not suggest making registration optional; unsupported Codex events require a separate compatible hook file.
- **`grep` inside a process substitution does not abort under `set -euo pipefail`**: `bin/validate-plugins` reads matches with `while IFS= read -r x; do ... done < <(grep ... | sed ... | sort -u)`. A no-match `grep` exits 1, but a process substitution's exit status is not the `while` loop's status and does not trigger `set -e`, and the pipeline ends in `sort`, which succeeds. This no-match path executes for the 47 of 50 `SKILL.md` files that reference no script, on every validator run. Do not flag it as an abort risk or suggest adding `|| true`.
- **`|| true` on a directly assigned pipeline is not redundant**: the note above applies only to process substitution. In `bin/check-cross-references`, `declared_ignores` ends in `sort` but its output is captured with `ignores="$(declared_ignores "${file}")"`, so the function's own exit status becomes the assignment's and a no-match `grep` would abort under `set -e`. The `|| true` there is load-bearing. Do not suggest removing it, and do not suggest adding one to the process-substitution reads.
- **The OpenCode mirror links skills into this checkout**: Each `dist/opencode/skills/<name>` entry is a relative symlink to `plugins/<name>/skills/<name>`, so a plugin's `scripts/` and `templates/` directories stay reachable beside the skill, and every skill that bundles a script documents a glob fallback that finds it there. Do not flag a plugin-root `scripts/` or `templates/` directory as missing from the OpenCode mirror or unreachable from an OpenCode install.
- **`find -maxdepth` is portable to macOS**: it is a documented primary in the BSD `find` man page (`-maxdepth n: Always true; descend at most n directory levels below the command line arguments`), not a GNU extension, and `/usr/bin/find` on macOS 15 runs it correctly. The scrut suite exercises these paths on macOS locally and in CI. Do not flag `-maxdepth` as GNU-only or claim it fails on macOS. The genuinely GNU-only spellings to watch for are long options such as `--maxdepth`, and `-printf`.
- **`diff --exclude` is portable to macOS**: it is documented in the FreeBSD `diff` man page, and `/usr/bin/diff` on macOS 26.6.2 reports `Apple diff (based on FreeBSD diff)` and accepts it; a `diff -r --exclude=.git` over two trees whose `.git` contents differ excludes them and exits 0. Older macOS releases shipped GNU diffutils 2.8.1, which accepts it too, so no supported macOS `diff` rejects it. `tests/scrut/review-corpus.md` relies on it and passes on macOS locally and on `ubuntu-latest` in CI. Do not flag `--exclude` as GNU-only or suggest `-x` for portability. Unlike `find --maxdepth` above, this long option is not a GNU extension.
- **`dependOn(b.getInstallStep())` changes which binary `zig build run` executes**: it is not merely an ordering constraint. Verified against Zig 0.16.0 by printing `argv[0]`: with the dependency the run step executes the installed artifact (`zig-out/bin/<name>`, following `--prefix` to `<prefix>/bin/<name>`); without it, `./.zig-cache/o/<hash>/<name>`. Upstream's own `zig init` template documents the same behavior. `plugins/scaffold-zig-cli/skills/scaffold-zig-cli/references/build-zig.md` relies on it so `zig build run` matches what a user would get. Do not flag that note as describing an ordering-only effect.
- **ZON string values are sentinel-terminated, so `[:0]const u8` is correct for them**: `@import("build.zig.zon")` in a `build.zig` yields string fields typed as `*const [N:0]u8`, verified with `@compileLog` against Zig 0.16.0, which coerces to `[:0]const u8` but is not a plain `[]const u8`. `plugins/scaffold-zig-cli/skills/scaffold-zig-cli/references/build-zig.md` therefore declares `build_options.addOption([:0]const u8, "version", manifest.version)`, and `cboone/fosforo`'s `build.zig` does the same. Do not flag that declaration as a type mismatch or claim the template fails to compile.
- **A new skill's OpenCode mirror entry is a symlink, and a diff renders it as one line**: `bin/build-opencode-mirror` adds `dist/opencode/skills/<name>` as a relative symlink to `plugins/<name>/skills/<name>`. Git tracks it with mode `120000` and shows it in a diff as a single line holding the target path, not as a file addition with content, so it is easy to scan past and read as absent. Confirm with `git ls-files -s dist/opencode/skills/<name>`, which prints `120000` when the entry is present. CI's `git status --porcelain dist/ .agents/` drift step already fails when a mirror entry is genuinely missing or stale, so a green run is evidence it is there. Do not flag a new plugin as missing its OpenCode mirror entry on the strength of the diff alone.
- **jq `and` and `or` short-circuit**: jq evaluates the right operand only when the left one does not settle the result, so a guard such as `type == "string" and test("\\S")` never runs `test` on a non-string. Likewise, jq evaluates an `elif` only when every earlier condition is false, so a type check in the first branch guards the later ones. `plugins/publish-report-board/scripts/report-board` relies on this throughout, and its scrut suite feeds numbers and objects to those guards. Do not flag these guards as type-unsafe or suggest rewriting them as `if` expressions.
- **The report-board scrut suite has two fixtures**: `tests/data/report-board/backlog-triage.json` holds seven issues, #101 through #107, and nearly every case mutates it; `backlog-triage-next.json` is a later sync with five issues that only the compare cases read. Do not flag the seven-issue expectations as mismatched with the second fixture.
- **A head lane frees work that records no dependency on the head**: in `plugins/publish-report-board/templates/backlog-triage.html`, `freedAfter` counts an issue whose recorded blockers all sit inside the head's branch, so an issue with no blockers of its own is counted. That is the head-lane model rather than a vacuous-truth bug: the lane itself holds the rest back until the head lands, which is why the mode is documented as "the first issue alone, then everything it frees at once". Requiring an explicit `waitingOn` edge to the head would report zero for every head lane on the real boards, while still counting nothing that waits on work outside the head's branch. Do not suggest gating those `every` calls on the issue having at least one dependency.
- **The freed count stands while the head is running, even beside another branch**: a head lane whose head is in progress still releases its downstream when the head lands, so `freedAfter` is gated on the head being in the running set, not on the head being the only branch there. A lane whose head is not running at all already counts zero. Do not suggest requiring the head to be the sole running root.
- **A changed row label is reported only when both syncs draw a matrix**: in `plugins/publish-report-board/scripts/report-board`, `compare` reports `row label` only when each side carries at least one contention claim. A board with no claims draws no matrix, so when the first claims arrive the `claim ... added` lines already tell the reader the matrix appeared, and a `row label (Plugin to X)` line would name a `Plugin` header that was never rendered; when the last claims go, the `claim ... removed` lines say as much. The guard was added because the unguarded version announced a header change for a matrix that had disappeared. Do not suggest reporting the label whenever either side has claims.
- **`Changed milestones` reports the milestone metadata list, not every matrix column**: in `plugins/publish-report-board/scripts/report-board`, `compare` reports the drawn projection of `.milestones`, meaning the listed entries that a claimed issue carries. A claimed milestone the metadata omits is still drawn as a column headed by its own title, but nothing about such a column can change unless an issue milestone, a claim membership, or the claim order changes, and `Changed milestone`, `Changed contention`, and `claim order` each already report those. Moving a claimed issue between two unlisted milestones, for instance, reports `Changed milestone: #107 cli: color output (Packaging to Shipping)`. Extending the projection to issue-derived keys would repeat the per-issue line for every such move, which is the noise the projection was added to remove. Do not suggest including claimed-but-unlisted milestones in it.
- **Cross-lane contention claims are a known design question, tracked in #392**: `validate` checks that a claim names at least two distinct issues that are on the board, but not that those issues share a lane. The backlog reference says to group lanes from the claims, and the bundled fixture's `parser` claim spans L1 and L2, so the guidance and the shipped example disagree. Whether the invariant is a rule validation should enforce, whether the fixture should move #104, and whether the reference should soften are one decision, recorded in #392 together with the fixture instance. Do not re-raise any part of it as a new finding.
- **Publishing an empty backlog is a known gap, tracked in #393**: `validate` rejects an empty `issues` list, and the lane check rejects the matching empty lane list, so a board cannot be published once the last issue closes. Supporting it needs empty states in both the page and the schema, which is out of scope for the branch that introduced the plugin. The behavior is deliberate for now and recorded in #393; do not re-raise it as a new finding.
- **Reporting where an inserted item landed is a known gap, tracked in #401**: `reordered` in `plugins/publish-report-board/scripts/report-board` compares only the items present on both sides, so inserting an issue into the middle of a lane, a pick list, or a contention claim is reported as an addition without its position. That is deliberate rather than an oversight: without it, appending one issue to a lane would report an order change for a sequence nobody touched, and every addition anywhere would carry a spurious reordering line. Nothing documents the change report as exhaustive, so this is a gap in completeness rather than a defect. #401 records the worked example, the three ordered lists affected, and the options. Do not re-raise it as a new finding.
- **A board title cannot forge the data marker**: `render` writes the title through `jq -r '.title | @html'`, so a title containing `id="board-data"` reaches the page as `id="board-data"` and cannot match the marker `extract` searches for. Rendering a board titled `widgets id="board-data" backlog` and extracting it round-trips the title exactly, and the suite covers that. Do not flag the marker search as anchorable to a forged title, and do not claim HTML escaping leaves such a title unchanged.
- **`ZONE_PATTERN` already refuses a dot segment**: `plugins/publish-report-board/scripts/report-board` defines it as `^[A-Za-z][A-Za-z0-9_+-]*(/[A-Za-z0-9_+-]+)*$`. No character class in it contains a dot, so no segment can be `.` or `..`, and a value such as `Etc/../UTC` is refused with the IANA zone message before the zone database is ever consulted. Do not flag the pattern as permitting dot segments, and do not suggest a separate path-segment guard beside it.
- **`is_text` already refuses blank and whitespace-only strings**: `plugins/publish-report-board/scripts/report-board` defines `is_text` as `type == "string" and test("\\S")`, so every field checked through it rejects an empty or whitespace-only value. That includes each entry of `sync.extra`, which is checked with `all(.[]; is_text)`. Do not read such a check as a bare `type == "string"`, and do not suggest adding a separate non-blank predicate beside it.
- **Issue links reach pull requests too**: GitHub redirects `/OWNER/REPO/issues/N` to `/OWNER/REPO/pull/N` when N is a pull request, so the report board links a `{ "ref": "OWNER/REPO#N" }` reference through `/issues/` whether it names an issue or a pull request. Do not flag it as unable to reach a pull request.
- **`--` is the POSIX end-of-options delimiter, and the BSD utilities honor it**: `plugins/publish-report-board/scripts/report-board` passes `--` to `dirname`, `chmod`, and `mv` so that a path beginning with a hyphen cannot be read as an option. macOS ships the BSD versions of all three, and each accepts `--`; the scrut suite renders boards on macOS both locally and in CI, exercising the script's own directory resolution, `cmd_render`, and the atomic write in `write_output`. Do not flag `dirname --`, `chmod --`, or `mv --` as GNU-only, and do not claim they fail on macOS or BSD.
- **HEREDOC with `-m` is intentional**: The pattern `git commit -m "$(cat <<'EOF' ... EOF)"` is a project convention for commit messages. The `$(cat ...)` command substitution correctly preserves internal newlines. Do not suggest replacing it with `git commit -F -` or other alternatives.
- **`corepack enable` before `setup-node` is intentional**: In GitHub Actions workflows, `corepack enable` runs before `actions/setup-node` so that the Yarn shim exists when `setup-node` computes the cache key for `cache: "yarn"`. This is the documented pattern for Yarn Berry with Corepack. Do not suggest reordering these steps.
- **golangci-lint templates use v2 configuration format**: Config templates with `version: "2"` use the golangci-lint v2 schema. In v2, `formatters:` is a valid top-level section (separate from `linters:`), and linter settings are nested as `linters.settings:` (not the v1-era `linters-settings:` top-level key). Do not suggest restructuring v2 configs to match the deprecated v1 layout.
- **`stable` is a valid Go version for `actions/setup-go`**: CI workflow templates use `"stable"` in the Go version matrix. This is a documented keyword supported by `actions/setup-go` since v4 that resolves to the latest stable Go release. YAML quoting (`"stable"` vs bare `stable`) does not change the string value. Do not flag this as an invalid version string.
- **GoReleaser changelog regexps use valid RE2 syntax**: The `??` quantifier in GoReleaser changelog group patterns (for example `(\([[:word:]]+\))??!?:`) is a valid RE2 non-greedy optional quantifier, not a syntax error. GoReleaser uses Go's `regexp` package, which implements RE2. Do not flag `??` as an invalid or unsupported quantifier.
- **`CGO_ENABLED=0` is the standard for Go CLI templates**: GoReleaser templates in this project target pure Go CLI tools (Cobra-based, distributed via Homebrew). `CGO_ENABLED=0` is intentional and consistent across all reference repos. Do not suggest adding cgo compatibility notes or removing this setting.
- **CI and local templates use `gh release download` for scrut installation**: Scrut is installed via `gh release download` from `facebookincubator/scrut` with a pinned version tag. The upstream install script is not used because its `get_latest_version()` function hardcodes the old `ukautz/scrut` repo. The `gh release download` approach supports version pinning for reproducible builds. Do not suggest replacing it with `curl | sh`, cargo, Homebrew, or other package managers.
- **`.prettierignore` shell script exclusions are intentional**: Shell scripts are excluded from prettier via `.prettierignore` because they are formatted with `shfmt`. Do not flag the `.prettierignore` exclusions as contradicting the prettier config (`.prettierrc.json`), and do not suggest removing the exclusions or reverting to prettier for shell formatting.
- **Git remote-derived project names are not normalized to kebab-case**: In scaffold skills, the project name derived from `git remote get-url origin` is used as-is (after stripping `.git` and taking the last path segment) because it is the canonical repo name. Only README h1 headings are converted to kebab-case, since those are human-readable text that needs slugification. Do not suggest adding kebab-case normalization to the git-remote extraction step.
- **Makefile targets assume global tool installation**: Makefile targets use bare command names (`prettier`, `swiftlint`, etc.) because they assume the tool is installed globally (via Homebrew or similar), which is documented in each tool's "Non-Node Projects" or "Install" section. Do not suggest replacing bare commands with `npx` in Makefile targets; `npx` requires npm infrastructure, which defeats the purpose for non-Node projects.
- **Quarantine removal in Homebrew cask templates is intentional**: GoReleaser and scaffold templates include `system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", ...]` in `hooks.post.install` for Homebrew casks. This is required for unsigned Go binaries distributed via Homebrew to avoid "App is damaged" Gatekeeper errors on macOS. Code signing is impractical for individual Go developers (requires Apple Developer Program membership and notarization). This is the standard pattern for unsigned Homebrew cask binaries. Do not flag this as a security concern or suggest removing it.
- **`PostToolUse` is a valid Claude Code hook category**: Claude Code supports `PostToolUse` hooks that fire after a tool call completes. These are observe-only hooks (exit code is ignored) used for logging, analysis, or triggering side effects. Do not suggest replacing `PostToolUse` with `Stop` or other categories, and do not flag it as unrecognized.
- **`SKIP_SETOPT_CHECK` uses opt-out design intentionally**: The generated `check-zsh.zsh` script runs the setopt warnings step by default (opt-out via `SKIP_SETOPT_CHECK=1`) rather than requiring opt-in. This is intentional: the check is a core part of the 7-tool pipeline and should run by default during local development. CI templates set `SKIP_SETOPT_CHECK=1` to keep lint jobs purely static. Do not suggest changing the default to opt-in (for example `RUN_SETOPT_CHECK=1`).
## Plugin metadata and user-facing settings
- **Plugin description fields are concise summaries**: The `description` field in `plugin.json` and `marketplace.json` is a short metadata summary. Sub-steps of a listed capability (for example "tap issue creation" is part of "Homebrew tap") should not be called out separately. Do not suggest expanding these descriptions with implementation details.
- **Skill descriptions are short routing descriptions**: The `description` in a `SKILL.md` frontmatter decides when a harness activates the skill, and every installed skill's description shares Codex's discovery budget, which rule 17 of `bin/validate-plugins` enforces and which warns above 150 characters. It is written separately from the catalog summary and need not match it. Do not suggest adding trigger phrases, requirements, or detail that would lengthen it, and do not flag a difference from the marketplace `description`.
- **A plugin README's catalog paragraph is the one under its title**: `AGENTS.md` requires the first paragraph of each `plugins/<name>/README.md`, directly below the H1, to match the marketplace `description` verbatim. Later sections, such as "What It Does", elaborate on it freely by design. Do not flag an elaborating paragraph as a mismatch with the catalog description when the paragraph under the H1 matches.
- **A permission rule matches each pipeline segment on its own**: Claude Code is aware of shell operators and splits a Bash command on `&&`, `||`, `;`, `|`, `|&`, `&`, and newlines, matching every subcommand against the allow rules independently. A skill that runs `gh api ... | jq 'add'` therefore lists a `gh api` rule and a separate `Bash(jq *)` rule, and the `gh` rule needs no trailing wildcard to reach past its own closing quote. Do not suggest extending one rule to cover a later segment of a pipeline.
- **Recommended Permissions sections are user-opt-in convenience settings**: Each plugin README contains a "Recommended Permissions" section with `permissions.allow` rules that users voluntarily copy into their own `settings.json`. These are not enforced security boundaries. The wildcards (`git add *`, `git commit *`, `bash */script-name *`) are intentional because command arguments vary and plugin install paths are not fixed. Each section already includes "Review and adjust the rules to match your security preferences." Do not flag these as security vulnerabilities, overly broad permissions, or attack vectors. The user explicitly chooses which rules to adopt.
## Markdown and regex quirks
- **Markdown tables use standard single-pipe syntax**: Tables in `README.md` and `SKILL.md` files use standard Markdown table syntax with single `|` pipe delimiters. Do not flag these as invalid or claim they use `||` double-pipe sequences.
- **`<!-- validate-plugins: ... -->` comments are checker directives**: `bin/check-cross-references` reads two HTML comments out of skill bodies. `repository-paths` declares that the `bin/` and `docs/` paths in that file name files in this repository rather than in the project the skill is run against; `ignore` exempts individual references that are illustrations, listed exactly as the checker reports them. They are file-scoped rather than line-scoped on purpose: most of the references needing an exemption sit inside an ordered list or a table row, where an HTML comment would break the Markdown. `AGENTS.md` documents the vocabulary. Do not flag them as stray HTML, suggest moving them next to the line they cover, or suggest converting them to frontmatter.
- **Bracket expressions can contain `[` as a literal**: In ERE/POSIX regex, an unescaped `[` inside an existing character class is a literal character, not a nested class opener. The pattern `[*[0-9]` is a valid bracket expression containing `*`, `[`, and digits. Do not flag such bracket expressions as having unmatched brackets or invalid regex syntax.
- **A code-span delimiter may be shorter than the backtick run it contains**: CommonMark closes a code span only on a backtick run of the same length as the opener, so a single-backtick delimiter padded with one space on each side safely wraps a literal triple-backtick fence. The inner run cannot terminate the span early. Both of these are valid and render to byte-identical HTML on GitHub:
````text
` ```{=latex} `
`` ```{=latex} ``
````
Prettier normalizes the second form to the first, which is a rendering no-op. This pattern appears throughout `plugins/write-pandoc-markdown/`, where the docs must show Pandoc fence syntax literally. Do not flag either form as terminating early or rendering incorrectly, and do not suggest HTML `<code>` wrappers or longer backtick delimiters.
- **Leading SPDX HTML comment blocks in markdown do not violate MD041**: Reference and template markdown files under `plugins/manage-repo-licensing/` (and any other markdown that cannot carry a sidecar `.license` file) begin with a `<!-- SPDX-FileCopyrightText: ... / SPDX-License-Identifier: ... -->` block before the first ATX heading. This is the project's REUSE-style licensing convention. The repo's `.markdownlint.jsonc` does not customize MD041, and markdownlint v0.40 (via `markdownlint-cli2`) allows leading HTML comment blocks before the first heading by default; lint passes on these files. Do not suggest adding a `<!-- markdownlint-disable MD041 -->` directive, moving licensing metadata to `REUSE.toml`, or otherwise rewriting these headers.
## Writing conventions
These apply to skill bodies, reference material, READMEs, plan and review documents, commit messages, and PR and issue bodies. Skills here are prompts, so their prose is the product. `AGENTS.md` is the core statement; its scoped instruction files and linked references provide component-specific detail. The essentials:
- **No em dashes.** Use a comma, colon, semicolon, parenthetical, or a separate sentence. Where a dash genuinely reads best, use a spaced double hyphen (`--`). Flag em dashes in new prose; do not "correct" `--` back to an em dash.
- **No time or effort estimates** in any form (hours, days, sprints, "quick", t-shirt sizes, story points), including in skill instructions that tell an agent to produce them. Describe scope instead. Estimates of runtime behavior (complexity, latency, throughput, memory) are fine.
- **Neutral technical terminology.** Prefer `running` / `stopped` over `alive` / `dead`, `terminate` over `kill`, `validity check` over `sanity check`, `allowlist` / `blocklist` over `whitelist` / `blacklist`, `primary` / `replica` over `master` / `slave`, `placeholder data` over `dummy data`. Genuine proper nouns are exempt: git's `master` branch in default-branch detection, `config/master.key`, BibTeX `@mastersthesis`.
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

