Skip to content

feat(skill): add /create-skill — skill factory with quality gates#587

Open
carlos-alm wants to merge 83 commits intomainfrom
feat/create-skill
Open

feat(skill): add /create-skill — skill factory with quality gates#587
carlos-alm wants to merge 83 commits intomainfrom
feat/create-skill

Conversation

@carlos-alm
Copy link
Contributor

Summary

  • Adds /create-skill skill that scaffolds, writes, and validates new Claude Code skills
  • Encodes lessons from 200+ Greptile review comments across 4 titan PRs into 10 mandatory patterns and a 17-item self-review checklist
  • Dogfooded against itself — caught and fixed 3 issues (hardcoded temp path in example, hardcoded lint command, missing exit conditions)

Motivation

The titan skill PRs accumulated 210 review comments (103 from Greptile alone) across 10 recurring issue categories. Most were structural consistency problems — shell vars across fences, stale step references, silent failures, internal contradictions. This skill prevents those issues upfront by making them part of the authoring process rather than catching them in review.

What the skill does

  1. Phase 0 — Discovery: Gathers requirements interactively (purpose, args, phases, tools, artifacts, dangerous ops, resume support)
  2. Phase 1 — Scaffold: Creates directory + SKILL.md with correct frontmatter and structure
  3. Phase 2 — Write: Enforces 10 mandatory patterns derived from the top Greptile findings
  4. Phase 3 — Guards: Adds safety guards for any dangerous operations
  5. Phase 4 — Self-Review: 17-item checklist covering structure, anti-patterns, robustness, and completeness
  6. Phase 5 — Finalize: User review + commit

Test plan

  • Ran the skill's Phase 4 self-review checklist against itself
  • Fixed 3 issues found during dogfooding
  • Use it to create the next skill and verify it catches issues early

Encodes lessons from 200+ Greptile review comments across the titan
skill PRs into a reusable skill-creation workflow. Includes 10 mandatory
patterns (no shell vars across fences, no silent failures, stable
cross-references, etc.) and a 17-item self-review checklist that must
pass before any new skill can be committed.
@claude
Copy link

claude bot commented Mar 24, 2026

Claude finished @carlos-alm's task —— View job


PR Review Complete ⭐⭐⭐⭐☆

Todo List

  • Read the added SKILL.md file
  • Check repository context files (CLAUDE.md, FOUNDATION.md, BACKLOG.md)
  • Phase 1: Root Cause Analysis
  • Phase 2: Configuration Change Detection
  • Phase 3: Test Integrity
  • Phase 4: Code Quality
  • Phase 5: Backlog Compliance Assessment
  • Provide final recommendation with rating

Root Cause Analysis

Problem Being Solved: The "titan skill PRs" (presumably large skills like /deploy-check) accumulated 210 review comments across 10 recurring structural issues — shell variables across fences, stale step references, silent failures, internal contradictions. This skill codifies those lessons into mandatory patterns to prevent issues upfront during authoring rather than catching them in review.

