Communityライティング&編集github.com

tzwkb/repo-maintainer

Agent Skill — GitHub repository cleanup, consolidation & maintenance.

対応~Claude Code~Codex CLI~Cursor
npx skills add tzwkb/repo-maintainer

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

ドキュメント

Repository Maintainer

Systematic workflow for transforming fragmented GitHub profiles into clean, maintainable, professional repositories.

Overview

Execute cleanup in 6 phases. Skip phases not applicable. Always get user confirmation on destructive actions (deletion, renaming, merging).

Core Principles (Non-Negotiable)

Every repository — new or old, active or archived — must have these four foundation documents. Do not consider a cleanup complete until all four are verified.

DocumentRuleWhy
LICENSEEvery public repo gets a LICENSE. Default to MIT unless user specifies otherwise.Without a license, code is "all rights reserved" by default. Nobody can legally use it.
README.mdMust exist with at least: project name, one-sentence description, and usage/install instructions if applicable.The first thing every visitor sees. An empty or missing README signals abandonment.
.gitignoreMust match the primary language (Python/PHP/Node/etc.). Never commit build artifacts, cache, env files, or IDE configs.Prevents accidental leakage of secrets and keeps diffs readable.
GitHub DescriptionThe description field must never be empty. One sentence, plain English (or matching repo language).Appears in search results, profile pins, and social cards.

If a repo is missing any of the four, fixing it takes priority over merging, renaming, or refactoring.

Phase 1: Scan & Diagnose

Fetch complete repo list via GitHub API:

curl -sH "Authorization: Bearer TOKEN" \
  "https://api.github.com/user/repos?per_page=100&affiliation=owner"

Check for each repo:

  • Missing LICENSE, .gitignore, README.md
  • Empty description field
  • Naming inconsistencies (mixed case, overly long names)
  • Obvious duplicates or series (e.g., project, project-v2, project-mineru)
  • Names containing outdated, deprecated, old, test

Phase 2: Standardize Foundation Files

For every active repo, ensure minimum file set exists:

FileRule
LICENSEDefault to MIT unless user specifies otherwise
.gitignoreMatch primary language (Python/PHP/Node)
README.mdMust exist; rewrite if only placeholder
GitHub DescriptionOne-sentence summary via API

Bulk update via GitHub Contents API (PUT /repos/{owner}/{repo}/contents/{path}).

Critical: PowerShell here-strings corrupt triple-backtick code blocks in Markdown. Use Python scripts for bulk README generation to avoid ```` escaping failures.

Phase 3: Handle Outdated Repos

Decision tree:

ConditionAction
Name contains outdated / deprecated / oldArchive or delete
Superseded by newer repoArchive + README successor link
Public repo history contains leaked secrets (API keys)Delete (archive leaves history intact)
Private repo contains keys, passwords, private config, or personal dataUser may ignore leakage rules; treat as acceptable private state unless they ask to sanitize
Useful as reference but unmaintainedArchive

Archived repos are read-only. To modify an archived repo (rename, update README), unarchive first (PATCH {"archived": false}), make changes, then re-archive.

Phase 4: Merge Related Repositories

When 2+ repos are variants of the same system (different backends, engines, adapters):

Option A — Monorepo (Recommended) Create a new clean repo (no history). Place each variant in engines/{name}/ subdirectory.

project-suite/
├── main.py              # default engine
├── engines/
│   ├── babeldoc/
│   └── mineru/
└── README.md

Preserve each engine's entry point and config. Update root README to explain switching.

Option B — Git Submodule Keep repos independent. Add submodules in parent. Use when history preservation matters more than simplicity.

Option C — Keep Independent + Link Do not merge. Add cross-links in READMEs. Use when variants have diverged significantly.

If history contains secrets: Always create a new clean repo and copy current snapshot (via zip download + re-commit). Do not use git merge or git submodule as history leaks will persist.

Phase 5: Rename Repositories

Patch API: PATCH /repos/{owner}/{repo} with {"name": "new-name"}.

Guidelines:

  • All lowercase, hyphens only
  • Max 3-4 words
  • Remove redundant words (based-on, design-and-development)
  • Add outdated suffix to archived repos if not already present

GitHub automatically redirects old URLs for a period, but bookmarks and scripts may break. Confirm with user before renaming starred/forked repos.

Phase 6: Code-Level Refactoring

After remote structure is clean, refactor code inside repos:

  1. Unified terminology — Replace hardcoded brand names with generic terms (GPTProcessorLLMProcessor) while preserving actual API model IDs (gpt-4, gpt-5).
  2. File renaming — GitHub Contents API has no rename; create new file with updated content, delete old file.
  3. Directory reorganization — Use engines/, variants/, or core/ + adapters/ patterns for multi-flavor projects.

Pushing Large Local Refactors to Remote

When the user downloads code locally, makes heavy changes (file renames, variable renames, structural rewrites), and needs to push back to the remote repo.

Pre-Push Secret Scan (MANDATORY)

推送前必须先扫描 secret 泄露,有任何发现立即报告,禁止继续 push。

Private repo exception: 如果目标仓库确认是 private,可以无视 key、密码、私有配置、个人信息以及其他敏感信息泄露规则。扫描可以作为提醒,但命中不阻断 push、不要求删库重建、不要求替换为环境变量,除非用户明确要求清理或计划改成 public。

扫描模式(两道都要跑——只跑 token 正则会漏掉明文密码):

① 高熵 token / API key:

grep -rn "sk-[A-Za-z0-9]\{20,\}\|ghp_[A-Za-z0-9]\{36\}\|ghu_\|github_pat_\|Bearer [A-Za-z0-9+/=]\{40,\}" \
  --include="*.py" --include="*.js" --include="*.ts" --include="*.yaml" \
  --include="*.env" --include="*.json" --include="*.bat" --include="*.sh" \
  --exclude-dir=".git" .

② 明文密码 / 凭证 helper(token 正则抓不到;实战曾漏掉 askpass.sh 里一行 echo "<sudo密码>",随 init commit 推上了 GitHub):

git ls-files -z | xargs -0 grep -nIE "password|passwd|密码|SUDO_ASKPASS|sshpass|^[[:space:]]*echo[[:space:]]+[\"'][^\"']{6,}[\"']" 2>/dev/null \
  | grep -vE "\.md:|getenv|getpass|placeholder|example"
# 并人工核对孤立的 askpass.sh / *_secret* / .pgpass / .npmrc 类文件——它们整文件就是凭证

Public repo 任一道命中即按下方强制流程处理。Private repo 命中只需提醒用户,不作为阻断条件。注意扫的是 git ls-files(已跟踪集),.gitignore 挡掉的不算泄露,但已 commit 的文件即使现在 gitignore 也仍在历史里

报告格式:

[LEAK] scripts/eval.py:15 — sk-xxxx (硬编码 fallback)
[LEAK] ui/ui_backend.py:33 — sk-xxxx (hardcoded default)

发现泄露后强制流程:

  1. 立即告知用户哪个文件哪一行
  2. 替换为环境变量(os.getenv("API_KEY") 无默认值)
  3. 如已 push 到 remote:告知用户,获得明确确认后删库重建(git history 无法通过 force push 彻底清除)
  4. 用户撤销对应服务商的 key

Pre-Push Checklist

推送前必须执行:

git status

确认无以下情况:

  • Untracked files — 未跟踪的新文件(常见遗漏:docs/, scripts/, config 模板)
  • Changes not staged — 修改未 add
  • Changes to be committed 中包含不应上传的文件(密钥、个人数据、临时脚本)

未确认前,禁止直接 push。

Decision Tree

SituationRecommended Action
Local repo has no .git (downloaded zip / copied files)See Scenario A below
Local is git clone with history, remote unchangedNormal git add → commit → push
Local is git clone, remote has new commitsgit pull --rebase or merge, resolve conflicts, push
Local is git clone, changes are massive renames + rewritesSee Scenario B below
Want to preserve remote history but replace all codeSee Scenario C below

Scenario A: Local Has No Git History

Most common when user downloads zip, edits extensively, then wants to sync back.

Option A1 — Force Push (Personal / Solo Projects)

# In local project folder
git init
git add .
git commit -m "refactor: major rewrite"
git remote add origin https://github.com/OWNER/REPO.git
git branch -M main
git push -u origin main --force

⚠️ Destroys remote history. Only use when user confirms remote history is disposable.

Option A2 — Preserve Remote History (Recommended for Valuable Repos)

git clone https://github.com/OWNER/REPO.git temp-repo
cp -r temp-repo/.git ./your-local-project/
cd your-local-project
git add .
git commit -m "refactor: major rewrite"
git push

This keeps all prior commits and appends the refactor as a single new commit.

Option A3 — New Clean Repo If the rewrite is so radical it is essentially a new project:

  1. Create new repo on GitHub (must include description in the create body or PATCH immediately after)
  2. Push local code there
  3. Archive old repo with successor link in README

Scenario B: Massive Renames + Rewrites in a Cloned Repo

Critical rule: never mix file renames and content changes in the same commit. Git will treat it as "delete old file + create new file" and lose history linkage.

Step-by-step commit strategy:

# Step 1: Rename only
git mv old_module.py new_module.py
git mv old_utils/ new_utils/
git commit -m "refactor: rename modules"

# Step 2: Variable/class renames
git add .
git commit -m "refactor: rename variables and classes"

# Step 3: Logic changes
git add .
git commit -m "feat: update core logic"

If user already mixed everything in working tree:

# Stash current changes, replay in stages
git stash push -m "mixed refactor"

# Restore renames only (use git mv or manual mv + git add)
git mv old_file new_file
git commit -m "refactor: rename files"

# Restore content changes
git stash pop
# Resolve conflicts if any, then commit

Scenario C: Replace All Code but Keep History

Useful when user wants to start fresh code but keep stars/issues/URL.

git checkout --orphan fresh-start
git rm -rf .
# Copy new local code here
git add .
git commit -m "init: clean rewrite"
git branch -D main
git branch -m main
git push -u origin main --force

Checking Rename Detection

After pushing, verify Git correctly tracked renames:

git log --follow -- new_file.py

If history is broken (shows file as brand new), the rename and content change were committed together.

Security & Compliance

  • GitHub token: 需要 PAT 时在 agent 记忆里找(不在此写具体路径/账号/值)。绝不把 token 值写进任何会被 push 的文件(含本 skill、README、配置)——含 token 的内容一旦 push 即被 GitHub secret-scanning 吊销。
  • Token hygiene: After bulk operations, instruct user to revoke the PAT immediately at https://github.com/settings/tokens.
  • Private repo exception: If a repo is private, the agent may ignore key leaks, passwords, private config, personal data, and other sensitive-info leakage rules. Do not block push, force sanitization, or recommend deletion solely because private history contains secrets unless the user asks or the repo may become public.
  • Public secret history: If a public repo ever committed API keys, deleting the repo is the only way to purge history from GitHub. Force-pushing sanitized history does not guarantee removal from GitHub's backup systems.
  • Archived public repos with secrets: If deletion is unacceptable, at minimum remove the repo from public visibility (make private) or delete and recreate empty with clean code.

Technical Reference

Encoding Traps (Windows)

PowerShell defaults to CP1252 for stdout, destroying Chinese characters and Markdown code blocks. Always reconfigure:

import sys
sys.stdout.reconfigure(encoding='utf-8')

When using Invoke-RestMethod in PowerShell with JSON bodies containing backticks or triple backticks, prefer Python scripts for content generation to avoid JSON serialization mangling ``` into `.

GitHub API Patterns

Create repo (never omit description):

api_call("POST", "/user/repos", {
    "name": "repo-name",
    "private": True,
    "description": "One-sentence summary."
})

Create/update file:

import base64, json, urllib.request

payload = {
    "message": "commit msg",
    "content": base64.b64encode(content.encode('utf-8')).decode(),
    "sha": existing_sha  # required for updates
}
req = urllib.request.Request(
    f"https://api.github.com/repos/{owner}/{repo}/contents/{path}",
    method="PUT",
    data=json.dumps(payload).encode(),
    headers={"Authorization": f"Bearer {token}", ...}
)

Archive/unarchive:

api_call("PATCH", f"/repos/{owner}/{repo}", {"archived": True})

Rename repo:

api_call("PATCH", f"/repos/{owner}/{repo}", {"name": "new-name"})

Delete repo:

api_call("DELETE", f"/repos/{owner}/{repo}")

Bulk Download Without Clone

Download repo snapshot as zip to avoid slow git clone over unstable networks:

curl -sL "https://github.com/{owner}/{repo}/archive/refs/heads/main.zip" -o {repo}.zip

Bundled Resources

  • scripts/repo_analyzer.py — Scan user's GitHub repos and generate cleanup recommendations
  • scripts/bulk_file_updater.py — Batch create/update LICENSE, .gitignore, README via GitHub API

関連スキル

steipete/notion

Notion CLI/API for pages, Markdown content, data sources, files, comments, search, Workers, and raw API calls.

community

affaan-m/seo

Audit, plan, and implement SEO improvements across technical SEO, on-page optimization, structured data, Core Web Vitals, and content strategy. Use when the user wants better search visibility, SEO remediation, schema markup, sitemap/robots work, or keyword mapping.

community

affaan-m/brand-voice

Build a source-derived writing style profile from real posts, essays, launch notes, docs, or site copy, then reuse that profile across content, outreach, and social workflows. Use when the user wants voice consistency without generic AI writing tropes.

community

affaan-m/crosspost

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

community

affaan-m/x-api

X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically.

community

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

community