Skip to content

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

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

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

Conversation

@carlos-alm
Copy link
Copy Markdown
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
Copy Markdown

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
Copy Markdown
Contributor

greptile-apps bot commented Mar 24, 2026

Greptile Summary

This PR adds /create-skill — a 6-phase skill factory that scaffolds, writes, and validates new Claude Code skills, encoding lessons from 250+ Greptile review comments into 17 mandatory patterns and a 17-item self-review checklist. The skill is accompanied by two quality-gate scripts (lint-skill.sh with 11 static checks and smoke-test-skill.sh for bash -n syntax verification) that are run against every generated skill before finalization.

The PR went through an extensive iterative review cycle (30+ threads), and the vast majority of structural, self-referential, and portability issues have been resolved. The final code is well-engineered: the linter uses an O(1) associative-array approach for cross-fence variable tracking, a depth-counter for nested detection blocks, and POSIX-compatible patterns ([[:space:]]*) for indented fence matching.

Findings:

  • lint-skill.sh Check 2 misses 2> /dev/null (space between 2> and /dev/null) — the spaced redirect form is valid bash but bypasses Pattern 2's justification gate. The fix is a one-character change: 2[[:space:]]*/dev/null.
  • Phase 5 description omits Check 6b and Check 8b — the lint-skill.sh summary on SKILL.md line 517 doesn't mention that the name field must match the directory name (Check 6b) or that a missing ## Examples section is now an error (Check 8b), so authors seeing those failures will lack context.

Confidence Score: 4/5

Safe to merge — no blocking issues; the two findings are minor linter gaps that don't affect the skill's core functionality.

The skill and its quality-gate scripts are production-ready after an extensive multi-round review. The two remaining issues are low-severity: a one-character regex gap in Check 2 (spaced 2> /dev/null form) and a documentation gap in the Phase 5 description (two new checks not listed). Neither causes incorrect skill generation or data loss.

.claude/skills/create-skill/scripts/lint-skill.sh line 109 (Check 2 regex gap) and .claude/skills/create-skill/SKILL.md line 517 (Phase 5 description completeness).

Important Files Changed

Filename Overview
.claude/skills/create-skill/SKILL.md Core skill file (599 lines) defining 6 phases, 17 mandatory patterns, and a 17-item self-review checklist. Thoroughly revised across many review iterations — structure, examples, exit conditions, and pattern templates are all present and self-consistent. One minor gap: the Phase 5 lint-skill.sh description omits two checks (6b, 8b) added during this review cycle.
.claude/skills/create-skill/scripts/lint-skill.sh 332-line static analyser with 11 checks covering cross-fence variable bugs, silent failures, hardcoded commands, portability issues, and structural completeness. Well-engineered (O(1) variable lookup, depth-tracking detection blocks, indented-fence support). One gap: Check 2 misses the 2> /dev/null form (space between 2> and /dev/null).
.claude/skills/create-skill/scripts/smoke-test-skill.sh 108-line bash -n syntax checker. Handles indented fences, quadruple-backtick skip regions, unclosed-block detection, zero-block warning, bash 4+ guard, and uses $BASH for version-consistent syntax checking. No new issues found.
CLAUDE.md Two-line addition to CLAUDE.md. No issues found.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["/create-skill skill-name"] --> B["Phase 0 — Discovery & Pre-flight\n• Bash: tool + git repo validation\n• Interactive: 7 requirement questions"]
    B --> C["Phase 1 — Scaffold\n• Idempotency guard\n• mkdir + Write SKILL.md from template"]
    C --> D["Phase 2 — Write the Skill Body\n• Apply 17 mandatory patterns"]
    D --> E["Phase 3 — Dangerous Operation Guards\n• git / file deletion / API / code-mod guards"]
    E --> F["Phase 4 — Self-Review Checklist\n• 17-item anti-pattern check"]
    F --> G["Phase 5 — Smoke Test\n• lint-skill.sh — 11 static checks\n• smoke-test-skill.sh — bash -n all blocks"]
    G --> H["Phase 6 — Finalize\n• User review + commit"]
Loading

Reviews (56): Last reviewed commit: "fix(skill): check 11 catches unquoted an..." | 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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor Author

@greptileai

@carlos-alm
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor Author

@greptileai


Write the SKILL.md file starting with this structure:

```markdown
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Scaffold template uses \``markdown```` instead of quadruple-backtick fences

Check 9 in lint-skill.sh itself warns: "Limitation: nested fences inside \``markdown```blocks … will togglein_block` incorrectly. Wrap such regions in quadruple-backtick ```` fences to avoid false positives."

The scaffold template here is exactly that case — a \``markdown```block that contains an inner```bash```block (the pre-flight example at line 91). Whenlint-skill.sh` Check 9 parses this file, the sequence is:

  1. ```markdownin_block=true (skip content)
  2. Inner ```bash (line 91) → in_block=false (content now processed as outside-a-block!)
  3. The pre-flight bash code (lines 92–97) is examined for ## Phase / **Exit condition:** markers
  4. Inner ``` (line 98) → in_block=true again
  5. Template prose including ## Phase 0 — Pre-flight, ## Phase N, and **Exit condition:** placeholders is skipped
  6. Closing ``` (line 119) → in_block=false

Currently this doesn't produce false results because the bash code in the template (lines 92–97) happens to contain no phase markers. But it is fragile: if a future edit adds a comment containing ## Phase or **Exit condition:** inside the template's bash block, Check 9 would produce incorrect results when linting create-skill/SKILL.md itself.

The fix is to wrap the scaffold template in quadruple-backtick fences, consistent with the recommendation in Check 9's own code comment:

Suggested change
```markdown
````markdown

And close with:


This would cause Check 9 to skip the entire template block (including the inner bash block), making the linter's behavior for this file robust regardless of future template edits.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c32e1d5 — changed the scaffold template fences from triple-backtick to quadruple-backtick on lines 72 and 119. Check 9 now correctly skips the entire template region (including the inner bash block at line 91), making the linter robust against future edits that add phase markers inside the template. Both lint-skill.sh (0 errors, 0 warnings) and smoke-test-skill.sh (10/10 passed) run clean.

The scaffold template at line 72 used triple-backtick fences which
causes Check 9's in_block toggle to misfire when the template contains
inner bash blocks. Switching to quadruple-backtick fences ensures
Check 9 skips the entire template region, making the linter robust
against future edits that add phase markers inside the template.
@carlos-alm
Copy link
Copy Markdown
Contributor Author

Addressed the remaining P2 Greptile finding in c32e1d5: changed the scaffold template fences from triple-backtick to quadruple-backtick (lines 72 and 119), so Check 9 correctly skips the entire template region. Both lint-skill.sh (0 errors, 0 warnings) and smoke-test-skill.sh (10/10 passed) run clean.

@greptileai

)

Every inner-loop check now uses [[ =~ ]] and [[ == *glob* ]] instead of
spawning echo|grep subshells. On Windows, where process creation is
~100x slower than Linux, this reduces runtime from >120 s (timeout) to
~5 s. All 11 checks produce identical results — verified with violation,
detection-block, and one-liner test cases.
@carlos-alm
Copy link
Copy Markdown
Contributor Author

Fixed a performance regression in lint-skill.sh (3b630ab):

Problem: On Windows, lint-skill.sh timed out after 120+ seconds due to process fork overhead. Every inner-loop check used echo "$line" | grep -qE patterns, spawning 2 subprocesses per call. With 63 block lines * 7 variables * ~4 grep pairs in Check 1 alone, that's ~1764 process forks — each costing ~50ms on Windows vs ~0.5ms on Linux.

Fix: Replaced all echo | grep patterns in inner loops with bash builtins:

  • [[ "$line" =~ regex ]] for regex matching
  • [[ "$line" == *glob* ]] for substring checks
  • ${line,,} for case-insensitive comparison (replacing grep -i)
  • ${line%%pattern} for comment stripping (replacing sed)

Result: Runtime dropped from >120s (timeout) to ~5s on Windows. All 11 checks produce identical results — verified with violation detection, detection-block, and one-liner test cases. Both lint-skill.sh (0 errors, 0 warnings) and smoke-test-skill.sh (10/10 passed) run clean against SKILL.md.

@greptileai

Comment on lines +169 to +172
# One-liner: detection block is self-contained — reset so subsequent lines are checked normally
in_detect=false
else
detect_depth=$((detect_depth + 1))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Check 4 false-positive on hyphenated npm/yarn/pnpm script names

The terminal anchor ([^:A-Za-z0-9_]|$) correctly excludes : (to avoid flagging npm run test:unit) but does NOT exclude -. Because - is not a word character and not in the excluded set, any hyphenated npm script will be falsely flagged as a hardcoded command:

npm run lint-staged   # ← FALSE POSITIVE: "lint" matches, "-" satisfies [^:A-Za-z0-9_]
npm run test-ci       # ← FALSE POSITIVE: "test" matches, "-" satisfies [^:A-Za-z0-9_]
yarn run lint-fix     # ← FALSE POSITIVE
pnpm run test-watch   # ← FALSE POSITIVE

lint-staged in particular is an extremely common dependency — most Node.js projects that use it will trigger this false positive, eroding trust in the linter in the same way the previous grep -qF prefix-match fix (daa4c88) was motivated.

The fix mirrors the colon-exclusion approach: add - to the excluded character set.

Suggested change
# One-liner: detection block is self-contained — reset so subsequent lines are checked normally
in_detect=false
else
detect_depth=$((detect_depth + 1))
if [[ "$line" =~ ^[[:space:]]*((npm|yarn|pnpm)\ test|(npm|yarn|pnpm)\ run\ (test|lint))([^:\-A-Za-z0-9_]|$) ]]; then

With this, npm run lint-staged and npm run test-ci are correctly allowed, while bare npm run lint, npm run test, etc. are still caught.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0332d88. Placed - at the end of the negated character class [^:A-Za-z0-9_-] instead of using \-, which broke the entire regex in bash [[ =~ ]]. Verified: npm run lint-staged and npm run test-ci are now correctly allowed, while bare npm run lint, npm run test, and npm test are still caught.

Comment on lines +280 to +285
prev_phase="$line"
phase_has_exit=false
fi
if [[ "${line,,}" == *'**exit condition'* ]]; then
phase_has_exit=true
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Check 11 misses single-quoted /tmp/ paths

The regex [\"/]/tmp/[a-zA-Z] uses the character class [\"/] which matches ", \, or /. It catches double-quoted paths ("/tmp/output.json") and unquoted paths (/tmp/output.json), but NOT single-quoted paths:

FILE='/tmp/output.json'   # ← NOT caught — single quote is not in [\"/]
rm -f '/tmp/tempfile.sh'  # ← NOT caught

Adding ' to the character class closes this gap:

Suggested change
prev_phase="$line"
phase_has_exit=false
fi
if [[ "${line,,}" == *'**exit condition'* ]]; then
phase_has_exit=true
fi
if [[ "$stripped" =~ [\"\'\/]/tmp/[a-zA-Z] ]]; then

The ${TMPDIR:-/tmp} allowlist check on the next line still applies, so correct template-syntax patterns are not affected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0332d88. Added single-quote to the character class: ["'\/] → catches FILE='/tmp/output.json' alongside double-quoted and unquoted paths. The ${TMPDIR:-/tmp} allowlist is unaffected. Verified with a test file containing single-quoted, double-quoted, and unquoted /tmp/ paths — all three are now caught while the portable template syntax is still allowed.

)

Check 4: Place `-` at end of negated character class `[^:A-Za-z0-9_-]`
so hyphenated script names like `npm run lint-staged` and `npm run
test-ci` are not falsely flagged. `\-` inside `[[ =~ ]]` broke the
entire regex on bash.

Check 11: Add single-quote to the `/tmp/` path detection character
class `["'\/]` so `FILE='/tmp/output.json'` is caught alongside
double-quoted and unquoted variants.
@carlos-alm
Copy link
Copy Markdown
Contributor Author

Addressed both findings from the latest Greptile review in 0332d88:

  1. P1 — Check 4 false-positive on hyphenated script names: Placed - at end of character class [^:A-Za-z0-9_-] (not \- which broke the regex in bash). npm run lint-staged / npm run test-ci now correctly pass; bare npm run lint / npm test still flagged.
  2. P2 — Check 11 misses single-quoted /tmp/ paths: Added ' to character class ["'\/]. Single-quoted paths like FILE='/tmp/output.json' are now caught.

@greptileai

'```'*) in_block=false; prev_line="$line"; continue ;;
esac
if $in_block; then
if [[ "$line" =~ 2\>/dev/null ]] || [[ "$line" =~ \>[[:space:]]*/dev/null\ 2\>\&1 ]] || [[ "$line" == *'&>/dev/null'* ]]; then
Copy link
Copy Markdown
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 multiple spaces between /dev/null and 2>&1

The regex \>[[:space:]]*/dev/null\ 2\>\&1 uses [[:space:]]* (zero-or-more whitespace) between > and /dev/null, correctly catching both >/dev/null and > /dev/null. However, the \ 2\>\&1 part matches exactly one space before 2>&1. A double-spaced form like >/dev/null 2>&1 would silently bypass Check 2 because neither the 2>/dev/null nor the \>[[:space:]]*/dev/null\ 2\>\&1 alternative catches it.

While uncommon in hand-written code, auto-formatters or editors that insert an extra space could produce this form. Replacing \ 2 with [[:space:]]+2 closes the gap consistently:

Suggested change
if [[ "$line" =~ 2\>/dev/null ]] || [[ "$line" =~ \>[[:space:]]*/dev/null\ 2\>\&1 ]] || [[ "$line" == *'&>/dev/null'* ]]; then
if [[ "$line" =~ 2\>/dev/null ]] || [[ "$line" =~ \>[[:space:]]*/dev/null[[:space:]]+2\>\&1 ]] || [[ "$line" == *'&>/dev/null'* ]]; then

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 83a3b1d — replaced the literal single-space anchor ( \ 2) with [[:space:]]+2 so double-spaced forms like >/dev/null 2>&1 are now also caught by Check 2. Both lint-skill.sh (0 errors, 0 warnings) and smoke-test-skill.sh (10/10 passed) run clean.

carlos-alm added a commit that referenced this pull request Mar 29, 2026
Replace literal single-space anchor (\ 2) with [[:space:]]+2 so
>/dev/null  2>&1 (double-spaced) is also caught by the bare-redirect
linter check.
@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

carlos-alm added a commit that referenced this pull request Mar 29, 2026
Replace literal single-space anchor (\ 2) with [[:space:]]+2 so
>/dev/null  2>&1 (double-spaced) is also caught by the bare-redirect
linter check.
Comment on lines +279 to +284
fi
prev_phase="$line"
phase_has_exit=false
fi
if [[ "${line,,}" == *'**exit condition'* ]]; then
phase_has_exit=true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Check 11 false negatives for common unquoted /tmp/ assignments and arguments

