
Publish Skill
- 44 installs
- 236 repo stars
- Updated August 3, 2026
- aperivue/medsci-skills
Publish-skill is a Claude Code skill that converts a personal agent skill into a distributable, open-source-ready package via a 7-phase audit and packaging pipeline.
About
Publish-skill is a Claude skill that converts a personal agent skill into a distributable, open-source-ready package. It runs a seven-phase pipeline: identify the source, apply a skill-worthiness gate, check originality, run a zero-tolerance PII de-identification audit, generalize language, verify license compatibility, and package for commit. It stops publication when a skill fails the worthiness or originality gates.
- 7-phase pipeline to turn a personal skill into a distributable package
- Zero-tolerance PII audit plus a skill-worthiness gate
- License-compatibility and cross-platform adapter review
Publish Skill by the numbers
- 44 all-time installs (skills.sh)
- Ranked #344 of 782 Skill Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
publish-skill capabilities & compatibility
- Capabilities
- publish skill · orchestrate
- Works with
- github
- Use cases
- documentation · security audit
What publish-skill says it does
**Zero tolerance**: the skill must have exactly 0 PII matches before proceeding.
npx skills add https://github.com/aperivue/medsci-skills --skill publish-skillAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 44 |
|---|---|
| repo stars | ★ 236 |
| Last updated | August 3, 2026 |
| Repository | aperivue/medsci-skills ↗ |
What it does
A skill author uses it to scrub PII, generalize, and license-check a personal skill before releasing it open source.
Who is it for?
Packaging a personal agent skill for open-source distribution.
Skip if: Writing a new skill from scratch or distributing an orchestrator that references private agents (refactor to standalone first).
When should I use this skill?
You want to publish, distribute, open-source, or universalize a personal skill.
What you get
A PII-clean, generalized, license-checked skill package ready to commit and distribute.
- PII audit report
- Generalized skill package
- License/attribution headers
By the numbers
- 7-phase pipeline
- 0 PII matches required
- 3-part skill-worthiness gate
Files
Skill: publish-skill
Convert a personal agent skill into a clean, distributable, open-source-ready skill package. This skill walks through a 7-phase pipeline that audits for personally identifiable information, generalizes language and role assumptions, verifies license compatibility, checks cross-platform adapter needs, and prepares the final package for commit.
Communication Rules
- Communicate with the user in their preferred language
- Use English for technical terms (PII, MIT, CC BY, GPL, YAML frontmatter)
- Present audit findings in structured tables
---
Phase 0: Init and Identify Source
Required Inputs
Collect from the user:
1. Source skill path: directory containing the personal skill (e.g., ~/.claude/skills/my-skill/ or ~/.agents/skills/my-skill/) 2. Target package path: directory of the distributable package (e.g., ~/workspace/<your-package>/) 3. Target license: license of the package (default: MIT)
Actions
1. Read SKILL.md from the source skill directory 2. Inventory all files recursively (ls -R) 3. Classify skill type:
- Standalone: self-contained skill with no agent delegation
- Orchestrator: delegates to sub-agents (NOT suitable for distribution without refactoring)
- Wrapper: thin wrapper around another tool/API
4. Present inventory table to the user:
| File | Lines | Type | Notes |
|------|-------|------|-------|Gate: User confirms source skill and target package before proceeding.
---
Phase 0.5: Skill-Worthiness Gate
Before spending effort on PII scrubbing and generalization, confirm the workflow is worth distributing as a skill at all. A skill earns its place by encoding a reusable decision heuristic, a hard-won constraint, or a verification step — not a snippet anyone could reconstruct in five minutes. Apply all three gates; any "no" (or "yes" on the inverse) stops publication in favor of documentation or a memory note instead.
| Gate | Question | Pass condition |
|---|---|---|
| Uniqueness | Could a competent user get the same result by searching the web for ~5 minutes, or by asking a general assistant with no skill installed? | No |
| Specificity | Does it encode a workflow, decision heuristic, constraint, or convention specific to this domain or a recurring task — rather than a generic code snippet or a standard-library example? | Yes |
| Effort | Did discovering it take real debugging, study design, operational effort, or a reviewer-anticipation lesson (a pitfall, a verification step, a domain convention)? | Yes |
Favor skills that encode reviewer-anticipation, reporting-guideline constraints, verification gates, and decision trees over thin wrappers or one-off snippets. This is the publish-time analogue of the "reusable pattern vs one-off hack" distinction: a workflow that fails the gate is better captured as a doc or a memory note than shipped as a skill that dilutes the catalog.
Gate: If any of the three fails, recommend documentation/memory instead and stop. If the value is real but the skill delegates to private agents, route through Phase 1's orchestrator finding (refactor to standalone first). Only a clear three-way pass proceeds.
---
Phase 1: Originality Check
Verify the skill is original work suitable for open-source distribution.
Checks
1. External source: Is this skill adapted from another package or author? Check for attribution headers, license blocks, or "based on" comments. 2. Third-party content: Do any files in references/ come from external sources (published guidelines, textbooks, standards bodies)? 3. Competitive sensitivity: Does the skill reveal proprietary business logic or competitive advantage that should remain private?
Decision Matrix
| Finding | Action |
|---|---|
| Fully original | Proceed to Phase 2 |
| Adapted with compatible license | Add attribution header, proceed |
| Contains non-compatible third-party content | Flag for removal or URL manifest conversion |
| Orchestrator with private agent references | STOP -- requires refactoring to standalone first |
| Competitive/proprietary logic | STOP -- not suitable for open-source |
---
Phase 2: PII De-identification Audit
Zero tolerance: the skill must have exactly 0 PII matches before proceeding.
Pre-scan Setup
Before running, ask the user for everything that should also count as a PII hit but is unique to them:
- Their name(s) in all languages and romanizations (a placeholder shape:
<First Last>|<native-script name>) - Their institutional affiliation(s) (a placeholder shape:
<Institution>|<Hospital>) - Any collaborator surnames that may appear in drafts or filenames
Combine the inputs into a single grep -E alternation pattern (pipe-separated).
Automated Scan
Run the bundled audit script. The first argument is the skill directory; the second is the user-specific alternation pattern from Pre-scan Setup.
bash ${CLAUDE_SKILL_DIR}/scripts/audit_skill.sh <source_skill_path> \
"<First Last>|<native-script name>|<Institution>|<Hospital>"The script runs nine categories that mirror the medsci-skills monorepo linter (scripts/validate_skills.sh):
1. Hardcoded paths (/Users/<name>/, /home/<name>/, ~/Documents, ~/Desktop, ~/Downloads, ~/Projects) 2. Email addresses (any address-shaped string) 3. IP addresses / internal URLs (*.internal, *.local, *.corp) 4. Institutional references (SNUH / AMC / SMC / KAIST / SNU / ASAN / MGH / Mayo Clinic / Johns Hopkins / Samsung Medical / Severance / Asan Medical) 5. Academic roles with names (professor <Surname>, Prof. <Surname>, Dr. <Surname>, PGY[0-9], <한글이름> 교수님) 6. Language hardcoding ("in Korean", "한국어로", "in Japanese", "in Chinese") 7. Location specifics (Seoul / Busan / Daegu / Tokyo / Beijing / Shanghai / Boston / Stanford and Korean variants) 8. Blockquote dated precedent (> YYYY-MM-DD ... lines that reveal an internal review timeline) 9. Author-style filenames (<Surname>{Year}_* pattern, e.g., <Surname>2025_<Journal>_Fig01.png; allow-list excludes generic tokens like Issue2024_, Sample2025_) 10. Binary EXIF metadata (DOCX / PPTX / XLSX / PDF / PNG / JPG / TIFF — scanned via exiftool when installed; skipped silently otherwise with an install hint)
False-positive guard: text scans use grep --binary-files=without-match so byte-stream collisions inside .pyc, .png, or .docx files do not trigger findings. __pycache__/ is also explicitly skipped.
Cross-validation
For categories the script flags, also manually verify with the Grep tool against ${CLAUDE_SKILL_DIR}/references/pii-patterns.md. Pay particular attention to:
- Names not in the EXTRA_PATTERNS argument (e.g., a co-author who appeared only in one early draft)
- Domain-specific institutional acronyms (your institution may not be in the default list)
- Project-specific identifiers like
CK-NN,MA-NN, dated cohort names
Output Format
Present all findings in a remediation table:
| # | File:Line | Category | Match | Suggested Fix |
|---|-----------|----------|-------|---------------|Gate: User reviews all findings. Fix each one. Re-run audit. Proceed only when 0 hits confirmed.
---
Phase 3: Generalization
Transform personal assumptions into universal defaults.
Language
- Replace:
"in Korean"/"한국어로"/"Korean language"→"in the user's preferred language" - Replace:
"communicate in [specific language]"→"Communicate with the user in their preferred language" - Keep: multilingual trigger keywords in the
triggers:field (these aid discovery)
Role
- Replace:
"radiology researcher"→"medical researcher"(if the skill is domain-general) - Replace:
"professor"/"fellow"→"researcher"or"user"(context-dependent) - Keep: domain-specific terms that define the skill's scope (e.g., "diagnostic accuracy" is fine)
Paths
- Replace: hardcoded absolute paths →
${CLAUDE_SKILL_DIR}for bundled reference files - Replace:
~/Documents/...→ user-provided output directory - Keep: relative paths within the skill directory structure
Environment
- Remove: assumptions about specific OS (macOS, Linux)
- Remove: assumptions about specific editors or IDEs
- Remove: references to personal infrastructure (agents, other personal skills)
- Keep: tool requirements listed in frontmatter
tools:field
Interoperability
- Check: does the skill reference other skills by name (e.g., "route to
analyze-stats")? - If referenced skill exists in target package: keep the reference
- If referenced skill does NOT exist in target package: make it optional with fallback instructions
Output
Show a unified diff of all generalization changes for user review.
---
Phase 4: License Compatibility Check
Verify all bundled files are compatible with the target package license.
Scan Process
For each file in the skill's references/ and scripts/ directories:
1. Check for license headers or declarations within the file 2. Check for LICENSE files in the same directory 3. If the file contains content from a known standard (reporting guidelines, clinical scores, etc.), identify the source and its license
Compatibility Matrix
Reference ${CLAUDE_SKILL_DIR}/references/license-compatibility-matrix.md for the full matrix.
Quick reference for MIT target:
| Source License | Can Bundle? | Action |
|---|---|---|
| CC0 / Public Domain | Yes | No changes needed |
| CC BY 4.0 / 3.0 | Yes | Add attribution header |
| MIT / BSD / Apache 2.0 | Yes | Include license notice |
| CC BY-NC | No | Convert to URL reference |
| CC BY-NC-ND | No | Convert to URL reference |
| CC BY-SA | No | Copyleft risk -- convert to URL reference |
| GPL v2/v3 | No | Mark as optional external dependency |
| Unknown / Proprietary | No | Assume incompatible -- remove or get permission |
URL Manifest Pattern
For non-compatible content, convert from bundled file to a URL manifest:
## [Checklist Name]
This checklist is not bundled due to license restrictions ([License Type]).
**Official source**: [URL]
**How to use**: Download the checklist from the official source and place it in
`references/` before using this skill's reporting check feature.Output
Present license audit table:
| File | Source | License | Compatible? | Action |
|------|--------|---------|------------|--------|---
Phase 5: Validate and Test
Structural Validation
1. YAML frontmatter: Parse and verify all required fields (name, description, tools) 2. File references: Every ${CLAUDE_SKILL_DIR}/... path resolves to an actual file 3. Script executability: Scripts in scripts/ have appropriate shebangs 4. Line count: SKILL.md should be under 500 lines for optimal loading 5. Description quality: Description should start with a verb and include trigger keywords
Final PII Re-check
Run audit_skill.sh one final time. Must return exit code 0.
Cross-Platform Adapter Review
Check whether the skill can run in common desktop-agent environments:
| Platform | Check |
|---|---|
| Claude Code | No hardcoded dependency on private ~/.claude paths unless documented. |
| Codex | SKILL.md is self-contained and installable under ~/.agents/skills/. |
| Cursor | A short .cursor/rules/*.mdc adapter can point to the canonical SKILL.md. |
| Windows | Commands avoid Unix-only assumptions or provide PowerShell/Python alternatives. |
| macOS/Linux | Shell examples use portable paths where possible. |
If the package is intended for a workshop or classroom, prepare direct-download ZIPs rather than asking users to navigate GitHub manually:
https://github.com/{owner}/{repo}/releases/latest/download/{package}-classroom-windows.zip
https://github.com/{owner}/{repo}/releases/latest/download/{package}-classroom-macos.zipREADME Entry Draft
Generate a table row matching the target package's README format:
| **{skill-name}** | {One-sentence description of what the skill does.} |User Testing
Instruct the user to:
1. Copy the cleaned skill to a test location: cp -r <cleaned_skill> ~/.claude/skills/<skill-name> 2. Restart Claude Code 3. Test the skill triggers by typing /<skill-name> or relevant trigger phrases 4. Verify all phases work end-to-end on a sample input
Gate: User confirms testing is complete.
---
Phase 6: Package and Commit
Copy to Target Package
cp -r <cleaned_skill_path> <target_package>/skills/<skill-name>/Update README
Apply the README entry drafted in Phase 5:
- Add row to the appropriate table (Available Now / Coming Soon)
- Update pipeline diagram if the skill adds a new stage
- Update skill count if mentioned in prose
Generate Commit Commands
Present the exact commands but do NOT auto-execute push:
cd <target_package>
git add skills/<skill-name>/
git add README.md
git diff --cached # User reviews
git commit -m "Add <skill-name>: <one-line description>"Gate: User reviews git diff --cached and explicitly approves the commit. Push is always manual.
Post-Publish
Remind the user to:
- Update any memory files tracking package status
- Add the skill to any marketplace listings if applicable
- Test installation from a clean clone:
git clone <repo> && cp -r <repo>/skills/<skill-name> ~/.claude/skills/ - For classroom distribution, create or update GitHub Release ZIP assets and test direct download links.
Classroom Package Checklist (if applicable)
- [ ] Full skill set is installed once; lesson tasks use only 1-2 skills at a time
- [ ] Windows ZIP includes
installers/install-windows.cmd - [ ] macOS ZIP includes
installers/install-macos.command - [ ]
README_FIRST.mdexplains unzip -> double-click -> restart -> test prompt - [ ] Email announcement uses direct GitHub Release download links
- [ ] WSL is documented as an advanced option, not a default requirement
- [ ] First prompts avoid full end-to-end orchestration
---
What This Skill Does NOT Do
- Never auto-executes
git push-- push is always manual - Never modifies the source skill in place -- works on a copy or the target directory
- Never makes judgment calls on competitive sensitivity -- always asks the user
- Never bundles content with incompatible licenses -- converts to URL references
- Never assumes a specific package license -- asks in Phase 0
- Never skips the PII audit -- zero tolerance is enforced at Phase 2 and Phase 5
Anti-Hallucination
- Never fabricate file paths, URLs, DOIs, or package names. Verify existence before recommending.
- Never invent journal metadata, impact factors, or submission policies without verification at the journal's website.
- If a tool, package, or resource does not exist or you are unsure, say so explicitly rather than guessing.
License Compatibility Matrix
Reference for determining whether third-party content can be bundled in an open-source skill package. This matrix assumes the target package uses the MIT License as its primary license.
Compatible for Bundling
These licenses allow bundling inside an MIT-licensed package:
| License | Requirements | Notes |
|---|---|---|
| CC0 / Public Domain | None | Ideal for bundled references |
| CC BY 4.0 | Attribution in file header | Most open-access academic content |
| CC BY 3.0 | Attribution in file header | Older open-access content |
| MIT | Include license notice | Standard for code |
| BSD 2-Clause / 3-Clause | Include license notice | Common for scientific libraries |
| Apache 2.0 | Include license + NOTICE file | Patent grant included |
| ISC | Include license notice | Simplified MIT equivalent |
Attribution Template
When bundling CC BY content, add this header to the file:
<!--
Source: [Title] by [Author(s)]
License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
Original: [URL]
Modifications: [describe any changes, or "None"]
-->---
NOT Compatible for Bundling
These licenses conflict with MIT distribution. Convert to URL references.
| License | Conflict | Remediation |
|---|---|---|
| CC BY-NC 4.0 / 3.0 | Non-commercial restriction incompatible with MIT | URL reference only |
| CC BY-NC-ND | Non-commercial + no-derivatives | URL reference only |
| CC BY-ND | No-derivatives prevents modifications | URL reference only |
| CC BY-SA 4.0 | Copyleft: would require entire package to be CC BY-SA | URL reference only |
| GPL v2 / v3 | Copyleft: would require entire package to be GPL | Optional external dependency |
| LGPL | Linking requirements | Optional external dependency |
| Proprietary / All Rights Reserved | No distribution rights | Remove entirely |
| Unknown | Assume incompatible | Remove or contact author |
---
URL Reference Pattern
When content cannot be bundled, replace the file with a reference document:
# [Resource Name]
> Not bundled due to license restrictions ([License Type]).
## Official Source
- **URL**: [direct link to official source]
- **Publisher**: [organization name]
- **License**: [license name and URL]
## How to Use
1. Download the resource from the official source above
2. Place it in this skill's `references/` directory as `[filename]`
3. The skill will automatically detect and use the local copy
## What This Skill Does Without It
When the resource is not locally available, the skill will:
- Use knowledge-based assessment instead of checklist-driven assessment
- Note in its output that the assessment was performed without the official checklist
- Recommend the user obtain the official resource for more thorough analysis---
Optional External Dependency Pattern
For GPL-licensed tools that the skill can use but does not require:
## Optional: [Tool Name]
This skill can optionally use [Tool Name] for [specific feature].
- **License**: GPL v3
- **Install**: `pip install [package]` or `install.packages("[package]")`
- **Without it**: The skill will [describe fallback behavior]---
Common Academic Content Licenses
Quick reference for frequently encountered academic content:
| Content Type | Typical License | Bundleable? |
|---|---|---|
| PubMed abstracts | Public domain (US gov) | Yes |
| Open-access journal articles | CC BY 4.0 | Yes (with attribution) |
| STROBE checklist | CC BY 4.0 | Yes |
| STARD checklist | CC BY 4.0 | Yes |
| PRISMA 2020 checklist | CC BY 4.0 | Yes |
| ARRIVE 2.0 checklist | CC BY 4.0 | Yes |
| TRIPOD+AI checklist | CC BY 4.0 | Yes |
| CONSORT checklist | CC BY-NC 4.0 | No -- URL reference |
| CARE checklist | Exclusive rights (Elsevier) | No -- URL reference |
| SPIRIT checklist | CC BY-NC-ND | No -- URL reference |
| CLAIM checklist | License unverified | No -- URL reference |
R metafor package | GPL v2 | Optional dependency |
Python lifelines | MIT | Yes |
Python python-pptx | MIT | Yes |
Python statsmodels | BSD | Yes |
---
Decision Flowchart
Is the content original (written by you)?
YES → Bundleable under MIT
NO → What is its license?
CC0 / CC BY / MIT / BSD / Apache → Bundle with attribution
CC BY-NC / CC BY-ND / CC BY-SA → URL reference only
GPL / LGPL → Optional external dependency
Unknown / Proprietary → Remove or contact authorPII Detection Patterns
Grep patterns for identifying personally identifiable information in Claude Code skills before open-source publication. All patterns are grep -riE compatible.
How to Use
1. Add your own identifiers to the User-Specific section below before running 2. Run audit_skill.sh <skill_directory> or use these patterns manually with Grep 3. Every match must be resolved before publishing -- zero tolerance
---
Universal Patterns
Hardcoded Paths
/Users/[a-zA-Z]|/home/[a-zA-Z]|~/Documents|~/Desktop|~/Downloads|~/ProjectsRemediation: Replace with ${CLAUDE_SKILL_DIR} for bundled references, or use user-provided paths for output directories.
Email Addresses
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}Remediation: Remove entirely. If an email is needed for API registration, use a placeholder like <your-email>.
IP Addresses and Internal URLs
\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b|https?://[a-z]+\.(internal|local|corp)Remediation: Remove or replace with <your-server>.
---
Domain-Specific Patterns (Medical/Academic)
Institutional References
SNUH|AMC|SMC|KAIST|SNU|ASAN|Mayo Clinic|Johns Hopkins|MGH|CharitRemediation: Replace with "institution" or "medical center", or remove if not essential to the skill logic.
Academic Roles with Names
professor\s+[A-Z][a-z]+|Prof\.\s+[A-Z]|Dr\.\s+[A-Z][a-z]+|PGY[0-9]|R[0-9]-[0-9]Remediation: Replace with "researcher", "user", or "collaborator".
Hospital-Specific Identifiers
IRB[- ]?\d|MRN[- ]?\d|patient[- ]?id|accession[- ]?numberRemediation: Use generic placeholders like <IRB-number>, <patient-id>.
---
Language and Locale
Hardcoded Language Preferences
in Korean|한국어로|Korean language|communicate in Korean|in Japanese|in Chinese|in English(?! for technical)Remediation: Replace with "in the user's preferred language".
Note: Multilingual trigger keywords in the triggers: field are acceptable and encouraged for discoverability.
Location-Specific References
Seoul|Busan|Tokyo|Beijing|서울|부산|울산|창원Remediation: Remove or generalize to region/country level only if essential.
---
User-Specific Patterns
IMPORTANT: Before each audit, add your personal identifiers below.
Names (add yours)
# Example -- replace with your actual names:
# firstname|lastname|이름|성명|romanized_variantsInstitutional Affiliations (add yours)
# Example -- replace with your actual affiliations:
# university_name|hospital_name|lab_name|departmentCollaborator Names (add yours)
# Example -- replace with collaborator names that may appear:
# collaborator1|collaborator2|advisor_name---
False Positive Guidance
The following matches are typically safe to ignore:
| Pattern | Context Where It Is OK |
|---|---|
English | "Use English for technical terms" -- this is a style guide, not locale hardcoding |
Korean | In the triggers: field for discoverability |
professor | Generic role description without a specific name attached |
/Users/ | In documentation explaining installation paths (not in skill logic) |
@ | In email-format examples clearly marked as placeholders |
When in doubt, flag the match for human review rather than auto-dismissing.
#!/usr/bin/env bash
# audit_skill.sh -- PII / hardcoded-path / metadata scanner for a single
# Claude Code skill. Standalone wrapper that mirrors the per-skill checks
# in `medsci-skills/scripts/validate_skills.sh` so a personal skill can be
# audited the same way before being moved into a public repo.
#
# Usage:
# audit_skill.sh <skill_directory> [extra_patterns]
#
# Arguments:
# skill_directory Path to a single skill (must contain SKILL.md).
# extra_patterns Optional `grep -E` alternation pattern of names /
# institutions / collaborator handles to add. Example:
# "jane doe|MIT Medical|@gmail\.com"
#
# Exit codes:
# 0 Clean -- no findings
# 1 Findings detected -- review required before publication
# 2 Usage error
#
# Coverage parity with medsci-skills/scripts/validate_skills.sh:
# rule 6 Personal precedent (text) yes
# rule 7 Absolute path leak yes
# rule 7b Real personal email yes
# rule 7c Author{Year}_ filename pattern yes
# rule 8 Blockquote dated precedent yes
# rule 10 Binary EXIF metadata (DOCX/PDF/PNG) yes (skipped silently if
# exiftool not installed)
#
# False-positive guard: text scans use `grep --binary-files=without-match`
# so compiled `.pyc`, raster `.png` byte-stream collisions, and `git` pack
# files do not count as matches. `__pycache__/` is also explicitly skipped.
set -u
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
cat >&2 <<USAGE
Usage: audit_skill.sh <skill_directory> [extra_patterns]
Examples:
audit_skill.sh ~/.claude/skills/my-skill
audit_skill.sh ./skills/my-skill "jane doe|Stanford|@gmail\.com"
USAGE
exit 2
fi
SKILL_DIR="$1"
EXTRA_PATTERNS="${2:-}"
if [ ! -d "$SKILL_DIR" ]; then
echo "Error: directory not found: $SKILL_DIR" >&2
exit 2
fi
# Resolve to absolute path so the report shows useful locations.
SKILL_DIR="$(cd "$SKILL_DIR" && pwd)"
FOUND=0
TOTAL=0
# Color output only when stdout is a TTY.
if [ -t 1 ]; then
RED=$'\033[0;31m'
GRN=$'\033[0;32m'
YEL=$'\033[1;33m'
NC=$'\033[0m'
else
RED=""; GRN=""; YEL=""; NC=""
fi
# Files to exclude from text scans. Binary scan handles its own subset.
TEXT_EXCLUDES=(
--exclude-dir=.git
--exclude-dir=__pycache__
--exclude-dir=.pytest_cache
--exclude-dir=.mypy_cache
--exclude-dir=node_modules
--exclude=audit_skill.sh
--exclude=pii-patterns.md
--exclude="*.pyc"
)
# ---------------------------------------------------------------------
# Helper: scan a category of grep -E pattern over text files only.
# `--binary-files=without-match` keeps random byte sequences inside .png
# / .pdf / .docx / .pyc from triggering false positives.
# ---------------------------------------------------------------------
scan_text() {
local category="$1"
local pattern="$2"
local whitelist="${3:-}" # optional grep -E pattern; matches removed before counting
local results
results=$(grep -rinE --binary-files=without-match \
"${TEXT_EXCLUDES[@]}" \
"$pattern" "$SKILL_DIR" 2>/dev/null || true)
if [ -n "$whitelist" ] && [ -n "$results" ]; then
results=$(printf '%s\n' "$results" | grep -vE "$whitelist" || true)
fi
if [ -n "$results" ]; then
local count
count=$(printf '%s\n' "$results" | wc -l | tr -d ' ')
TOTAL=$((TOTAL + count))
FOUND=1
echo
echo "${RED}## $category${NC} ($count match(es))"
printf '%s\n' "$results" | sed 's/^/ /'
fi
}
# ---------------------------------------------------------------------
# Helper: filename pattern check. Catches the case where file CONTENT is
# fine but the filename itself reveals authorship (e.g. Nam2025_KJR_Fig01.png).
# Mirrors validate_skills.sh rule 7c.
# ---------------------------------------------------------------------
scan_filenames() {
local category="Author-style filenames (Surname{Year}_)"
local pattern='^[A-Z][a-zA-Z]{2,}[0-9]{4}_'
local allow='^(Issue|Year|Vol|Table|Figure|Sample|Example|Demo|Test|Type|Class|Group|Cohort|Study|Trial|Phase|Run|Batch|Round|Stage|Step|Item|Mode)[0-9]{4}_'
local hits=""
while IFS= read -r -d '' f; do
local base
base=$(basename "$f")
if [[ "$base" =~ $pattern ]] && ! [[ "$base" =~ $allow ]]; then
hits="${hits}${f}"$'\n'
fi
done < <(find "$SKILL_DIR" -type f \
-not -path '*/.git/*' \
-not -path '*/__pycache__/*' \
-print0 2>/dev/null)
if [ -n "$hits" ]; then
local count
count=$(printf '%s' "$hits" | grep -c '^' || true)
TOTAL=$((TOTAL + count))
FOUND=1
echo
echo "${RED}## $category${NC} ($count match(es))"
printf '%s' "$hits" | sed 's/^/ /'
fi
}
# ---------------------------------------------------------------------
# Helper: optional EXIF scan via exiftool. Skipped silently if exiftool is
# not installed (publish-skill users are not expected to have it). When
# present, mirrors validate_skills.sh rule 10.
# ---------------------------------------------------------------------
scan_exif() {
if ! command -v exiftool >/dev/null 2>&1; then
echo "${YEL}## Binary EXIF metadata${NC} skipped (exiftool not installed)"
echo " Install: brew install exiftool # macOS"
echo " sudo apt-get install -y libimage-exiftool-perl # Ubuntu"
return 0
fi
local binary_files=()
while IFS= read -r -d '' f; do
binary_files+=("$f")
done < <(find "$SKILL_DIR" -type f \
\( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" \
-o -iname "*.tif" -o -iname "*.tiff" \
-o -iname "*.pdf" -o -iname "*.docx" -o -iname "*.pptx" -o -iname "*.xlsx" \) \
-print0 2>/dev/null)
if [ ${#binary_files[@]} -eq 0 ]; then
return 0
fi
local pii_pattern='/Users/[a-zA-Z]|/home/[a-zA-Z]'
if [ -n "$EXTRA_PATTERNS" ]; then
pii_pattern="${pii_pattern}|${EXTRA_PATTERNS}"
fi
local exif_dump
exif_dump=$(exiftool -S \
-Author -Creator -LastModifiedBy -LastSavedBy -Copyright -Artist \
-Owner -OwnerName -CompanyName -Manager -HostComputer -UserComment \
-Subject -Title -Description -Keywords -Comment \
-Producer -CreatorTool -Software \
"${binary_files[@]}" 2>/dev/null || true)
# exiftool only prints `======== <file>` headers when given multiple files.
# Pre-prime current_file so single-file mode still attributes hits correctly.
local current_file="${binary_files[0]}"
local hits=""
while IFS= read -r line; do
if [[ "$line" == ========\ * ]]; then
current_file="${line#======== }"
continue
fi
[ -z "$line" ] && continue
[ -z "$current_file" ] && continue
if echo "$line" | grep -qE "$pii_pattern"; then
hits="${hits}${current_file}: ${line}"$'\n'
fi
done <<< "$exif_dump"
if [ -n "$hits" ]; then
local count
count=$(printf '%s' "$hits" | grep -c '^' || true)
TOTAL=$((TOTAL + count))
FOUND=1
echo
echo "${RED}## Binary EXIF metadata${NC} ($count match(es))"
printf '%s' "$hits" | sed 's/^/ /'
fi
}
echo "=========================================="
echo "PII Audit: $SKILL_DIR"
echo "=========================================="
# --- Universal text categories -----------------------------------------
# rule 7: hardcoded user-home / project paths.
scan_text "Hardcoded Paths" \
'/Users/[a-zA-Z]|/home/[a-zA-Z]|~/Documents|~/Desktop|~/Downloads|~/Projects'
# rule 7b: real personal email addresses. The whitelist matches common
# placeholder / example / RFC-reserved domains so the script does not
# flag legitimate documentation samples.
EMAIL_WHITELIST='example\.com|example\.org|example\.net|your@email|user@host|noreply@|placeholder|<your-email>|<email>'
scan_text "Email Addresses" \
'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' \
"$EMAIL_WHITELIST"
# Internal infrastructure leakage.
scan_text "IP Addresses / Internal URLs" \
'\b[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\b|https?://[a-z0-9-]+\.(internal|local|corp)'
# rule 6: institutional references. `\b` word boundaries replace the
# `(?<!...)` lookbehind that the original grep -E silently failed on.
scan_text "Institutional References" \
'\b(SNUH|AMC|SMC|KAIST|SNU|ASAN|MGH|UCSF)\b|Mayo Clinic|Johns Hopkins|Samsung Medical|Severance|Asan Medical'
# rule 6 cont.: titled academic roles with adjacent surname.
scan_text "Academic Roles with Names" \
'professor [A-Z][a-z]+|Prof\. [A-Z]|Dr\. [A-Z][a-z]+|PGY[0-9]|[가-힣]{2,4}[[:space:]]*(교수님|선생님|박사님)'
# Language-default hardcoding.
scan_text "Language Hardcoding" \
'in Korean|한국어로|Korean language|communicate in Korean|in Japanese|in Chinese'
# Frequent-collision city names.
scan_text "Location Specifics" \
'\b(Seoul|Busan|Daegu|Tokyo|Beijing|Shanghai|Boston|Stanford)\b|서울|부산|울산|창원|대구|대전'
# rule 8: dated precedent inside blockquote (`> 2026-04-26 ...`).
# Allow-list: meta headers like "Last updated:", "Created:", "Updated:",
# "Date:" — these are routine version stamps, not internal review timeline.
scan_text "Blockquote Dated Precedent" \
'^>.*20[2-9][0-9]-[0-1][0-9]-[0-3][0-9]' \
'> *(Last updated|Created|Updated|Date|Version|Released):'
# rule 7c filename pattern.
scan_filenames
# rule 10 EXIF scan (optional).
scan_exif
# --- User-supplied identifiers ----------------------------------------
if [ -n "$EXTRA_PATTERNS" ]; then
scan_text "User-Specified Patterns" "$EXTRA_PATTERNS"
fi
# --- Summary -----------------------------------------------------------
echo
echo "=========================================="
if [ "$FOUND" -eq 0 ]; then
echo "${GRN}RESULT: CLEAN (0 findings)${NC}"
echo "=========================================="
exit 0
else
echo "${RED}RESULT: $TOTAL FINDING(S) DETECTED${NC}"
echo "Review and fix all findings before publishing."
echo "=========================================="
exit 1
fi
schema_version: 2
name: publish-skill
layer: B
owner_domain: skill_publishing
maturity: official
when_to_use: "Convert a personal agent skill into a distributable, OSS-ready skill: PII audit, generalization, license check, cross-platform review, packaging."
when_NOT_to_use: "Authoring a brand-new skill from scratch (out of scope; this hardens an existing one)."
inputs:
- "a personal skill directory"
outputs:
- "PII audit report"
- "generalized OSS-ready skill"
deterministic_scripts:
- scripts/audit_skill.sh
side_effects:
- writes_skill_artifacts
downstream_consumers:
- none
forbidden_actions:
- publish_skill_with_unresolved_pii
- weaken_the_pii_blocklist_to_pass
# v2.1 quality card
purpose: "Harden a personal skill for open-source release through a PII audit, generalization, and license/portability checks."
safety_boundaries:
- "Publication is gated on a passing PII audit; the blocklist is conservative and not weakened to pass."
- "Personal paths, names, and document metadata are scrubbed before release."
known_limitations:
- "The audit catches known PII patterns; novel identifiers still need human review."
- "Generalization preserves behavior but a maintainer should re-read the result."
validation_commands:
- "bash scripts/audit_skill.sh <skill-dir>"
- "bash scripts/validate_skills.sh"
evidence_surface: bundled_script
Related skills
FAQ
What is the skill-worthiness gate?
A three-part uniqueness/specificity/effort check; any failure stops publication in favor of a doc or memory note instead of diluting the catalog.
How strict is the PII audit?
Zero tolerance: the skill must have exactly 0 PII matches before proceeding, scanned with a user-specific alternation pattern.