feat!: collapse seven skills into one router skill - #18
Conversation
Seven sibling skills competed for the same request and none carried a trigger-first description, so the kit went unused while agents wrote unsafe shell. One 'shell' skill now routes into reference/ on demand. Adds reference/pipelines.md covering the awk/sed replacement table, the non-portable sed -i, and interpreter selection (macOS /bin/bash is 3.2). BREAKING: shell-skill:bash and the other six are now shell-skill:shell.
…dance Description now fires on the act (about to type awk, parsing du/ps output, nesting $(...)) rather than on an explicit request for shell help. Adds nu invocation forms to reference/nushell.md: agent and CI one-liners need -n/--no-config-file, or they inherit the user's config.nu and env.nu and stop being deterministic.
Adds the escalation ladder (--help -> man -> context7 -> web) to the skill, and reference/cli-cheatsheets.md with defaults that silently do the wrong thing -- notably fd/rg honouring .gitignore, where -H fixes only half the problem and -I is the other half. Verified against installed versions.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request consolidates seven separate shell skills into a single, unified shell skill and introduces new integration gates and configurations for Codex, ChatGPT, Claude Code, and Serena. Key additions include JSON Schema modelines for YAML-like files, automated validation scripts for schemas and AI integrations, and updated documentation reflecting the new 'Shell Skill Kit' branding. Feedback on the changes suggests optimizing tools/check-yaml-schemas.sh by using the Bash builtin read -r instead of spawning sed subprocesses in a loop, and leveraging Nushell in tools/check-ai-integrations.sh to parse YAML frontmatter robustly instead of using a fragile awk script.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| status=0 | ||
|
|
||
| while IFS= read -r file; do | ||
| if [[ ! -f "$file" ]]; then | ||
| continue | ||
| fi | ||
|
|
||
| first_line=$(sed -n '1p' "$file") | ||
| if [[ ! $first_line =~ ^#\ yaml-language-server:\ \$schema= ]]; then | ||
| printf 'missing first-line YAML schema comment: %s\n' "$file" >&2 | ||
| status=1 | ||
| fi |
There was a problem hiding this comment.
Using sed in a loop to read the first line of each file creates a new subprocess for every file, which can significantly degrade performance when checking many files. Additionally, the unquoted regex pattern ^#\ yaml-language-server:\ \$schema= contains an unquoted $ inside double brackets, which can lead to parsing and portability issues across different shell versions and platforms.
We can optimize this by using the Bash builtin read -r to read the first line without forking a subprocess, and by storing the regex pattern in a variable to ensure robust and portable matching.
| status=0 | |
| while IFS= read -r file; do | |
| if [[ ! -f "$file" ]]; then | |
| continue | |
| fi | |
| first_line=$(sed -n '1p' "$file") | |
| if [[ ! $first_line =~ ^#\ yaml-language-server:\ \$schema= ]]; then | |
| printf 'missing first-line YAML schema comment: %s\n' "$file" >&2 | |
| status=1 | |
| fi | |
| status=0 | |
| schema_pattern='^# yaml-language-server: \$schema=' | |
| while IFS= read -r file; do | |
| if [[ ! -f "$file" ]]; then | |
| continue | |
| fi | |
| first_line="" | |
| read -r first_line < "$file" || : | |
| if [[ ! $first_line =~ $schema_pattern ]]; then | |
| printf 'missing first-line YAML schema comment: %s\n' "$file" >&2 | |
| status=1 | |
| fi |
| skill_name=$(awk ' | ||
| $0 == "---" { fence++; next } | ||
| fence == 1 && $1 == "name:" { | ||
| sub(/^name:[[:space:]]*/, "", $0) | ||
| gsub(/^["'\'']|["'\'']$/, "", $0) | ||
| print $0 | ||
| exit | ||
| } | ||
| ' "$skill_md") |
There was a problem hiding this comment.
Since Nushell (nu) is already a required dependency of this repository and is used extensively in this script, we can leverage it to parse the YAML frontmatter of SKILL.md files robustly. This avoids relying on a complex and potentially fragile hand-rolled awk parser for YAML frontmatter, aligning perfectly with the repository's guideline: 'Structured data deserves a structured tool'.
| skill_name=$(awk ' | |
| $0 == "---" { fence++; next } | |
| fence == 1 && $1 == "name:" { | |
| sub(/^name:[[:space:]]*/, "", $0) | |
| gsub(/^["'\'']|["'\'']$/, "", $0) | |
| print $0 | |
| exit | |
| } | |
| ' "$skill_md") | |
| skill_name=\$(nu -c "open '$skill_md' | split row '---' | get 1 | from yaml | get name") |
Serena's memory store was empty -- 0 files in ~/.serena/memories -- while the install carried 182 MB of bundled language servers. The six .serena/memories/ files tracked in this repo were Serena's own auto-generated onboarding output (conventions, tech_stack, suggested_commands, task_completion, core, memory_maintenance), restating README.md and the Taskfile while drifting from them. Editor/agent integration state does not belong in a public repo. Removes .serena/, schemas/serena-project.schema.json, the Serena block in tools/check-ai-integrations.sh, and the references in REGISTRY.md, llms.txt, README.md, AGENTS.md, CONTRIBUTING.md, CHATGPT.md, Taskfile.yml and lefthook.yml. .gitignore now ignores .serena/ wholesale instead of whitelisting parts of it. task ci and task ai-integrations both pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review suggestions (gemini-code-assist, PR #18): 1. check-yaml-schemas.sh forked `sed -n 1p` once per file inside the loop. Replaced with the `read` builtin. Also moved the regex into a variable expanded unquoted in [[ =~ ]] -- the inline backslash-escaped form worked but silently degrades on edit. 2. check-ai-integrations.sh hand-rolled a YAML frontmatter parser in awk, re-implementing fence tracking, key matching and quote stripping. This kit teaches 'no awk beyond {print $1}' and 'structured data deserves a structured tool', so it now parses with nu. The suggested replacement was `nu -c "open '$f' | split row ..."`, which fails: `open` on a .md path does not return a string, so `split row` errors with only_supports_this_input_type. Needs --raw. It also omitted -n/--no-config-file, which this kit's own skill requires so the command does not inherit the user's config.nu. 3. Restores macOS CI, dropped when four jobs were consolidated into one. tools/ci-install-macos.sh survived as dead code, and its own header explains why the job exists: a portability signal for a repo whose premise is that BSD and GNU behave differently. A Linux-only gate cannot verify what this kit teaches. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The restored macOS job used `task ci`, which also runs the nushell gate. Homebrew's nushell is unpinned and the macos-14 runner currently ships 0.113.1, while the Linux job and local dev pin 0.114.1. The input/output signature in examples/nushell/03-typed-command.nu is 0.114+ syntax, so 0.113.1 parses `nothing` as a stray positional and --ide-check fails. That is version skew, not a platform difference -- nu is a single Rust binary and behaves identically on macOS and Linux. Linux owns the nushell gate at a pinned version; this job owns bash, shfmt and ShellCheck, which genuinely do differ across BSD and GNU. Same step list the original shell-macos job had. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collapsing seven skills into one router deleted skills/{nushell,shebang,
streams,zsh}/ but left reference/{nushell,shebang,streams,zsh}.md linking to
them. All five gates stayed green through the merge because none of them
reads a link -- in a repo whose product is its documentation, that is a real
hole. The four now point at skills/shell/.
tools/check-doc-links.sh resolves every relative Markdown link against its own
file's directory and fails on any miss. External URLs are deliberately not
fetched: needs network, slow, and turns someone else's outage into a red
build. Verified both ways -- 83 links resolve, and an injected bad link fails
the run.
Wired into task ci, a standalone task doc-links, and lefthook pre-commit on
*.md. The hook self-scans the whole tree rather than only staged files,
because moving one file breaks links in files the commit never touches --
exactly how this shipped.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Why
The kit exported seven separate skills (
bash,zsh,posix-sh,nushell,shell-standards,shebang,streams). An agent had to already know which one it needed — but the decision an agent actually faces is "should this be shell at all?", and that question was answered by none of them.Worse, the seven descriptions competed with each other on load, so the most useful one frequently lost.
What changed
One router skill (
skills/shell/). It answers "should this be shell?" first — usually the answer isjq,yq,sd,nu, Python orbun— then routes to the per-shell reference for whatever survives that question. The sevenSKILL.mdfiles collapse into one; their content moves toreference/, loaded on demand.Trigger-first description. The description now fires on the act ("you are about to type
awk,sed,cut, or a second pipe") rather than on a request to write shell. Skills lose to recency in a long context; a situational trigger fires when it matters.New
reference/cli-cheatsheets.md. Cached essentials forfd,rg,sd,nu,jq,yq,sttr— verified against installed versions, which are recorded so a reader knows when to re-check. It deliberately does not replace--help; it records the defaults that silently do the wrong thing:fd/rghonour.gitignoreas well as hiding dotfiles.-Hfixes only half — you need-I. This costs an hour every time, and the file you are hunting (.db,.env) is usually gitignored, which is precisely why you are hunting it.sed -iinvocation portable across BSD and GNU.sed -i 's/a/b/' fworks on GNU and errors on macOS;sed -i '' 's/a/b/' fworks on macOS and creates a file named''on GNU. Hencesd.$nu.home-pathdoes not exist — check$nu | columnsbefore using any$nu.*field.New
reference/pipelines.md. Replacement table, BSD-vs-GNU traps, worked rewrites. Hard stop: three or more pipeline stages, or anyawkbeyond{print $1}, means write a script file.nu -n -cguidance. Without-n/--no-config-file,nu -cloads the user'sconfig.nu/env.nuand inherits their aliases,$env, and any parse-timesource— so the command stops being deterministic. Same argument as pinning an interpreter.AI-integration surfaces.
REGISTRY.md,llms.txt, JSON schemas for Codex plugin / OpenAI skill metadata / Serena project, plustools/check-ai-integrations.shandtools/check-yaml-schemas.shwired into the Taskfile so the surfaces are checked, not just claimed.Breaking
feat!— the six removed skill names no longer resolve. Anything referencingshell-standards,bash,zsh,posix-sh,nushell,shebang, orstreamsas a skill must now useshell. The content is not gone; it moved toreference/behind the router.Verification
task cipasses on this branch: ShellCheck across all non-bad scripts, the good-vs-bad example contract (every.bad.shmust produce its documented SC code),nu --ide-checkon every.nu, the Nushell startup-order demo (all 3 cases), the bats suite, formatting, and the YAML-schema check.🤖 Generated with Claude Code