Why This Approach: Rather than writing better documentation or linting rules, the author chose to encode institutional knowledge into an interactive skill factory. This is justified — the problems are systematic (200+ comments) and structural (violate Claude Code's execution model), making them good candidates for programmatic prevention.

Risk Assessment: Low implementation risk. This is pure markdown documentation with no code paths, but it represents significant process/quality impact for skill authoring workflows.

Configuration Changes

No configuration changes detected — this adds a new skill file without modifying linting rules, build configs, or test thresholds.

Test Integrity

No test changes — this PR is purely additive (new skill documentation) with no impact on existing test suite.

Code Quality Analysis

Structural Quality: Excellent

The skill follows codegraph's established skill patterns:

  • ✅ Proper frontmatter with required fields (name, description, argument-hint, allowed-tools)
  • ✅ Phase-based structure (Phases 0-5) with clear exit conditions
  • ✅ Comprehensive self-review checklist (17 items in Phase 4)
  • ✅ Arguments section with validation rules
  • ✅ Rules section with hard constraints

Content Quality: Very Strong

The 10 mandatory patterns (lines 97-204) directly address real issues:

  1. Pattern 1 (No shell vars across fences): Correctly identifies that each code fence is a separate shell invocation — a common Claude Code mistake
  2. Pattern 2 (No silent failures): Bans 2>/dev/null without documentation — prevents debugging nightmares
  3. Pattern 3 (Temp files need extensions): Critical for codegraph's tree-sitter language detection
  4. Pattern 4 (No hardcoded temp paths): Prevents concurrent session collisions
  5. Pattern 5 (Stable cross-references): Phase names vs numbers — prevents breakage when inserting steps

All patterns include wrong/correct examples, which is excellent for skill authors.

Self-Review Framework: Comprehensive

The Phase 4 checklist (lines 238-270) covers:

  • Structure checks: Validates frontmatter, phase naming, argument parsing
  • Anti-pattern checks: Direct mapping to the 10 patterns
  • Robustness checks: Rollback paths, error messages, concurrency safety
  • Completeness checks: Artifact schemas, exit conditions, scope boundaries

This is more thorough than most code review templates.

Minor Issues

  1. Line 231: Hardcoded lint command detection logic could be extracted to a reusable pattern since multiple skills likely need this
  2. Line 295: "Test commands must be detected, not assumed" — good principle but could use an example
  3. The skill doesn't address how to handle version conflicts between different users' skills in the same repo

Backlog Compliance Assessment

  • Zero-dep: ✓ — Pure markdown documentation, no runtime dependencies
  • Foundation-aligned: ✓ — Aligns with P8 (embeddable-first), supports agent quality without violating any principles
  • Problem-fit: 4/5 — Directly addresses skill authoring quality, which improves agent reliability. While not core "lost AI" problem, prevents agents from getting broken/unreliable skills
  • Breaking: No — Purely additive, no existing behavior changes
  • Tier: 1 (zero-dep ✓ + foundation-aligned ✓)

Critical Strengths

  1. Evidence-driven design: The 10 patterns come from "200+ review comments" — this isn't theoretical, it's based on actual failure modes
  2. Self-dogfooding: "caught and fixed 3 issues" when running against itself demonstrates the approach works
  3. Interactive discovery: Phase 0 forces requirements gathering before writing, preventing scope creep
  4. Comprehensive validation: 17-item self-review checklist is more thorough than most manual review processes

Minor Recommendations

  1. Consider adding a troubleshooting section for common skill authoring errors
  2. The pattern examples are excellent — consider extracting them to a separate reference doc for quick lookup
  3. Phase 5 could include testing instructions for newly created skills

Final Assessment

## Root Cause Analysis
**Problem Being Solved**: Systematic skill authoring quality issues (210 review comments across recurring structural problems)
**Why This Approach**: Programmatic prevention vs. documentation — justified by systematic nature of issues
**Risk Assessment**: Low — pure documentation, high process quality impact

## Backlog Compliance
- Zero-dep: ✓ — no runtime dependencies
- Foundation-aligned: ✓ — supports embeddability without violating principles
- Problem-fit: 4 — improves agent reliability through better skill quality
- Breaking: No — purely additive
- Tier: 1

## Critical Strengths
- Evidence-driven: Based on 200+ actual review comments
- Self-validation: Dogfooded against itself successfully
- Comprehensive: 10 mandatory patterns + 17-item checklist
- Interactive: Forces requirements gathering upfront

## Minor Areas for Enhancement
- Extract lint command detection to reusable pattern
- Add troubleshooting section for common errors
- Include skill testing guidance

## Final Recommendation
- Rating: ⭐⭐⭐⭐☆ (4/5)
- Action: APPROVE
- Reasoning: High-quality skill factory that addresses real systematic issues with comprehensive validation. Evidence-based approach with self-dogfooding demonstrates effectiveness. Minor enhancement opportunities don't block adoption.

This is excellent work that will meaningfully improve skill quality across the codebase. The systematic approach to preventing known failure modes is exactly the right solution.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 24, 2026

Greptile Summary

This PR introduces /create-skill, a skill factory that scaffolds, validates, and commits new Claude Code skills. It encodes 250+ lessons from prior Greptile review cycles into 17 mandatory patterns, a 17-item Phase 4 self-review checklist, and two automated quality-gate scripts (lint-skill.sh with 11 checks, smoke-test-skill.sh with bash -n syntax validation). The CLAUDE.md change adds a "Never fabricate facts" directive.\n\nThe PR has been through an unusually deep review cycle (40+ previous threads, all resolved), and the final state is structurally sound. Two edge-case gaps remain in lint-skill.sh's Check 4 detection-block tracker:\n\n- One-liner if ...; fi leaves detect_depth stuck — when a detection if and its fi appear on a single line, the inline-fi handler is never reached (bash if/elif short-circuits after the first matching branch), leaving detect_depth at 1 and in_detect=true for all subsequent lines in the fence. Hardcoded npm test / yarn test after the one-liner won't be warned about.\n- find-based detection conditions aren't recognised — the elif find . -name ... branch in the Phase 3 lint-runner template doesn't match any of the detection keywords. The current template is safe because if [ -f \"biome.json\" ] precedes it, but a skill author who leads a detection block with a find condition would see incorrect warnings.\n\nBoth are P2 edge cases with low practical impact on the generated skills this tool produces.

Confidence Score: 4/5

Safe to merge; two minor edge cases in lint-skill.sh's Check 4 detection-block heuristic are false-negative/false-positive gaps with very low real-world impact

After 40+ resolved review rounds the code is thoroughly hardened. The remaining issues are P2 edge cases: (1) one-liner if/fi leaves detect_depth at 1 causing a false negative, and (2) find-based elif branches aren't in the detection keyword set. Neither affects the quality gates for skills following the taught patterns (all examples use multi-line detection blocks).

.claude/skills/create-skill/scripts/lint-skill.sh lines 138–165 (Check 4 detection-block tracking)

Important Files Changed

Filename Overview
.claude/skills/create-skill/SKILL.md New skill factory with 17 quality-gate patterns, 6 phases, and a self-review checklist; structurally sound after extensive review cycles
.claude/skills/create-skill/scripts/lint-skill.sh Static linter with 11 checks; well-implemented but Check 4 has an edge-case false negative for single-line if/fi patterns and misses find-based detection conditions
.claude/skills/create-skill/scripts/smoke-test-skill.sh Bash syntax checker; correctly handles indented blocks, unclosed fences, and uses $BASH for version-consistent checking
CLAUDE.md Adds 'Never fabricate facts' directive — minor additive change

Reviews (42): Last reviewed commit: "Merge branch 'main' into feat/create-ski..." | Re-trigger Greptile

Comment on lines +175 to +184
**Correct:**
````markdown
Detect the test runner:
```bash
if [ -f "pnpm-lock.yaml" ]; then TEST_CMD="pnpm test"
elif [ -f "yarn.lock" ]; then TEST_CMD="yarn test"
else TEST_CMD="npm test"; fi
```
Then run: `$TEST_CMD`
````
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Pattern 6's "Correct" example violates Pattern 1

The "Correct" example for Pattern 6 sets TEST_CMD inside a code fence (lines 179–181) and then references $TEST_CMD in prose outside the fence (Then run: \$TEST_CMD`). This is exactly the cross-fence variable usage that Pattern 1 prohibits — any skill author following this example would produce a skill where $TEST_CMD` is set in one block and used in a separate block, which is the bug Pattern 1 is designed to prevent.

The example should either:

  1. Keep detection and execution in the same block, or
  2. Show persisting TEST_CMD to a file (consistent with Pattern 1's "Correct" guidance)
Suggested change
**Correct:**
````markdown
Detect the test runner:
```bash
if [ -f "pnpm-lock.yaml" ]; then TEST_CMD="pnpm test"
elif [ -f "yarn.lock" ]; then TEST_CMD="yarn test"
else TEST_CMD="npm test"; fi
```
Then run: `$TEST_CMD`
````
**Correct:**
````markdown
Detect the test runner and run in a single block:
```bash
if [ -f "pnpm-lock.yaml" ]; then TEST_CMD="pnpm test"
elif [ -f "yarn.lock" ]; then TEST_CMD="yarn test"
else TEST_CMD="npm test"; fi
$TEST_CMD
```

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 923b51b. Pattern 6's "Correct" example now keeps \ assignment and \ execution in the same bash block, so it no longer violates Pattern 1 (no cross-fence variable usage).

Comment on lines +226 to +230
**Exit condition:** Every dangerous operation identified in Phase: Discovery has a corresponding guard in the SKILL.md.

### For code modifications:
- Run tests after changes: detect test runner per Phase: Write the Skill Body, Pattern 6
- Run lint after changes: detect lint runner the same way (check for `biome.json` → `npx biome check`, `eslint.config.*` → `npx eslint`, fallback → `npm run lint`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Exit condition placed before the final Phase 3 section

The Phase 3 exit condition ("Every dangerous operation identified in Phase: Discovery has a corresponding guard") appears on line 226, but the "For code modifications" section (lines 228–230) comes after it. This means an AI following these instructions could declare Phase 3 complete after passing the exit condition, never reading the code-modification guards (test/lint detection).

The exit condition should be the last item in a phase, not an intermediate gate. Move the ### For code modifications section above the exit condition so the gate covers all Phase 3 content:

Suggested change
**Exit condition:** Every dangerous operation identified in Phase: Discovery has a corresponding guard in the SKILL.md.
### For code modifications:
- Run tests after changes: detect test runner per Phase: Write the Skill Body, Pattern 6
- Run lint after changes: detect lint runner the same way (check for `biome.json``npx biome check`, `eslint.config.*``npx eslint`, fallback → `npm run lint`)
### For code modifications:
- Run tests after changes: detect test runner per Phase: Write the Skill Body, Pattern 6
- Run lint after changes: detect lint runner the same way (check for `biome.json``npx biome check`, `eslint.config.*``npx eslint`, fallback → `npm run lint`)
**Exit condition:** Every dangerous operation identified in Phase: Discovery has a corresponding guard in the SKILL.md.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 923b51b. Moved the "For code modifications" section (test/lint detection) above the exit condition, so the gate now covers all Phase 3 content. An AI following the instructions will process all guard sections before reaching the exit check.


### For code modifications:
- Run tests after changes: detect test runner per Phase: Write the Skill Body, Pattern 6
- Run lint after changes: detect lint runner the same way (check for `biome.json` → `npx biome check`, `eslint.config.*` → `npx eslint`, fallback → `npm run lint`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Lint detection uses prose instead of an explicit script (violates Pattern 6)

Pattern 6 (line 172) requires: "write the explicit detection script — do not use <detected-value> placeholders." However, the lint runner detection here is only described in prose (check for biome.json → npx biome check ...) with no actual bash block. This violates the same pattern the skill teaches, and the detection criteria (config files) are also different from the test runner example (lock files), making "the same way" misleading.

Provide an explicit detection block so skill authors have a concrete template:

Suggested change
- Run lint after changes: detect lint runner the same way (check for `biome.json``npx biome check`, `eslint.config.*``npx eslint`, fallback → `npm run lint`)
- Run lint after changes: detect lint runner:
```bash
if [ -f "biome.json" ]; then LINT_CMD="npx biome check"
elif ls eslint.config.* 2>/dev/null | grep -q .; then LINT_CMD="npx eslint ."
else LINT_CMD="npm run lint"; fi
$LINT_CMD

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 923b51b. Replaced the prose-only lint detection with an explicit bash block that detects biome.json, eslint.config.*, or falls back to npm run lint. This now follows Pattern 6 properly (no placeholders, no "the same way" hand-waving).

- Pattern 6 example: keep TEST_CMD assignment and usage in same block
  to avoid violating Pattern 1 (no cross-fence variables)
- Phase 3: move "For code modifications" section above exit condition
  so the gate covers all Phase 3 content
- Phase 3: replace prose lint detection with explicit bash script
  to follow Pattern 6's own guidance (no placeholders)
@carlos-alm
Copy link
Contributor Author

Addressed Claude review feedback:

  1. Lint command detection (line 231) — now has an explicit bash script block instead of prose, making it reusable as a template.
  2. "Test commands must be detected, not assumed" (line 295) — the example is now in Pattern 6 (same-block detection + execution), which serves as the concrete reference.
  3. Version conflicts between skills — valid enhancement but out of scope for this PR; the skill factory focuses on single-skill structural quality.

The three minor recommendations (troubleshooting section, pattern reference doc, Phase 5 testing instructions) are good ideas for a follow-up iteration but not blocking for the initial version.

@carlos-alm
Copy link
Contributor Author

@greptileai

@carlos-alm
Copy link
Contributor Author

@greptileai What else is missing from this skill creation for it to be more robust and resilient and the skills it creates avoid future code reviews?

Comment on lines +229 to +234
```bash
if [ -f "biome.json" ]; then LINT_CMD="npx biome check"
elif ls eslint.config.* 2>/dev/null | grep -q .; then LINT_CMD="npx eslint ."
else LINT_CMD="npm run lint"; fi
$LINT_CMD
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Phase 3 lint template violates Pattern 2 (its own rule)

The lint detection template block uses 2>/dev/null on line 231 without any justification comment, which directly violates Pattern 2 ("Never use 2>/dev/null without documenting the skip path") and the explicit Rule at line 301 ("Every 2>/dev/null needs a justification comment in the generated skill").

When the AI generates a skill with code modifications and copies this Phase 3 template verbatim, the resulting skill will contain an unjustified 2>/dev/null. A skill author (or a generated skill's self-review in Phase 4) checking against Pattern 2 would correctly flag this as a violation — but the template they were given already contains the violation. This is a self-contradiction that undermines the quality gate it's supposed to enforce.

The 2>/dev/null here intentionally suppresses ls's error when no eslint.config.* glob matches (shell glob expansion failure). That rationale should be documented inline:

Suggested change
```bash
if [ -f "biome.json" ]; then LINT_CMD="npx biome check"
elif ls eslint.config.* 2>/dev/null | grep -q .; then LINT_CMD="npx eslint ."
else LINT_CMD="npm run lint"; fi
$LINT_CMD
```
- Run lint after changes: detect lint runner:
```bash
if [ -f "biome.json" ]; then LINT_CMD="npx biome check"
elif ls eslint.config.* 2>/dev/null | grep -q .; then LINT_CMD="npx eslint ." # 2>/dev/null: ls exits non-zero when glob matches nothing — intentionally tolerant
else LINT_CMD="npm run lint"; fi
$LINT_CMD

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f356b68. Added the justification comment to the 2>/dev/null in the lint detection template: # 2>/dev/null: ls exits non-zero when glob matches nothing — intentionally tolerant. The template now follows Pattern 2 properly.

Comment on lines +113 to +121
````markdown
```bash
mktemp -d > .codegraph/$SKILL_NAME/.tmpdir
```
Later:
```bash
rm -rf "$(cat .codegraph/$SKILL_NAME/.tmpdir)"
```
````
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Pattern 1 "Correct" example uses $SKILL_NAME across separate fences

The Pattern 1 example is specifically teaching that shell variables do not survive across code fence boundaries — yet the "Correct" example itself uses $SKILL_NAME in two separate code blocks without redefining it:

```bash
mktemp -d > .codegraph/$SKILL_NAME/.tmpdir   # $SKILL_NAME set... where?
```
Later:
```bash
rm -rf "$(cat .codegraph/$SKILL_NAME/.tmpdir)"  # $SKILL_NAME undefined
```

The intent is for the AI generating a new skill to substitute the literal skill name (e.g., deploy-check) in place of $SKILL_NAME, treating it as a meta-placeholder rather than a runtime shell variable. But that intent is nowhere explained, and an AI following the example could interpret $SKILL_NAME as a shell variable — producing generated skills where both blocks use an undefined variable, causing the tmpdir path to silently degrade to .codegraph//.tmpdir.

Consider adding a brief clarifying note, or replacing $SKILL_NAME with a concrete example name to make it unambiguous:

```bash
# Replace SKILL_NAME with your actual skill name (e.g. deploy-check)
mktemp -d > .codegraph/deploy-check/.tmpdir
```
Later:
```bash
rm -rf "$(cat .codegraph/deploy-check/.tmpdir)"
```

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f356b68. Replaced $SKILL_NAME with the concrete example deploy-check in Pattern 1's "Correct" example. Both fences now use .codegraph/deploy-check/.tmpdir — no ambiguity about whether it's a runtime variable or a substitution placeholder.

Comment on lines +140 to +148
```bash
PREV_FILE=$(mktemp --suffix=.js)
if git show HEAD:$FILE > "$PREV_FILE" 2>&1; then
codegraph where --file "$PREV_FILE"
else
echo "WARN: $FILE is new (not in HEAD) — skipping before/after comparison"
fi
rm -f "$PREV_FILE"
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Pattern 2 "Correct" example hardcodes .js extension

The Pattern 2 example creates a temp file with mktemp --suffix=.js, and this hardcoded .js extension will be copied by skill authors into new skills regardless of the language they're actually working with. Pattern 3 (directly below) correctly teaches that "temp files passed to codegraph must have the correct extension" — but the Pattern 2 example models the wrong behaviour for non-JS skills.

A skill authoring a Python or Go workflow would blindly copy --suffix=.js from this example, creating a codegraph language detection mismatch.

A small clarifying note would prevent this:

Suggested change
```bash
PREV_FILE=$(mktemp --suffix=.js)
if git show HEAD:$FILE > "$PREV_FILE" 2>&1; then
codegraph where --file "$PREV_FILE"
else
echo "WARN: $FILE is new (not in HEAD) — skipping before/after comparison"
fi
rm -f "$PREV_FILE"
```
```bash
PREV_FILE=$(mktemp --suffix=.js) # adjust extension to match the language of $FILE
if git show HEAD:$FILE > "$PREV_FILE" 2>&1; then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f356b68. Added # adjust extension to match the language of $FILE comment to the mktemp --suffix=.js line. This makes it clear that .js is an example, not a universal default — consistent with Pattern 3's guidance on correct extensions.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 24, 2026

Here are the critical gaps that would make generated skills more robust:

Add execution testing

Phase 4.5 — Smoke Test (between self-review and finalize):

  • Shell-check all bash blocks for syntax errors
  • Verify Phase 0 actually validates the tools it claims to check
  • Run Phase 0 in a test directory to catch argument parsing bugs

The self-review is purely theoretical. Most dogfooding issues (wrong paths, shell syntax, missing tools) only surface when you actually try to run the code.

Missing from checklist

Add to Phase 4 checks:

Idempotency: Re-running the skill on the same state must be safe. Check: does it handle existing output files? What happens on second run?

Dependency validation: Phase 0 must verify all allowed-tools are available before starting work. Catch "command not found" before Phase 3.

Exit codes: Every error path uses explicit exit 1, never silent early returns

State cleanup: If the skill creates .codegraph/$SKILL_NAME/* files, document when they're cleaned up or how users remove them

Examples section: Every skill needs 2-3 realistic usage examples showing common invocations

Missing patterns

Pattern 11 — Progress indicators: For phases >10s, emit progress: echo "Processing file $i/$total"

Pattern 12 — Artifact reuse: Before running expensive operations, check if output exists: if [ -f results.json ]; then echo "Using cached"; else run; fi

Pattern 13 — Platform portability: Test glob patterns on Linux and macOS (e.g., ls *.config.* behaves differently). Document platform-specific behavior.

The smoke test phase would have the biggest impact — it turns "did I follow the checklist?" into "does this actually run?"

- Add 2>/dev/null justification comment to lint detection template (Pattern 2 compliance)
- Replace $SKILL_NAME with concrete example in Pattern 1 to avoid cross-fence ambiguity
- Add extension-adjustment comment to Pattern 2 temp file example
@carlos-alm
Copy link
Contributor Author

Addressed round-2 Greptile feedback in f356b68:

  • P1: Added 2>/dev/null justification comment to lint detection template (Pattern 2 compliance)
  • P2: Replaced $SKILL_NAME with concrete deploy-check in Pattern 1 example (no cross-fence ambiguity)
  • P2: Added extension-adjustment comment to Pattern 2 temp file example

@greptileai

- [ ] **Artifact schema**: If the skill produces files, path/format/schema are documented
- [ ] **Exit conditions**: Each phase states what must be true before the next phase starts
- [ ] **Scope boundary**: The skill's purpose is clear — it does one thing, not five
- [ ] **Examples section**: At least 2-3 realistic usage examples showing common invocations are included
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing Examples section violates own Phase 4 checklist

The Phase 4 Self-Review Checklist (line 308) explicitly requires: "Examples section: At least 2-3 realistic usage examples showing common invocations are included." The PR description states Phase 4 was run against this skill itself, yet the SKILL.md has no ## Examples section anywhere in its 390 lines.

This is a self-referential violation — the skill fails the very quality gate it's supposed to enforce. An Examples section might look like:

## Examples

- `/create-skill deploy-check` — scaffold a deployment validation skill
- `/create-skill review-pr` — scaffold a PR review skill with API calls
- `/create-skill db-migrate` — scaffold a database migration skill with dangerous-op guards

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fb08cbd. Added an ## Examples section with 3 realistic usage examples (deploy-check, review-pr, db-migrate) between Phase 6 and Rules. The skill now passes its own Phase 4 completeness checklist item for examples.

Comment on lines +219 to +227
````markdown
```bash
if [ -f ".codegraph/$SKILL_NAME/results.json" ]; then
echo "Using cached results from previous run"
else
# run expensive operation
fi
```
````
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Pattern 12 example still uses ambiguous $SKILL_NAME meta-placeholder

The previous thread (line 113) identified that Pattern 1's "Correct" example used $SKILL_NAME ambiguously — an AI could read it as a runtime shell variable rather than a substitution placeholder — and it was fixed in commit f356b68 by replacing it with the concrete example deploy-check. The same problem still exists in Pattern 12's example:

if [ -f ".codegraph/$SKILL_NAME/results.json" ]; then

An AI following this example when writing a new skill would face the same ambiguity: should it substitute the actual skill name (e.g., deploy-check), or define $SKILL_NAME as a runtime variable? The fix applied to Pattern 1 (using a concrete example name) was not applied here.

Suggested change
````markdown
```bash
if [ -f ".codegraph/$SKILL_NAME/results.json" ]; then
echo "Using cached results from previous run"
else
# run expensive operation
fi
```
````
Before running expensive operations (codegraph build, embedding generation, batch analysis), check if usable output already exists (replace `deploy-check` with your actual skill name):
````markdown
```bash
if [ -f ".codegraph/deploy-check/results.json" ]; then
echo "Using cached results from previous run"
else
# run expensive operation
fi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fb08cbd. Replaced $SKILL_NAME with concrete deploy-check in Pattern 12 example (same approach as the Pattern 1 fix in f356b68). Added clarifying note: "replace deploy-check with your actual skill name".


## Phase 2 — Write the Skill Body

Write each phase following these **mandatory patterns** (derived from the top 10 Greptile review findings):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 "Top 10" description contradicts 13 defined patterns

The Phase 2 introduction says the patterns are "derived from the top 10 Greptile review findings" — but 13 patterns are defined (Patterns 1–13). The exit condition at the bottom of Phase 2 correctly states "follows all 13 patterns," but the leading description undermines this by implying only 10 are core findings.

An AI reading this skill might reasonably treat Patterns 11–13 as supplementary or optional based on the "top 10" framing, rather than as equally mandatory. The heading and exit condition are internally contradictory.

Suggested change
Write each phase following these **mandatory patterns** (derived from the top 10 Greptile review findings):
Write each phase following these **mandatory patterns** (derived from Greptile review findings across 200+ comments):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fb08cbd. Changed "top 10" to "Greptile review findings across 200+ comments" in Phase 2 intro (line 95) and updated the checklist header from "the top 10" to "all 13 patterns" (line 286). Both now accurately reflect the 13 defined patterns.

Comment on lines +286 to +296
### Anti-pattern checks (the top 10):
- [ ] **Shell variables**: No variable is set in one code fence and used in another. State that must persist is written to a file
- [ ] **Silent failures**: No `2>/dev/null` without a documented skip rationale. No commands that swallow errors
- [ ] **Temp file extensions**: Every temp file passed to codegraph has the correct language extension
- [ ] **Temp file uniqueness**: Every temp path uses `mktemp`, never hardcoded paths
- [ ] **Cross-references**: All step references use phase names, not bare numbers
- [ ] **Placeholders**: Every `<placeholder>` has a preceding detection/assignment script
- [ ] **Contradictions**: No two sections describe contradictory behavior for the same condition
- [ ] **Rules sync**: Every command/tool in the procedure is covered by Rules. Every Rules exception maps to a real step
- [ ] **Redundancy**: No codegraph command is run twice with the same arguments. Later phases reference earlier results
- [ ] **Skip validation**: If `--start-from`/`--skip-*` is supported, every skip path validates required artifacts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Pattern 12 (artifact reuse) missing from Phase 4 checklist

Every other mandatory pattern (1–11, 13) has a corresponding checklist item in Phase 4. Pattern 12 — "Before running expensive operations, check if usable output already exists" — has no corresponding verification step. The anti-pattern section header still says "the top 10" which explains why it was omitted, but since Pattern 12 is a mandatory pattern, an author running Phase 4 will never be prompted to verify they implemented it.

Consider adding to the "Robustness checks" or "Safety checks" section:

- [ ] **Artifact reuse**: Expensive operations (codegraph build, embedding generation, batch analysis) check for existing output before re-running

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fb08cbd. Added Pattern 12 (artifact reuse) to the anti-pattern checklist, along with Patterns 11 and 13 which were previously only in the Safety checks section. Moved all three to the "Anti-pattern checks (all 13 patterns)" section and removed the duplicates from Safety checks, so each pattern has exactly one checklist item.

Adds a top-level callout instructing Claude to always verify factual
claims (licenses, versions, features) before stating them.
- Replace "top 10" with accurate "all 13 patterns" in Phase 2 intro and
  checklist header since 13 patterns are defined
- Replace ambiguous $SKILL_NAME with concrete deploy-check in Pattern 12
  example (same fix as Pattern 1 in previous round)
- Add Patterns 11-13 (progress, artifact reuse, portability) to Phase 4
  anti-pattern checklist and remove duplicates from Safety checks
- Add missing Examples section with 3 realistic usage examples (required
  by own Phase 4 completeness checklist)
@carlos-alm
Copy link
Contributor Author

@greptileai

Comment on lines +229 to +231
This supports both idempotent re-runs and resume-after-failure.

### Pattern 13: Platform portability
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Phase 3 lint template violates Pattern 13 (glob expansion)

The lint detection template uses ls eslint.config.*, which is the exact glob-expansion pattern that Pattern 13 prohibits:

"Use find ... -name "*.ext" instead of glob expansion (ls *.ext) which differs between bash versions"

Any skill generated with code-modification guards will copy this template verbatim and inherit a portability violation — the very kind of issue this skill is designed to prevent. A skill author running Phase 4 self-review would correctly flag this under the "Platform portability" checklist item, but then have no compliant template to use.

Suggested change
This supports both idempotent re-runs and resume-after-failure.
### Pattern 13: Platform portability
if [ -f "biome.json" ]; then LINT_CMD="npx biome check"
elif find . -maxdepth 1 -name "eslint.config.*" -print -quit 2>/dev/null | grep -q .; then LINT_CMD="npx eslint ." # 2>/dev/null: find exits non-zero when path is unreadable — intentionally tolerant
else LINT_CMD="npm run lint"; fi
$LINT_CMD

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 98abfb2. Replaced ls eslint.config.* with find . -maxdepth 1 -name "eslint.config.*" -print -quit in the Phase 3 lint detection template. The template now follows Pattern 13 (platform portability) — no glob expansion.

Comment on lines +345 to +355
- Environment validation produces clear error messages on failure

```bash
TEST_DIR=$(mktemp -d)
cd "$TEST_DIR"
git init
# Simulate the Phase 0 checks from the skill here
cd -
rm -rf "$TEST_DIR"
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Phase 5 smoke test has no cleanup trap — working directory may be left dirty

The smoke test block uses cd "$TEST_DIR" and defers cleanup to cd - and rm -rf "$TEST_DIR" at the end. If any of the "Simulate Phase 0 checks" commands fail partway through (e.g. a tool is missing and exit 1 fires), the shell exits the block mid-way, leaving:

  1. The process's working directory set to $TEST_DIR (for the AI's shell session)
  2. $TEST_DIR undeleted on disk

The fix is to add a trap guard so cleanup always runs, consistent with Pattern 2's guidance that every failure path must be handled explicitly:

Suggested change
- Environment validation produces clear error messages on failure
```bash
TEST_DIR=$(mktemp -d)
cd "$TEST_DIR"
git init
# Simulate the Phase 0 checks from the skill here
cd -
rm -rf "$TEST_DIR"
```
TEST_DIR=$(mktemp -d)
trap 'cd - > /dev/null 2>&1; rm -rf "$TEST_DIR"' EXIT
cd "$TEST_DIR"
git init
# Simulate the Phase 0 checks from the skill here
cd -
rm -rf "$TEST_DIR"
trap - EXIT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 98abfb2. Added a \ guard to the Phase 5 smoke test block. If any command fails mid-block, the trap ensures cleanup runs (restore working directory + delete temp dir). The trap is cleared after normal cleanup with .

- Phase 3 lint template: replace `ls eslint.config.*` with
  `find . -maxdepth 1 -name "eslint.config.*"` to comply with Pattern 13
  (platform portability)
- Phase 5 smoke test: add `trap` guard for cleanup so early exits don't
  leave the shell in a foreign directory or temp files on disk
@carlos-alm
Copy link
Contributor Author

@greptileai

Comment on lines +21 to +35
## Phase 0 — Discovery

Before writing anything, gather requirements interactively. Ask the user these questions (all at once, not one-by-one):

1. **Purpose** — What does this skill do? (one paragraph)
2. **Arguments** — What CLI arguments should it accept? (e.g. `--fix`, `--dry-run`, `<path>`)
3. **Phases** — What are the major steps? (bullet list of 3-8 phases)
4. **Tools needed** — Which tools does it need? (Bash, Read, Write, Edit, Glob, Grep, Agent)
5. **Artifacts** — Does it produce output files? If so, where and what format?
6. **Dangerous operations** — Does it modify code, push to git, call external APIs, or delete files?
7. **Resume/skip support** — Should it support `--start-from` or `--skip-*` flags for long-running pipelines?

**Wait for the user's answers before proceeding.** Do not guess or assume.

**Exit condition:** All 7 questions have answers. Purpose, arguments, phases, tools, artifacts, dangerous ops, and resume support are defined.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Phase 0 fails its own structural requirement and Phase 4 checklist item

The structural requirements in Phase 1 (line 83) state: "Phase 0 always exists — pre-flight checks, argument parsing, environment validation." The Phase 4 self-review checklist (line 282) also checks: "Phase 0 exists and validates the environment."

However, this skill's own Phase 0 is purely discovery/requirements gathering — it has no pre-flight checks and no environment validation. Specifically:

  • There's no check that the current directory is a git repo (or any other environment precondition)
  • No verification that required tools (Bash, Write, etc.) are accessible
  • Argument parsing ($ARGUMENTSSKILL_NAME + regex validation) happens in the ## Arguments section before Phase 0, not in Phase 0 itself

A skill author who faithfully runs Phase 4 self-review against this SKILL.md would check "Phase 0 exists and validates the environment" and find a gap.

Consider renaming Phase 0 to "Phase 0 — Discovery & Pre-flight" and prepending a brief environment check, e.g.:

## Phase 0 — Discovery & Pre-flight

**Pre-flight:** Confirm you are in a git repository root (`git rev-parse --show-toplevel` should succeed). Validate `$ARGUMENTS` is set and matches kebab-case (`^[a-z][a-z0-9]*(-[a-z0-9]+)*$`). If not, abort with a clear error.

**Discovery:**

This brings the skill into alignment with the structural requirement it teaches and the checklist item it enforces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f741925. Renamed Phase 0 to "Discovery & Pre-flight" and added pre-flight checks at the top: git repo root validation (git rev-parse --show-toplevel) and $ARGUMENTS kebab-case validation. The exit condition now includes "Pre-flight passed (git repo confirmed, skill name validated)." This brings the skill into alignment with its own structural requirement (line 83) and Phase 4 checklist item (line 282).


### Pattern 9: No command redundancy

If a phase runs a codegraph command and stores the result, later phases must reference that result — not re-run the command. Add a note: "Using <result> from Phase: <Name>".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Pattern 9 instruction uses undefined <result> / <Name> placeholders — ambiguous guidance

Pattern 6 (line 170) explicitly prohibits undefined placeholders: "Every variable or placeholder in pseudocode must have a preceding assignment." Yet Pattern 9's instruction reads:

Add a note: "Using <result> from Phase: <Name>".

The <result> and <Name> tokens are placeholders with no preceding definition. A skill author following this instruction might write the note literally with angle brackets rather than substituting the actual artifact variable name and phase name. The fix from the previous thread (replacing $SKILL_NAME with deploy-check in Pattern 12's example) should be applied here too — use a concrete example:

Suggested change
If a phase runs a codegraph command and stores the result, later phases must reference that result — not re-run the command. Add a note: "Using <result> from Phase: <Name>".
If a phase runs a codegraph command and stores the result, later phases must reference that result — not re-run the command. Add a note like: "Using `impact_report` from Phase: Impact Analysis".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f741925. Replaced the ambiguous <result> / <Name> placeholders in Pattern 9 with a concrete example: "Using \ from Phase: Impact Analysis". This follows the same approach used for Pattern 1 (f356b68) and Pattern 12 (fb08cbd) — concrete examples instead of undefined angle-bracket tokens.

@carlos-alm
Copy link
Contributor Author

@greptileai

- Run lint after changes: detect lint runner:
```bash
if [ -f "biome.json" ]; then LINT_CMD="npx biome check"
elif find . -maxdepth 1 -name "eslint.config.*" -print -quit 2>/dev/null | grep -q .; then LINT_CMD="npx eslint ." # 2>/dev/null: find exits non-zero when path is unreadable — intentionally tolerant
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 2>/dev/null justification is inaccurate after lsfind migration

The previous version used ls eslint.config.* which does produce stderr when no glob matches, legitimately justifying 2>/dev/null. Since that was replaced with find . -maxdepth 1 -name "eslint.config.*" -print -quit, find no longer produces any stderr output when no files match the pattern — it simply outputs nothing with exit code 0. The current comment ("find exits non-zero when path is unreadable") describes an edge case (unreadable child entries under -maxdepth 1) that is both uncommon and not the original intent.

This is a self-referential Pattern 2 issue: the skill teaches "never use 2>/dev/null without documenting the skip path," but the documentation it provides is now misleading about when the suppression actually fires.

Either remove the 2>/dev/null (since find doesn't need it for the no-match case) or update the comment to accurately reflect the actual scenario it guards:

Suggested change
elif find . -maxdepth 1 -name "eslint.config.*" -print -quit 2>/dev/null | grep -q .; then LINT_CMD="npx eslint ." # 2>/dev/null: find exits non-zero when path is unreadable — intentionally tolerant
elif find . -maxdepth 1 -name "eslint.config.*" -print -quit | grep -q .; then LINT_CMD="npx eslint ."

If you prefer to keep it as a defensive guard against unexpected find errors (e.g. permission issues on .'s children), the comment should say so explicitly:

# 2>/dev/null: suppresses find permission errors on unreadable directory entries — defensive only, unlikely in practice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 88955d0. Removed the \ entirely since \ doesn't produce stderr on no-match (it outputs nothing with exit 0). The inaccurate justification comment is gone too — no suppression needed means no comment needed.

Comment on lines +14 to +23
- `$ARGUMENTS` must contain the skill name in kebab-case (e.g. `deploy-check`)
- If `$ARGUMENTS` is empty, ask the user for a skill name before proceeding

Set `SKILL_NAME` to the provided name. Validate it is kebab-case (`^[a-z][a-z0-9]*(-[a-z0-9]+)*$`). Reject otherwise.

---

## Phase 0 — Discovery & Pre-flight

**Pre-flight:** Confirm you are in a git repository root (`git rev-parse --show-toplevel` should succeed). Validate `$ARGUMENTS` is set and matches kebab-case (`^[a-z][a-z0-9]*(-[a-z0-9]+)*$`). If not, abort with a clear error.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Kebab-case validation defined twice — self-violation of Pattern 7

The same validation decision (validate $ARGUMENTS against ^[a-z][a-z0-9]*(-[a-z0-9]+)*$) is specified in two separate places:

  • Lines 14–17 (Arguments section): "Set SKILL_NAME to the provided name. Validate it is kebab-case. Reject otherwise."
  • Line 23 (Phase 0): "Validate $ARGUMENTS is set and matches kebab-case… If not, abort with a clear error."

Pattern 7 (line 188) says: "Each decision… must be defined in exactly one place. If two sections describe the same decision path, consolidate them and reference the single source."

The Arguments section defining the validation once and Phase 0 simply invoking it (e.g. "Parse $ARGUMENTS per the Arguments section above") would satisfy Pattern 7 and remove the ambiguity about which location has the authoritative rejection logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 88955d0. Phase 0 now says "Parse $ARGUMENTS per the Arguments section above" instead of re-specifying the kebab-case regex. The Arguments section (line 17) is the single source of truth for validation, satisfying Pattern 7.

cd "$TEST_DIR"
git init
# Simulate the Phase 0 checks from the skill here
cd -
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 cd - prints the previous directory to stdout

cd - outputs the target directory path to stdout by default on most shells. In the smoke test context this pollutes the output and is inconsistent with the trap line above (which correctly suppresses it with > /dev/null 2>&1).

Suggested change
cd -
cd - > /dev/null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 88955d0. Changed cd - to cd - > /dev/null in the smoke test cleanup (line 355), consistent with the trap line above which already suppresses stdout.

- Remove unnecessary 2>/dev/null from find command in lint template
- Deduplicate kebab-case validation (Phase 0 now references Arguments section)
- Suppress cd - stdout in smoke test template
@carlos-alm
Copy link
Contributor Author

@greptileai

Comment on lines +116 to +122
```bash
mktemp -d > .codegraph/deploy-check/.tmpdir
```
Later:
```bash
rm -rf "$(cat .codegraph/deploy-check/.tmpdir)"
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Pattern 1 "Correct" example silently fails when .codegraph/deploy-check/ doesn't exist

The redirect mktemp -d > .codegraph/deploy-check/.tmpdir requires the parent directory .codegraph/deploy-check/ to already exist. If it doesn't, bash sets up the redirect first, fails to open the file, and never invokes mktemp -d — so the temp directory is never created and the .tmpdir file is never written. The later block's $(cat .codegraph/deploy-check/.tmpdir) then expands to an empty string, and rm -rf "" is executed.

This is the exact kind of silent failure Pattern 2 is designed to prevent, yet the Pattern 1 "Correct" example contains it. Skill authors who copy this pattern without pre-creating the directory will get a non-obvious failure.

The example should either show directory creation as a prerequisite step, or add a note that the directory must already exist:

**Correct:** Persist state to a file (use your actual skill name, not a variable).
First ensure the directory exists:
```bash
mkdir -p .codegraph/deploy-check
mktemp -d > .codegraph/deploy-check/.tmpdir
```
Later:
```bash
rm -rf "$(cat .codegraph/deploy-check/.tmpdir)"
```

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 659e60d. Added mkdir -p .codegraph/deploy-check before the mktemp -d redirect in Pattern 1's Correct example. The parent directory is now explicitly created, preventing the silent failure where bash can't open the redirect target.

Comment on lines +349 to +358
```bash
TEST_DIR=$(mktemp -d)
trap 'cd - > /dev/null 2>&1; rm -rf "$TEST_DIR"' EXIT
cd "$TEST_DIR"
git init
# Simulate the Phase 0 checks from the skill here
cd - > /dev/null
rm -rf "$TEST_DIR"
trap - EXIT
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Inconsistent stderr suppression between trap and explicit cleanup

The trap on line 351 uses cd - > /dev/null 2>&1 (suppresses both stdout and stderr), but the explicit cleanup on line 355 uses only cd - > /dev/null (suppresses stdout only). If $OLDPWD is unset or the previous directory was removed, cd - will print an error to stderr on the explicit cleanup path. The previous review thread fixed the missing > /dev/null but didn't bring the stderr suppression into parity with the trap line.

Suggested change
```bash
TEST_DIR=$(mktemp -d)
trap 'cd - > /dev/null 2>&1; rm -rf "$TEST_DIR"' EXIT
cd "$TEST_DIR"
git init
# Simulate the Phase 0 checks from the skill here
cd - > /dev/null
rm -rf "$TEST_DIR"
trap - EXIT
```
cd - > /dev/null 2>&1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 659e60d. Changed explicit cleanup path from cd - > /dev/null to cd - > /dev/null 2>&1, matching the trap line's stderr suppression. Both paths are now consistent.

Comment on lines +41 to +92
## Phase 1 — Scaffold

Create the skill directory and SKILL.md with frontmatter:

```bash
mkdir -p .claude/skills/$SKILL_NAME
```

Write the SKILL.md file starting with this structure:

```markdown
---
name: $SKILL_NAME
description: <one-line from user's purpose>
argument-hint: "<from user's argument design>"
allowed-tools: <from user's tool list>
---

# /$SKILL_NAME — <Title>

<Purpose paragraph from Phase 0>

## Arguments

- `$ARGUMENTS` parsing rules here
- Set state variables: `DRY_RUN`, `AUTO_FIX`, etc.

## Phase 0 — Pre-flight

1. Confirm environment (repo root, node version, required tools)
2. Parse `$ARGUMENTS` into state variables
3. Validate preconditions

## Phase N — <Name>

<Steps>

## Rules

- <Hard constraints>
```

### Structural requirements to include in every skill:

1. **Phase 0 always exists** — pre-flight checks, argument parsing, environment validation
2. **Every phase has a clear exit condition** — what must be true before moving to the next phase
3. **Arguments section** — explicit parsing of `$ARGUMENTS` into named state variables
4. **Rules section** — hard constraints at the bottom, kept in sync with the procedure
5. **Artifact definitions** — if the skill produces files, specify path, format, and schema

**Exit condition:** `.claude/skills/$SKILL_NAME/SKILL.md` exists with valid frontmatter, Phase 0, Arguments section, and Rules section.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Phase 1 violates its own Idempotency checklist item

The Phase 4 Safety checks mandate: "Idempotency: Re-running the skill on the same state is safe. Existing output files are handled (skip, overwrite with warning, or merge)."

However, Phase 1 never checks whether .claude/skills/$SKILL_NAME/SKILL.md already exists before writing it. A second invocation of /create-skill deploy-check silently overwrites the user's existing, possibly partially-written skill without any warning. This is a self-referential violation of the idempotency requirement the skill itself defines.

A guard should be added at the start of Phase 1:

if [ -f ".claude/skills/$SKILL_NAME/SKILL.md" ]; then
  echo "WARN: .claude/skills/$SKILL_NAME/SKILL.md already exists."
  echo "Proceeding will overwrite it. Confirm or abort."
  # Prompt user for confirmation before continuing
fi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 659e60d. Added an idempotency guard at the top of Phase 1 that checks for an existing SKILL.md and warns before overwriting. This brings the skill into compliance with its own Phase 4 Idempotency checklist item.

@carlos-alm
Copy link
Contributor Author

Addressed both findings from the latest Greptile review in 5717c21:

  1. P1 — Compact else ...; fi one-liner leaves detect_depth unreset: Added an elif branch that checks for inline \bfi\b when in_detect is active, so compact forms correctly close the detection block.
  2. P2 — Check 2 misses &>/dev/null bash shorthand: Added &>/dev/null to the Check 2 regex.

@greptileai

Comment on lines +108 to +110
if echo "$line" | grep -qE '^\s*git add (--\s+\.|\.|(-A|--all))'; then
error "bash block $bnum: 'git add .' or 'git add -A' — stage named files only"
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Check 3 false-positives on dotfiles and relative paths

The \. alternative in the regex matches any literal dot, so git add .gitignore, git add .env, and git add ./src/file.ts are all flagged as violations — even though they are staging specific, named files (the correct behavior this check is designed to enforce).

Verified locally:

git add .gitignore    → MATCH (false positive)
git add ./src/file.ts → MATCH (false positive)
git add .env          → MATCH (false positive)
git add -- .gitignore → MATCH (false positive)  ← the `--\s+\.` variant also lacks termination

A skill that stages .gitignore or .eslintrc.cjs would be incorrectly blocked by its own quality gate, eroding trust in the linter — the same concern raised in the earlier grep -qF prefix thread (fixed in daa4c88).

The fix is to require that the dot ends the token (followed by whitespace, end-of-line, or a comment #):

Suggested change
if echo "$line" | grep -qE '^\s*git add (--\s+\.|\.|(-A|--all))'; then
error "bash block $bnum: 'git add .' or 'git add -A' — stage named files only"
fi
if echo "$line" | grep -qE '^\s*git add (--\s+\.([[:space:]]|$)|\.([ \t;#]|$)|(-A|--all))'; then

With this, git add . and git add -- . still fire, while git add .gitignore, git add ./src/file.ts, and git add -- .gitignore are correctly allowed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 163b328 — the \. alternatives in the regex now require the dot to be a standalone token by anchoring with ([ \t;#]|$). This prevents false positives on dotfiles (.gitignore, .env) and relative paths (./src/file.ts).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 163b328 — Check 3 regex now terminates the dot with ([ \t;#]|$) so git add .gitignore, git add ./src/file.ts, and git add -- .gitignore are correctly allowed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 163b328 — Check 3 regex now terminates the dot with ([ \t;#]|$) so git add .gitignore, git add ./src/file.ts, and git add -- .gitignore are correctly allowed.

@carlos-alm
Copy link
Contributor Author

@greptileai

- **No Claude Code references** in commit messages or the skill body.
- **Persist temp state to files, not shell variables** when it must survive across code fences.
- **Test commands must be detected, not assumed.** Never hardcode `npm test` — detect the package manager first.
- **Every `2>/dev/null` needs a justification comment** in the generated skill.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Rules section understates prohibited npm commands

The rule currently reads:

Test commands must be detected, not assumed. Never hardcode \npm test` — detect the package manager first.`

But lint-skill.sh Check 4 (line 162) now catches three forms:

'^\s*(npm test|npm run (test|lint))([^:A-Za-z0-9_]|$)'

A skill author who reads only this Rules section would believe npm run test and npm run lint are acceptable outside a detection block, then be surprised when Check 4 fires. Since the Rules section is the canonical quick-reference that authors consult, it should enumerate all three prohibited patterns:

Suggested change
- **Every `2>/dev/null` needs a justification comment** in the generated skill.
- **Test commands must be detected, not assumed.** Never hardcode `npm test`, `npm run test`, or `npm run lint` — detect the package manager first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 00fb02b — Rules section now lists all prohibited forms: npm test, npm run test, npm run lint, yarn test, and pnpm test, matching what lint-skill.sh Check 4 enforces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 00fb02b and 3f43d8b — Rules section now enumerates all 9 prohibited forms.

fi
fi
if ! $in_detect; then
if echo "$line" | grep -qE '^\s*(npm test|npm run (test|lint))([^:A-Za-z0-9_]|$)'; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Check 4 coverage gap for yarn test and pnpm test

The check catches npm test, npm run test, and npm run lint hardcoded outside a detection block, but not yarn test or pnpm test. A skill that unconditionally runs:

yarn test   # outside any detection block

passes Check 4 silently even though it violates Pattern 6 in exactly the same way. Pattern 6's "Correct" example includes pnpm-lock.yaml and yarn.lock detection precisely because those runners also need to be detected.

Adding yarn test and pnpm test to the regex closes the gap:

if echo "$line" | grep -qE '^\s*(npm test|npm run (test|lint)|yarn test|pnpm test)([^:A-Za-z0-9_]|$)'; then

(Note: yarn run lint and pnpm run lint are less common than their test equivalents, but could be added alongside for completeness.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 00fb02b — Check 4 regex now uses (npm|yarn|pnpm) alternation to catch all three package managers for both direct commands (e.g. yarn test) and run variants (e.g. pnpm run lint) outside detection blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 00fb02b — Check 4 regex now matches (npm|yarn|pnpm) test|(npm|yarn|pnpm) run (test|lint), covering all three package managers.

@carlos-alm
Copy link
Contributor Author

@greptileai

fi
```

- **`lint-skill.sh`** checks for cross-fence variable bugs, bare `2>/dev/null`, hardcoded `npm test` / `npm run test` / `npm run lint`, `git add .` / `git add -- .`, missing frontmatter, missing Phase 0 / Rules, missing exit conditions, GNU-only `find -quit`, hardcoded `/tmp/` paths, and `sed -i` portability issues.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Phase 5 description understates Check 4's yarn/pnpm coverage

After commit 00fb02b, lint-skill.sh Check 4 was expanded to catch all three package managers — including yarn run test, yarn run lint, pnpm run test, and pnpm run lint — via the regex:

(npm|yarn|pnpm) test|(npm|yarn|pnpm) run (test|lint)

But this line still only lists npm variants:

"hardcoded npm test / npm run test / npm run lint"

A skill author who writes yarn run lint or pnpm run test outside a detection block will see an unexpected WARN from the linter with no explanation in the documentation they were given to understand it.

The Rules section (line 570) has a related gap: it enumerates yarn test and pnpm test but not yarn run test, yarn run lint, pnpm run test, or pnpm run lint — so authors reading the Rules section as the authoritative prohibited list will also miss these forms.

Suggested change
- **`lint-skill.sh`** checks for cross-fence variable bugs, bare `2>/dev/null`, hardcoded `npm test` / `npm run test` / `npm run lint`, `git add .` / `git add -- .`, missing frontmatter, missing Phase 0 / Rules, missing exit conditions, GNU-only `find -quit`, hardcoded `/tmp/` paths, and `sed -i` portability issues.
- **`lint-skill.sh`** checks for cross-fence variable bugs, bare `2>/dev/null`, hardcoded `npm test` / `npm run test` / `npm run lint` / `yarn test` / `yarn run test` / `yarn run lint` / `pnpm test` / `pnpm run test` / `pnpm run lint`, `git add .` / `git add -- .`, missing frontmatter, missing Phase 0 / Rules, missing exit conditions, GNU-only `find -quit`, hardcoded `/tmp/` paths, and `sed -i` portability issues.

The Rules section (line 570) should similarly be updated to include yarn run test, yarn run lint, pnpm run test, and pnpm run lint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f43d8b — Phase 5 description (line 501) now lists all nine prohibited forms across npm/yarn/pnpm (including run variants). Rules section (line 570) similarly updated to include yarn run test, yarn run lint, pnpm run test, and pnpm run lint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in 3f43d8b — Phase 5 description and Rules section now list all 9 prohibited forms including yarn/pnpm run variants.

@carlos-alm
Copy link
Contributor Author

@greptileai

Comment on lines +252 to +254
if echo "$stripped" | grep -qE '"/tmp/[a-zA-Z]|/tmp/[a-zA-Z][a-zA-Z]'; then
# Allow ${TMPDIR:-/tmp} pattern
if ! echo "$stripped" | grep -qE '\$\{TMPDIR:-/tmp\}'; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Check 11 regex gap — unquoted single-letter /tmp/ paths not caught

The two alternatives in the regex are:

  • "/tmp/[a-zA-Z] — requires a leading " character before /tmp/
  • /tmp/[a-zA-Z][a-zA-Z] — requires at least two letters in the path name (no leading quote required)

This creates a blind spot: a bare (unquoted) hardcoded path with a single-letter tail — e.g. /tmp/f.json — matches neither alternative.

The second alternative should use [a-zA-Z] (one letter) to be consistent with the quoting-aware first alternative:

Suggested change
if echo "$stripped" | grep -qE '"/tmp/[a-zA-Z]|/tmp/[a-zA-Z][a-zA-Z]'; then
# Allow ${TMPDIR:-/tmp} pattern
if ! echo "$stripped" | grep -qE '\$\{TMPDIR:-/tmp\}'; then
if echo "$stripped" | grep -qE '"/tmp/[a-zA-Z]|/tmp/[a-zA-Z]'; then

This also subsumes the existing two-letter form, so no coverage is lost.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in de8e6c5. Changed /tmp/[a-zA-Z][a-zA-Z] to /tmp/[a-zA-Z] so single-letter unquoted paths like /tmp/f.json are now caught by Check 11.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in de8e6c5 — Check 11 regex uses single [a-zA-Z] in both alternatives, catching unquoted single-letter paths like /tmp/f.json.

'```bash'*) in_block=true; prev_line="$line"; continue ;;
'```'*) in_block=false; prev_line="$line"; continue ;;
esac
if $in_block && echo "$line" | grep -qE '2>/dev/null|>[ ]?/dev/null 2>&1|&>/dev/null'; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Check 2 regex misses > followed by 2+ spaces before /dev/null

The pattern >[ ]?/dev/null 2>&1 uses [ ]? which matches zero or one space. The form > /dev/null 2>&1 (two spaces, which some auto-formatters or editors emit) would not match either the 2>/dev/null alternative nor the >[ ]? alternative, and would silently bypass the justification requirement.

Extending to [ ]* (zero-or-more) closes the gap at no cost:

Suggested change
if $in_block && echo "$line" | grep -qE '2>/dev/null|>[ ]?/dev/null 2>&1|&>/dev/null'; then
if $in_block && echo "$line" | grep -qE '2>/dev/null|>[ ]*/dev/null 2>&1|&>/dev/null'; then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in de8e6c5. Changed >[ ]?/dev/null to >[ ]*/dev/null so multi-space variants (e.g. from auto-formatters) are now caught by Check 2.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in de8e6c5. Changed >[ ]?/dev/null to >[ ]*/dev/null so multi-space variants (e.g. from auto-formatters) are now caught by Check 2.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in de8e6c5 — Check 2 regex now uses >[ ]*/dev/null (zero-or-more) instead of >[ ]?, closing the multi-space gap.

Comment on lines +506 to +524
### Phase 0 dry-run

Run the skill's Phase 0 (pre-flight) logic in a temporary test directory to verify:
- Argument parsing works for valid inputs and rejects invalid ones
- Tool availability checks actually detect missing tools (temporarily rename one to confirm)
- Environment validation produces clear error messages on failure

```bash
TEST_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")
# > /dev/null 2>&1: suppress cd's directory-path output — cleanup should be silent
trap 'cd - > /dev/null 2>&1; rm -rf "$TEST_DIR"' EXIT
cd "$TEST_DIR"
git init --quiet
# Simulate the Phase 0 checks from the skill here
# > /dev/null 2>&1: suppress cd's directory-path output — returning to original directory
cd - > /dev/null 2>&1
rm -rf "$TEST_DIR"
trap - EXIT
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Phase 5 smoke-test template scaffolds only the happy path

The Phase 5 description (line 508) explicitly requires testing that the skill rejects invalid inputs and produces clear error messages when required tools are absent. But the template bash block only sets up a valid environment (git init --quiet) and leaves a generic # Simulate the Phase 0 checks from the skill here comment. An AI following this template literally will only exercise the success path.

Consider adding stub comments for the failure-mode cases so the template is self-documenting:

# --- Happy path: valid skill name and all tools present ---
# Simulate: ARGUMENTS="deploy-check"; run the Phase 0 pre-flight checks

# --- Failure path 1: not in a git repo ---
# (run checks from a non-git directory — expect ERROR message + non-zero exit)

# --- Failure path 2: invalid skill name ---
# ARGUMENTS="BadName!"; run argument validation — expect rejection

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fc54517. Added failure-path stub comments to the Phase 5 smoke-test template: non-git directory, invalid skill name, and missing required tool. The template now guides skill authors to exercise both success and error paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in fc54517 — Phase 5 smoke-test template now includes failure-path stubs for non-git directory, invalid skill name, and missing required tool.

Check 2: change `>[ ]?/dev/null` to `>[ ]*/dev/null` so multi-space
variants emitted by auto-formatters are caught.

Check 11: change `/tmp/[a-zA-Z][a-zA-Z]` to `/tmp/[a-zA-Z]` so
single-letter unquoted paths like `/tmp/f.json` are caught.
The template previously only scaffolded the happy path. Now includes
stub comments for three failure scenarios (non-git directory, invalid
skill name, missing required tool) so generated skills exercise both
success and error paths.
@carlos-alm
Copy link
Contributor Author

@greptileai

in_block=false
# Run syntax check on the collected block
if [ -s "$TMPBLOCK" ]; then
if output=$(bash -n "$TMPBLOCK" 2>&1); then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 bash -n uses PATH bash, not the version-guarded bash

The script guards against bash < 4 at startup (lines 8–11) using ${BASH_VERSINFO[0]}, which reflects the bash that's actually executing this script. But line 68 invokes bash -n "$TMPBLOCK" — this resolves bash from PATH, which on macOS is /bin/bash (3.2) even after brew install bash.

On macOS, a user who runs smoke-test-skill.sh via /usr/local/bin/bash smoke-test-skill.sh (bash 4+) will pass the version guard, but bash -n will call /bin/bash (3.2). Any generated skill using bash 4+ features (declare -A, mapfile, readarray) would be falsely failed with a cryptic "bash: line X: declare: -A: invalid option" error rather than a clean "syntax OK".

This is exactly the portability concern Pattern 13 and the bash 4+ guard in both scripts are designed to prevent. The fix is to use "$BASH" (the special variable that holds the path to the currently-running bash):

Suggested change
if output=$(bash -n "$TMPBLOCK" 2>&1); then
if output=$("$BASH" -n "$TMPBLOCK" 2>&1); then

$BASH is set automatically by bash to the full path of the current interpreter, so it will always be the 4+ binary that passed the version guard.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ba179cd. Replaced bash -n with "$BASH" -n so the syntax check uses the same bash interpreter that passed the version guard, not the PATH-resolved one. On macOS where /bin/bash is 3.2, this prevents false failures for bash 4+ syntax like declare -A or mapfile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in ba179cd — smoke-test-skill.sh now uses \"\/usr/bin/bash\" -n instead of bash -n, ensuring the version-guarded interpreter is used.


# ── Check 3: git add . or git add -A (inside bash blocks only) ───────
while IFS=$'\t' read -r bnum line; do
if echo "$line" | grep -qE '^\s*git add (--\s+\.([ \t;#]|$)|\.([ \t;#]|$)|(-A|--all))'; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Check 3 --all alternative lacks trailing word boundary

The other two git add alternatives correctly anchor with ([ \t;#]|$) to prevent partial matches, but (-A|--all) has no trailing terminator. Any future git option prefixed with --all would match and emit a false-positive error.

Suggested change
if echo "$line" | grep -qE '^\s*git add (--\s+\.([ \t;#]|$)|\.([ \t;#]|$)|(-A|--all))'; then
if echo "$line" | grep -qE '^\s*git add (--\s+\.([ \t;#]|$)|\.([ \t;#]|$)|(-A|--all)([ \t;#]|$))'; then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e63d742. Added ([ \t;#]|$) trailing anchor to the (-A|--all) alternative in Check 3, matching the pattern used by the other two alternatives. Hypothetical future options like --all-staged will no longer false-positive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in e63d742 — Check 3 --all alternative now has trailing ([ \t;#]|$) boundary, consistent with the other alternatives.

Comment on lines +152 to +165
elif $in_detect && echo "$line" | grep -qE '\bfi\b'; then
# fi appears inline (e.g. "else ...; fi") — still closes the outermost detection block
if [ "$detect_depth" -gt 0 ]; then
detect_depth=$((detect_depth - 1))
[ "$detect_depth" -eq 0 ] && in_detect=false
else
in_detect=false
fi
fi
if ! $in_detect; then
if echo "$line" | grep -qE '^\s*((npm|yarn|pnpm) test|(npm|yarn|pnpm) run (test|lint))([^:A-Za-z0-9_]|$)'; then
warn "Line $line_num: Hardcoded '$(echo "$line" | sed 's/^[[:space:]]*//')' — detect package manager first (Pattern 6)"
fi
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Inline-fi resets in_detect before the hardcoded-command check runs on the same line

Branch 5 handles compact one-liners like else npm test; fi. When processed: branch 5 fires → in_detect=false, then the hardcoded-command check (line 161) runs on the same line and flags npm test with "detect package manager first" — a misleading message since detection already happened; the real issue is invoking the command directly rather than via TEST_CMD/LINT_CMD. Worth documenting in the comment even if the full fix is deferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e63d742. Introduced a \ variable that captures the detection context before fi-processing runs. The hardcoded-command check now evaluates against \ instead of , so commands on the same line as an inline \ (e.g. ) are correctly recognized as inside the detection block and not falsely flagged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in e63d742 — introduced was_in_detect variable that captures detection context before fi-processing, so inline commands like else npm test; fi are evaluated correctly. Explanatory comments added at lines 136 and 166-168.

On macOS, `bash -n` resolves to /bin/bash (3.2) even when the script
is running under bash 4+. Using "$BASH" ensures the same interpreter
that passed the version guard is used for syntax checking.
…fi detection context (#587)

- Check 3: added ([ \t;#]|$) after (-A|--all) to prevent false positives
  on hypothetical --all-prefixed options.
- Check 4: use was_in_detect to evaluate hardcoded-command check in the
  detection context that was active at the start of the line, so compact
  forms like "else npm test; fi" are not falsely flagged after the
  inline fi resets in_detect.
@carlos-alm
Copy link
Contributor Author

@greptileai

Comment on lines +429 to +479
## Phase 4 — Self-Review Checklist

Before finalizing, audit the SKILL.md against every item below. **Do not skip any item.** Fix violations before proceeding.

### Structure checks:
- [ ] Frontmatter has all four fields: `name`, `description`, `argument-hint`, `allowed-tools`
- [ ] `name` matches the directory name
- [ ] Phase 0 exists and validates the environment
- [ ] Arguments section explicitly parses `$ARGUMENTS` into named variables
- [ ] Rules section exists at the bottom
- [ ] Every phase has a clear name (not just a number)

### Anti-pattern checks (all 17 patterns):
- [ ] **Shell variables**: No variable is set in one code fence and used in another. State that must persist is written to a file
- [ ] **Silent failures**: No `2>/dev/null` without a documented skip rationale. No commands that swallow errors
- [ ] **Temp file extensions**: Every temp file passed to codegraph has the correct language extension
- [ ] **Temp file uniqueness**: Every temp path uses `mktemp`, never hardcoded paths
- [ ] **Cross-references**: All step references use phase names, not bare numbers
- [ ] **Placeholders**: Every `<placeholder>` has a preceding detection/assignment script
- [ ] **Contradictions**: No two sections describe contradictory behavior for the same condition
- [ ] **Rules sync**: Every command/tool in the procedure is covered by Rules. Every Rules exception maps to a real step
- [ ] **Redundancy**: No codegraph command is run twice with the same arguments. Later phases reference earlier results
- [ ] **Skip validation**: If `--start-from`/`--skip-*` is supported, every skip path validates required artifacts
- [ ] **Progress indicators**: Phases that iterate over files or run batch operations emit progress (`Processing $i/$total`)
- [ ] **Artifact reuse**: Expensive operations (codegraph build, embedding generation, batch analysis) check for existing output before re-running
- [ ] **Platform portability**: No `sed -i ''`, no unquoted globs, no GNU-only flags without fallback or documentation
- [ ] **Cleanup traps**: Phases that create temp files or modify repo state use `trap ... EXIT` for cleanup on error paths
- [ ] **Git stash safety**: Every `git stash push` has a named STASH_REF lookup; every `pop`/`drop` is guarded by `[ -n "$STASH_REF" ]`
- [ ] **Division-by-zero**: Every arithmetic division or percentage computation guards against zero denominators
- [ ] **DRY_RUN consistency**: If `--dry-run` is supported, every destructive operation is gated on the flag at the point of action, not just at phase entry

### Robustness checks:
- [ ] **Rollback paths**: Every destructive operation has documented undo instructions
- [ ] **Error messages**: Every failure path produces a specific, actionable error message (not just "failed")
- [ ] **Concurrency safety**: No shared global state that would break under parallel invocation
- [ ] **Determinism**: No non-deterministic algorithm output used for before/after comparisons (e.g., Louvain community IDs)

### Completeness checks:
- [ ] **Artifact schema**: If the skill produces files, path/format/schema are documented
- [ ] **Exit conditions**: Each phase states what must be true before the next phase starts
- [ ] **Scope boundary**: The skill's purpose is clear — it does one thing, not five
- [ ] **Examples section**: At least 2-3 realistic usage examples showing common invocations are included

### Safety checks:
- [ ] **Idempotency**: Re-running the skill on the same state is safe. Existing output files are handled (skip, overwrite with warning, or merge)
- [ ] **Dependency validation**: Phase 0 verifies all shell commands used in bash blocks are available before starting work (e.g. `command -v git mktemp jq`). "Command not found" is caught before Phase 2, not during Phase 3
- [ ] **Exit codes**: Every error path uses explicit `exit 1`. No silent early returns that leave the pipeline in an ambiguous state
- [ ] **State cleanup**: If the skill creates `.codegraph/$SKILL_NAME/*` files, the skill documents when they're cleaned up or how users remove them (e.g., `rm -rf .codegraph/$SKILL_NAME` in a cleanup section)
- [ ] **Git commit safety**: All `git add` calls use explicit file paths (never `.` or `-A`); `git diff --cached --quiet` is checked before committing to avoid empty commits

Read through the entire SKILL.md one more time after checking all items. Fix anything found.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Phase 4 missing exit condition — fails its own Check 9 and checklist item

Phase 4 (Self-Review Checklist) has no **Exit condition:** line. Every other phase (0–3, 5, 6) has one. This creates two self-referential failures:

  1. lint-skill.sh Check 9 will emit a WARN when run against this SKILL.md itself:

    WARN:  Phase '## Phase 4 — Self-Review Checklist' has no 'Exit condition' before the next phase
    

    This is confirmed by tracing the logic: when Check 9 encounters ## Phase 5 — Smoke Test (line 483), prev_phase is ## Phase 4 — Self-Review Checklist and phase_has_exit is false.

  2. Phase 4's own checklist item (line 468) requires: "Exit conditions: Each phase states what must be true before the next phase starts." Phase 4 itself violates the rule it enforces.

The PR description states "Ran the skill's Phase 4 self-review checklist against itself" — but a skill that produces a Check 9 WARN when linted cannot have fully passed that validation.

Suggested change
## Phase 4 — Self-Review Checklist
Before finalizing, audit the SKILL.md against every item below. **Do not skip any item.** Fix violations before proceeding.
### Structure checks:
- [ ] Frontmatter has all four fields: `name`, `description`, `argument-hint`, `allowed-tools`
- [ ] `name` matches the directory name
- [ ] Phase 0 exists and validates the environment
- [ ] Arguments section explicitly parses `$ARGUMENTS` into named variables
- [ ] Rules section exists at the bottom
- [ ] Every phase has a clear name (not just a number)
### Anti-pattern checks (all 17 patterns):
- [ ] **Shell variables**: No variable is set in one code fence and used in another. State that must persist is written to a file
- [ ] **Silent failures**: No `2>/dev/null` without a documented skip rationale. No commands that swallow errors
- [ ] **Temp file extensions**: Every temp file passed to codegraph has the correct language extension
- [ ] **Temp file uniqueness**: Every temp path uses `mktemp`, never hardcoded paths
- [ ] **Cross-references**: All step references use phase names, not bare numbers
- [ ] **Placeholders**: Every `<placeholder>` has a preceding detection/assignment script
- [ ] **Contradictions**: No two sections describe contradictory behavior for the same condition
- [ ] **Rules sync**: Every command/tool in the procedure is covered by Rules. Every Rules exception maps to a real step
- [ ] **Redundancy**: No codegraph command is run twice with the same arguments. Later phases reference earlier results
- [ ] **Skip validation**: If `--start-from`/`--skip-*` is supported, every skip path validates required artifacts
- [ ] **Progress indicators**: Phases that iterate over files or run batch operations emit progress (`Processing $i/$total`)
- [ ] **Artifact reuse**: Expensive operations (codegraph build, embedding generation, batch analysis) check for existing output before re-running
- [ ] **Platform portability**: No `sed -i ''`, no unquoted globs, no GNU-only flags without fallback or documentation
- [ ] **Cleanup traps**: Phases that create temp files or modify repo state use `trap ... EXIT` for cleanup on error paths
- [ ] **Git stash safety**: Every `git stash push` has a named STASH_REF lookup; every `pop`/`drop` is guarded by `[ -n "$STASH_REF" ]`
- [ ] **Division-by-zero**: Every arithmetic division or percentage computation guards against zero denominators
- [ ] **DRY_RUN consistency**: If `--dry-run` is supported, every destructive operation is gated on the flag at the point of action, not just at phase entry
### Robustness checks:
- [ ] **Rollback paths**: Every destructive operation has documented undo instructions
- [ ] **Error messages**: Every failure path produces a specific, actionable error message (not just "failed")
- [ ] **Concurrency safety**: No shared global state that would break under parallel invocation
- [ ] **Determinism**: No non-deterministic algorithm output used for before/after comparisons (e.g., Louvain community IDs)
### Completeness checks:
- [ ] **Artifact schema**: If the skill produces files, path/format/schema are documented
- [ ] **Exit conditions**: Each phase states what must be true before the next phase starts
- [ ] **Scope boundary**: The skill's purpose is clear — it does one thing, not five
- [ ] **Examples section**: At least 2-3 realistic usage examples showing common invocations are included
### Safety checks:
- [ ] **Idempotency**: Re-running the skill on the same state is safe. Existing output files are handled (skip, overwrite with warning, or merge)
- [ ] **Dependency validation**: Phase 0 verifies all shell commands used in bash blocks are available before starting work (e.g. `command -v git mktemp jq`). "Command not found" is caught before Phase 2, not during Phase 3
- [ ] **Exit codes**: Every error path uses explicit `exit 1`. No silent early returns that leave the pipeline in an ambiguous state
- [ ] **State cleanup**: If the skill creates `.codegraph/$SKILL_NAME/*` files, the skill documents when they're cleaned up or how users remove them (e.g., `rm -rf .codegraph/$SKILL_NAME` in a cleanup section)
- [ ] **Git commit safety**: All `git add` calls use explicit file paths (never `.` or `-A`); `git diff --cached --quiet` is checked before committing to avoid empty commits
Read through the entire SKILL.md one more time after checking all items. Fix anything found.
Read through the entire SKILL.md one more time after checking all items. Fix anything found.
**Exit condition:** All 17 checklist items pass. The SKILL.md has no unresolved structure, anti-pattern, robustness, completeness, or safety violations.
---

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e04545d — added the missing **Exit condition:** line to Phase 4. The skill now passes its own Check 9 with zero warnings and zero errors.

@carlos-alm
Copy link
Contributor Author

@greptileai

Comment on lines +138 to +165
if echo "$line" | grep -qE '^\s*if\s.*(-f\s|-d\s|lock|package|command -v|which\s)'; then
in_detect=true
was_in_detect=true
detect_depth=$((detect_depth + 1))
elif echo "$line" | grep -qE '^\s*elif\s.*(-f\s|-d\s|lock|package|command -v|which\s)'; then
# elif is a sibling branch — set in_detect but do NOT increment depth
in_detect=true
was_in_detect=true
elif echo "$line" | grep -qE '^\s*if\b'; then
# nested if (not a detection block) — track depth only when inside detection
[ "$in_detect" = true ] && detect_depth=$((detect_depth + 1))
elif echo "$line" | grep -qE '^\s*fi\b'; then
if [ "$detect_depth" -gt 0 ]; then
detect_depth=$((detect_depth - 1))
[ "$detect_depth" -eq 0 ] && in_detect=false
else
# Safety reset: in_detect was set by an elif without a preceding detection if
in_detect=false
fi
elif $in_detect && echo "$line" | grep -qE '\bfi\b'; then
# fi appears inline (e.g. "else ...; fi") — still closes the outermost detection block
if [ "$detect_depth" -gt 0 ]; then
detect_depth=$((detect_depth - 1))
[ "$detect_depth" -eq 0 ] && in_detect=false
else
in_detect=false
fi
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Check 4: one-liner if ...; fi leaves detect_depth stuck at 1

When a detection if and its closing fi appear on the same line (e.g. if [ -f "package.json" ]; then echo "found"; fi), the first if-detection branch (line 138) fires and increments detect_depth to 1. Because bash if/elif short-circuits, the later branches — including the inline-fi handler at line 157 — are never evaluated for the same line. The result is that detect_depth stays at 1 and in_detect stays true for every subsequent line in the same code fence.

Consequence: any npm test / yarn test line that follows the one-liner in the same fence is silently treated as "inside a detection block" and never warned about — a false negative.

Concrete failing case:

if [ -f "package.json" ]; then echo "found package.json"; fi
npm test   # ← not flagged, because in_detect is stuck true

A fix is to check for the closing fi on the same line as the opening detection if, before committing the depth increment:

if echo "$line" | grep -qE '^\s*if\s.*(-f\s|-d\s|lock|package|command -v|which\s)'; then
  in_detect=true
  was_in_detect=true
  # Only increment depth if fi does NOT also close on this line
  if ! echo "$line" | grep -qE '\bfi\b'; then
    detect_depth=$((detect_depth + 1))
  fi

Comment on lines +142 to +145
elif echo "$line" | grep -qE '^\s*elif\s.*(-f\s|-d\s|lock|package|command -v|which\s)'; then
# elif is a sibling branch — set in_detect but do NOT increment depth
in_detect=true
was_in_detect=true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Check 4 detection heuristic misses find-based conditions

The elif detection pattern (line 142) recognises -f, -d, lock, package, command -v, and which — but not find-based conditions. The Phase 3 lint-runner template in SKILL.md (line 419) uses:

elif find . -maxdepth 1 -name "eslint.config.*" | grep -q .; then LINT_CMD="npx eslint ."

This elif branch is currently safe only because the preceding if [ -f "biome.json" ] already sets in_detect=true. However, a skill author who restructures the detection block to lead with a find condition — e.g.:

if find . -name "*.go" | grep -q .; then CMD="go test ./..."
elif [ -f "package.json" ]; then CMD="npm test"
else CMD="true"; fi

…would have the outer if fall through to the non-detection if\b branch (line 146), which only increments detect_depth if already inside a detection block. in_detect would stay false, and npm test in the elif/else branch would be incorrectly flagged.

Consider adding find\s to both detection patterns:

if echo "$line" | grep -qE '^\s*if\s.*(-f\s|-d\s|lock|package|command -v|which\s|find\s)'; then

And the same for the elif branch (line 142).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant