Agent-almanac audit-discovery-symlinks
install
source · Clone the upstream repo
git clone https://github.com/pjt222/agent-almanac
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/wenyan/skills/audit-discovery-symlinks" ~/.claude/skills/pjt222-agent-almanac-audit-discovery-symlinks-29d137 && rm -rf "$T"
manifest:
i18n/wenyan/skills/audit-discovery-symlinks/SKILL.mdsource content
audit-discovery-symlinks
用時
- 新增技能、代理、團隊於 almanac 後
- 庫易名或遷位,絕對符連或已斷
- Claude Code 不見斜杠命令或代理
- 定期察其康,覺察註冊與發現之徑之漂
- 新項目初立,欲發現共用之 almanac 內容
勿用於始建符連樞紐之從無至有。見 symlink-architecture guide 以初立之。
入
| 參 | 類 | 必 | 述 |
|---|---|---|---|
| string | 否 | agent-almanac 之絕對徑。若略則由 之符連標的或 cwd 自辨 |
| enum | 否 | 、、或 (默 ) |
| enum | 否 | (默:唯察)、(盡修諸安之疵)、(修前逐問) |
法
第一步:識 almanac 之徑
尋 agent-almanac 之根目錄。
# Auto-detect from current project's .claude/agents symlink ALMANAC_PATH=$(readlink -f .claude/agents 2>/dev/null | sed 's|/agents$||') # Fallback: check if cwd is the almanac if [ -z "$ALMANAC_PATH" ] || [ ! -f "$ALMANAC_PATH/skills/_registry.yml" ]; then if [ -f "skills/_registry.yml" ]; then ALMANAC_PATH=$(pwd) fi fi # Fallback: check global agents symlink if [ -z "$ALMANAC_PATH" ] || [ ! -f "$ALMANAC_PATH/skills/_registry.yml" ]; then ALMANAC_PATH=$(readlink -f ~/.claude/agents 2>/dev/null | sed 's|/agents$||') fi echo "Almanac path: $ALMANAC_PATH"
得:
ALMANAC_PATH 指一目錄,含 skills/_registry.yml、agents/_registry.yml、teams/_registry.yml。
敗則: 若自辨失敗,問用者以
almanac_path。almanac 根乃含 skills/、agents/、teams/ 及其註冊之目錄。
第二步:列註冊之目
自註冊抽其正典之技能、代理、團隊之列。
# Count registered skills (entries with "- id:" under domain sections) REGISTERED_SKILLS=$(grep '^ \{6\}- id:' "$ALMANAC_PATH/skills/_registry.yml" | awk '{print $3}' | sort) REGISTERED_SKILL_COUNT=$(echo "$REGISTERED_SKILLS" | wc -l) # Count registered agents REGISTERED_AGENTS=$(grep '^ \{2\}- id:' "$ALMANAC_PATH/agents/_registry.yml" | awk '{print $3}' | sort) REGISTERED_AGENT_COUNT=$(echo "$REGISTERED_AGENTS" | wc -l) # Count registered teams REGISTERED_TEAMS=$(grep '^ \{2\}- id:' "$ALMANAC_PATH/teams/_registry.yml" | awk '{print $3}' | sort) REGISTERED_TEAM_COUNT=$(echo "$REGISTERED_TEAMS" | wc -l) echo "Registered: $REGISTERED_SKILL_COUNT skills, $REGISTERED_AGENT_COUNT agents, $REGISTERED_TEAM_COUNT teams"
得: 數與各註冊頭之
total_skills、total_agents、total_teams 相符。
敗則: 若數與頭之總不合,註冊自身不齊。記其異於報告而仍以實際之
- id: 條為準。
第三步:察項目之符連
察當前項目目錄之
.claude/skills/*、.claude/agents、.claude/teams。
PROJECT_CLAUDE=".claude" # --- Skills --- # Items on disk (excluding _template) PROJECT_SKILLS=$(ls "$PROJECT_CLAUDE/skills/" 2>/dev/null | grep -v '^_template$' | sort) PROJECT_SKILL_COUNT=$(echo "$PROJECT_SKILLS" | grep -c .) # Missing: in registry but not in project .claude/skills/ MISSING_PROJECT_SKILLS=$(comm -23 <(echo "$REGISTERED_SKILLS") <(echo "$PROJECT_SKILLS")) # Broken: symlink exists but target doesn't resolve BROKEN_PROJECT_SKILLS=$(find "$PROJECT_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -printf '%f\n' 2>/dev/null | sort) # Extraneous: in project but not in registry (and not external) EXTRA_PROJECT_SKILLS=$(comm -13 <(echo "$REGISTERED_SKILLS") <(echo "$PROJECT_SKILLS")) # --- Agents --- if [ -L "$PROJECT_CLAUDE/agents" ] || [ -d "$PROJECT_CLAUDE/agents" ]; then PROJECT_AGENT_STATUS="OK" test -d "$PROJECT_CLAUDE/agents" || PROJECT_AGENT_STATUS="BROKEN" PROJECT_AGENT_COUNT=$(ls "$PROJECT_CLAUDE/agents/"*.md 2>/dev/null | wc -l) else PROJECT_AGENT_STATUS="MISSING" PROJECT_AGENT_COUNT=0 fi # --- Teams --- # Teams are NOT symlinked. TeamCreate uses ~/.claude/teams/ for runtime state. # A .claude/teams symlink is a misconfiguration — warn if found. if [ -L "$PROJECT_CLAUDE/teams" ]; then PROJECT_TEAM_STATUS="MISCONFIGURED" PROJECT_TEAM_COUNT=0 # Stale symlink — should be removed to avoid collision with TeamCreate else PROJECT_TEAM_STATUS="OK" PROJECT_TEAM_COUNT=0 fi
得: 無闕、無斷。冗餘者皆已分類而釋。
敗則: 若
.claude/ 全無,項目無發現之設。記其事而跳至全域之審。
第四步:察全域之符連
察
~/.claude/skills/* 與 ~/.claude/agents。亦察 ~/.claude/teams 非符連(宜闕,或為 TeamCreate 運行狀態之目錄)。
GLOBAL_CLAUDE="$HOME/.claude" # --- Skills --- GLOBAL_SKILLS_ALL=$(ls "$GLOBAL_CLAUDE/skills/" 2>/dev/null | sort) # Classify each entry: almanac vs external ALMANAC_GLOBAL_SKILLS="" EXTERNAL_GLOBAL_SKILLS="" for item in $GLOBAL_SKILLS_ALL; do target=$(readlink -f "$GLOBAL_CLAUDE/skills/$item" 2>/dev/null) if [ -z "$target" ]; then # Real directory (not a symlink) — external EXTERNAL_GLOBAL_SKILLS="$EXTERNAL_GLOBAL_SKILLS $item" elif echo "$target" | grep -q "^$ALMANAC_PATH"; then ALMANAC_GLOBAL_SKILLS="$ALMANAC_GLOBAL_SKILLS $item" else EXTERNAL_GLOBAL_SKILLS="$EXTERNAL_GLOBAL_SKILLS $item" fi done # Filter: _template is always extraneous for almanac content ALMANAC_GLOBAL_SKILLS=$(echo "$ALMANAC_GLOBAL_SKILLS" | tr ' ' '\n' | grep -v '^_template$' | grep -v '^$' | sort) # Missing: in registry but not in global almanac skills MISSING_GLOBAL_SKILLS=$(comm -23 <(echo "$REGISTERED_SKILLS") <(echo "$ALMANAC_GLOBAL_SKILLS")) # Broken: symlink exists but target doesn't resolve BROKEN_GLOBAL_SKILLS=$(find "$GLOBAL_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -printf '%f\n' 2>/dev/null | sort) # Stale almanac entries: in global almanac set but not in registry STALE_GLOBAL_SKILLS=$(comm -13 <(echo "$REGISTERED_SKILLS") <(echo "$ALMANAC_GLOBAL_SKILLS")) # --- Agents --- if [ -L "$GLOBAL_CLAUDE/agents" ] || [ -d "$GLOBAL_CLAUDE/agents" ]; then GLOBAL_AGENT_STATUS="OK" test -d "$GLOBAL_CLAUDE/agents" || GLOBAL_AGENT_STATUS="BROKEN" GLOBAL_AGENT_COUNT=$(ls "$GLOBAL_CLAUDE/agents/"*.md 2>/dev/null | wc -l) else GLOBAL_AGENT_STATUS="MISSING" GLOBAL_AGENT_COUNT=0 fi # --- Teams --- # Teams are NOT symlinked. TeamCreate uses ~/.claude/teams/ for runtime state. # A ~/.claude/teams symlink is a misconfiguration — warn if found. if [ -L "$GLOBAL_CLAUDE/teams" ]; then GLOBAL_TEAM_STATUS="MISCONFIGURED" GLOBAL_TEAM_COUNT=0 # Stale symlink — should be removed to avoid collision with TeamCreate else GLOBAL_TEAM_STATUS="OK" GLOBAL_TEAM_COUNT=0 fi
得: almanac 之技能無闕、無斷。外來之內容(peon-ping 等)列而不標為錯。
敗則: 若
~/.claude/ 無之,全域之樞紐未立。見 symlink-architecture guide 以初設之。
第五步:生審報告
生兼兩層之概表。
# Discovery Symlink Audit Report **Date**: YYYY-MM-DD **Almanac**: <almanac_path> **Scope**: both | project | global ## Summary | Content | Registered | Project | Global (almanac) | Global (external) | |---------|------------|---------|-------------------|-------------------| | Skills | N | N | N | N | | Agents | N | STATUS | STATUS | — | | Teams | N | STATUS | STATUS | — | ## Issues ### Missing (registered but no symlink) - Project skills: [list or "none"] - Global skills: [list or "none"] ### Broken (symlink exists, target gone) - Project: [list or "none"] - Global: [list or "none"] ### Extraneous - Stale almanac (in discovery but not registry): [list or "none"] - _template in discovery path: [yes/no] - External content (non-almanac): [list — informational only]
得: 報告清而可行。無疵則康矣。
敗則: 若報告自身生之失敗,以原始之數與列示於終端以代之。
第六步:修(可選)
若
fix_mode 為 auto 或 interactive,修所察之疵。
6a. 建闕之項目符連:
for skill in $MISSING_PROJECT_SKILLS; do ln -s "../../skills/$skill" "$PROJECT_CLAUDE/skills/$skill" done
6b. 建闕之全域符連:
for skill in $MISSING_GLOBAL_SKILLS; do ln -s "$ALMANAC_PATH/skills/$skill" "$GLOBAL_CLAUDE/skills/$skill" done
6c. 除斷之符連:
# Project for broken in $BROKEN_PROJECT_SKILLS; do rm "$PROJECT_CLAUDE/skills/$broken" done # Global for broken in $BROKEN_GLOBAL_SKILLS; do rm "$GLOBAL_CLAUDE/skills/$broken" done
6d. 除陳之 almanac 項:
# Only remove items that target the almanac path but aren't in the registry for stale in $STALE_GLOBAL_SKILLS; do rm "$GLOBAL_CLAUDE/skills/$stale" done # Remove _template if present rm -f "$GLOBAL_CLAUDE/skills/_template" rm -f "$PROJECT_CLAUDE/skills/_template"
6e. 修闕之目錄符連(agents/teams):
# Project agents if [ "$PROJECT_AGENT_STATUS" = "MISSING" ]; then ln -s ../agents "$PROJECT_CLAUDE/agents" fi # Project teams if [ "$PROJECT_TEAM_STATUS" = "MISSING" ]; then ln -s ../teams "$PROJECT_CLAUDE/teams" fi # Global agents if [ "$GLOBAL_AGENT_STATUS" = "MISSING" ]; then ln -s "$ALMANAC_PATH/agents" "$GLOBAL_CLAUDE/agents" fi # Global teams if [ "$GLOBAL_TEAM_STATUS" = "MISSING" ]; then ln -sf "$ALMANAC_PATH/teams" "$GLOBAL_CLAUDE/teams" fi
切記: 勿除分類為外來者。此屬他項目(如 peon-ping),宜存。
得: 諸闕符連已建,諸斷符連已除,諸陳 almanac 項已清。外來之內容未動。
敗則: 若
ln -s 因標的已存文件/目錄(如空目錄代符連)而敗,先以 rmdir(空目錄)除之,或標為人手察(非空目錄)。
第七步:驗
重行第三至四步之審以確修之。
echo "=== Post-repair verification ===" echo "Project skills: $(ls "$PROJECT_CLAUDE/skills/" 2>/dev/null | grep -v '^_template$' | wc -l)" echo "Global skills (almanac): $(echo "$ALMANAC_GLOBAL_SKILLS" | wc -w)" echo "Broken project: $(find "$PROJECT_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -print 2>/dev/null | wc -l)" echo "Broken global: $(find "$GLOBAL_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -print 2>/dev/null | wc -l)" echo "Project agents: $PROJECT_AGENT_STATUS ($PROJECT_AGENT_COUNT .md files)" echo "Global agents: $GLOBAL_AGENT_STATUS ($GLOBAL_AGENT_COUNT .md files)" echo "Project teams: $PROJECT_TEAM_STATUS ($PROJECT_TEAM_COUNT .md files)" echo "Global teams: $GLOBAL_TEAM_STATUS ($GLOBAL_TEAM_COUNT .md files)"
得: 無闕、無斷。數與註冊之總相符(於 almanac 內容)。外來內容另列之。
敗則: 若修之後仍有疵,記其敗之具體者。常見之因:
~/.claude/ 之權、/mnt/ 之 NTFS 徑之長限、非空目錄阻符連之建。
驗
- almanac 之徑已正辨,含三註冊
- 註冊之數與
頭之值相符(或記其異)total_* - 項目層之技能、代理、團隊皆已審
- 全域層之技能、代理、團隊皆已審
- 外來內容(非 almanac)已識而出疵計之外
-
標為冗餘(永不屬發現之徑)_template - 審報告已生,數明而列可行
- 若
為fix_mode
:諸安修皆施,外來內容未動auto - 修後之驗確無闕、無斷
陷
-
混外來與闕之 almanac:
或含他項目之技能(如 peon-ping)。必先察符連之標的是否於 almanac 徑下,再判其陳或冗~/.claude/skills/ -
除外來內容:勿刪非指 almanac 者。其屬他項目,有意而為
-
符連
目錄:模板乃腳手架,非可用之內容。_template
永不宜於_template
或.claude/skills/
。批量同步之腳本必明略之.claude/agents/ -
陳之
符連:.claude/teams
指團隊定義之符連為誤配。Claude Code 之.claude/teams
用TeamCreate
為運行狀態(config.json、inbox)。若此徑為指 almanac 之~/.claude/teams/
之符連,運行物將書入 git 所追之庫。項目或全域之teams/
符連皆宜除.claude/teams -
相對與絕對之徑:項目層之技能符連用相對徑(
)。全域符連用絕對徑(../../skills/<name>
)。混用致遷時斷/path/to/almanac/skills/<name> -
註冊頭與實數:註冊頭之
或陳,若有增條而不更其數。信實際之total_skills
條,勿信頭- id:
參
- repair-broken-references — 廣之斷鏈與斷引之修
- tidy-project-structure — 項目目錄之整
- create-skill — 新技能之建,含符連之立(第十三步)
- create-agent — 新代理之建,含發現之驗(第十步)
- create-team — 團隊之建,與註冊整合