The regex [\"\'\/]/tmp/[a-zA-Z] only triggers when /tmp/ is immediately preceded by ", ', or /. Two very common patterns are missed:

TMPFILE=/tmp/output.json   # '=' before '/tmp/' — not in ["'\/]
rm /tmp/output.json        # ' ' before '/tmp/' — not in ["'\/]

These are among the most natural ways to write a hardcoded /tmp/ path, and both slip through without any warning. Pattern 4 specifically targets hardcoded paths like /tmp/skill-output.json — both examples above are exactly what it prohibits.

Alternatively, match anything NOT alphanumeric/underscore before /tmp/:

if [[ "$stripped" =~ (^|[^A-Za-z0-9_])/tmp/[a-zA-Z] ]]; then

This catches =/tmp/..., /tmp/..., and /tmp/... at the start of a line, while the ${TMPDIR:-/tmp} allowlist exclusion on the next line still correctly suppresses the portable mktemp pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c1fc463 — widened the Check 11 regex from ["'\/]/tmp/[a-zA-Z] to (^|[^A-Za-z0-9_])/tmp/[a-zA-Z] so TMPFILE=/tmp/output.json (preceded by =) and rm /tmp/output.json (preceded by space) are now caught. The ${TMPDIR:-/tmp} allowlist exclusion still correctly suppresses the portable mktemp pattern. Both lint-skill.sh (0 errors, 0 warnings) and smoke-test-skill.sh (10/10 passed) run clean.

Replace literal single-space anchor (\ 2) with [[:space:]]+2 so
>/dev/null  2>&1 (double-spaced) is also caught by the bare-redirect
linter check.
Widen the regex from ["'\/]/tmp/ to (^|[^A-Za-z0-9_])/tmp/ so
TMPFILE=/tmp/output.json and rm /tmp/output.json are also flagged.
The ${TMPDIR:-/tmp} allowlist exclusion still suppresses the portable
mktemp pattern.
@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

'```'*) in_block=false; prev_line="$line"; continue ;;
esac
if $in_block; then
if [[ "$line" =~ 2\>/dev/null ]] || [[ "$line" =~ \>[[:space:]]*/dev/null[[:space:]]+2\>\&1 ]] || [[ "$line" == *'&>/dev/null'* ]]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Check 2 misses 2> /dev/null (spaced redirect form)

The first condition [[ "$line" =~ 2\>/dev/null ]] only matches the no-space form. The valid bash redirect 2> /dev/null (with a space between 2> and /dev/null) satisfies neither the first condition nor the second (\>[[:space:]]*/dev/null[[:space:]]+2\>\&1). A skill author who writes:

git show HEAD:"$FILE" 2> /dev/null

would have this line silently bypass Check 2 — Pattern 2's core quality gate. The fix extends the 2> alternative to allow optional whitespace:

Suggested change
if [[ "$line" =~ 2\>/dev/null ]] || [[ "$line" =~ \>[[:space:]]*/dev/null[[:space:]]+2\>\&1 ]] || [[ "$line" == *'&>/dev/null'* ]]; then
if [[ "$line" =~ 2[[:space:]]*/dev/null ]] || [[ "$line" =~ \>[[:space:]]*/dev/null[[:space:]]+2\>\&1 ]] || [[ "$line" == *'&>/dev/null'* ]]; then

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 3ea7a35 — changed 2\>/dev/null to 2\>[[:space:]]*/dev/null so the spaced redirect form 2> /dev/null is now caught by Check 2. Verified with a test skill containing the spaced form — correctly flags it.

fi
```

- **`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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Phase 5 description omits Check 6b and Check 8b

The lint-skill.sh description lists all the checks authors should expect, but two checks added during this PR's review cycle are missing:

  • Check 6bname field must match the directory name (errors when they diverge)
  • Check 8b## Examples section must be present (added alongside the structural requirement)

An author who runs lint-skill.sh and sees ERROR: Missing '## Examples' section or ERROR: Frontmatter 'name: ...' does not match directory name will have no description here to explain why those checks fired. They'd need to read the linter source directly.

Suggested change
- **`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.
- **`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 (including `name` matching directory name), missing Phase 0 / Rules / Examples, missing exit conditions, GNU-only `find -quit`, hardcoded `/tmp/` paths, and `sed -i` portability issues.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6e1f4fb — updated the Phase 5 lint-skill.sh description to include both missing checks: Check 6b (name field must match directory name) and Check 8b (## Examples section must be present). Description now reads: "missing frontmatter (including name matching directory name), missing Phase 0 / Rules / Examples".

@carlos-alm
Copy link
Copy Markdown
Contributor Author

Addressed both findings from the latest Greptile review:

  1. P2 — Check 2 misses spaced redirect 2> /dev/null: Added [[:space:]]* after 2\> so both 2>/dev/null and 2> /dev/null forms are caught (3ea7a35).
  2. P2 — Phase 5 description omits Check 6b and Check 8b: Updated description to include name matching directory name and ## Examples section requirement (6e1f4fb).

Both lint-skill.sh (0 errors, 0 warnings) and smoke-test-skill.sh (10/10 passed) run clean.

@greptileai

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