Skip to content

Instantly share code, notes, and snippets.

@batpigandme
Last active June 18, 2026 12:24
Show Gist options
  • Select an option

  • Save batpigandme/10e28ddbf8ae451f445ee74148f6d4fe to your computer and use it in GitHub Desktop.

Select an option

Save batpigandme/10e28ddbf8ae451f445ee74148f6d4fe to your computer and use it in GitHub Desktop.
How to set up a project (AGENTS/CLAUDE.md, skills, etc) if you're working on it in both Claude Code and Claude Cowork.

One-liner

If you're authoring shared instructions across both Claude Code and Cowork from the same repo, bridge AGENTS.md → CLAUDE.md with a symlink, not an @import — Code expands @AGENTS.md, Cowork treats it as literal text. Discovered the hard way; fix is ln -s AGENTS.md CLAUDE.md.

Context. Spent yesterday refactoring a combo repo to work cleanly across Code + Cowork — extracting procedures into skills, trimming AGENTS.md, putting an audit on quarterly cadence. Found 5 Code/Cowork asymmetries that matter for anyone using both surfaces on the same repo.

1. CLAUDE.md @import doesn't work in Cowork. The Anthropic memory docs recommend a CLAUDE.md containing @AGENTS.md to bridge naming conventions. Code expands this directive on load. Cowork treats it as literal text. Result: instructions silently disappear in Cowork while your Code session looks fine.

Fix: replace the import with a symlink.

rm CLAUDE.md && ln -s AGENTS.md CLAUDE.md

Symlinks are filesystem-transparent — both surfaces read CLAUDE.md and get AGENTS.md's content. Single source of truth, no expansion semantics needed. Trade-off: git diff shows it as a typechange rather than visible text. Worth it.

2. Skill discovery is surface-different.

  • Code: filesystem-scans ~/.claude/skills/ and project-local <repo>/.claude/skills/ automatically.
  • Cowork: only registers skills you explicitly import via Customize → Add skill (UI). Doesn't auto-discover the filesystem.

So loose user skills under ~/.claude/skills/ are Code-only by default. You have to manually import each one into Cowork.

3. Cowork's import is a snapshot, not a live link. When you import a skill from a file, Cowork copies it into its own storage at ~/Library/Application Support/Claude/local-agent-mode-sessions/skills-plugin/.../skills/. Later edits to the Code source don't propagate. Edit-then-forget creates silent drift — Code sees your tightened description; Cowork still has the old version.

Drift-detection script I wrote that you can lift:

# ~/.claude/skills/workflow-hygiene/audit-cowork-drift.sh
# Reports NOT-IN-COWORK / DRIFTED / COWORK-ONLY. Silent = clean.
Full script in this dropdown
#!/usr/bin/env bash
# audit-cowork-drift.sh — detect Code↔Cowork skill copy drift.
#
# Cowork's "Customize → Add skill → import from file" copies SKILL.md into
# Cowork-managed storage. After the import, the two copies (Code's source +
# Cowork's frozen snapshot) drift independently — edits in one don't
# propagate to the other. This script surfaces the drift.
#
# Usage:
#   audit-cowork-drift.sh                   # checks ~/.claude/skills/ + ./.claude/skills/
#   audit-cowork-drift.sh <skill-dir> [...] # checks additional Code-side dirs
#
# Output:
#   NOT-IN-COWORK (origin): <name>   — exists in Code, not imported into Cowork
#   DRIFTED (origin):       <name>   — both exist but contents differ; re-import to sync
#   COWORK-ONLY:            <name>   — exists in Cowork only (plugin-bundled or manually added)
#
# "origin" is `personal` for ~/.claude/skills/ or `project-local` for any
# <repo>/.claude/skills/. Silent on skills that match — no output = clean.

set -euo pipefail

CODE_PERSONAL_DIR="$HOME/.claude/skills"
COWORK_ROOT="$HOME/Library/Application Support/Claude/local-agent-mode-sessions/skills-plugin"

# Resolve dir to its canonical path (follow symlinks), suppress errors.
canonical() {
  cd "$1" 2>/dev/null && pwd -P
}

# Collect Code-side skill dirs: personal + cwd project-local + any explicit args.
SKILL_DIRS=("$CODE_PERSONAL_DIR")
PERSONAL_CANON=$(canonical "$CODE_PERSONAL_DIR" || true)

if [ -d "$PWD/.claude/skills" ]; then
  PWD_CANON=$(canonical "$PWD/.claude/skills" || true)
  if [ -n "$PWD_CANON" ] && [ "$PWD_CANON" != "$PERSONAL_CANON" ]; then
    SKILL_DIRS+=("$PWD/.claude/skills")
  fi
fi

for arg in "$@"; do
  if [ -d "$arg" ]; then
    SKILL_DIRS+=("$arg")
  else
    echo "warning: $arg is not a directory, skipping" >&2
  fi
done

# Cowork stores skills at:
#   $COWORK_ROOT/<plugin-id>/<account-id>/skills/<name>/SKILL.md
# Find the active skills dir (most recent mtime if multiple accounts exist).
COWORK_SKILLS_DIR=$(find "$COWORK_ROOT" -type d -name skills 2>/dev/null \
  | xargs -I{} stat -f "%m %N" {} 2>/dev/null \
  | sort -rn | head -1 | cut -d' ' -f2-)

if [ -z "$COWORK_SKILLS_DIR" ] || [ ! -d "$COWORK_SKILLS_DIR" ]; then
  echo "warning: no Cowork skills directory found under $COWORK_ROOT" >&2
  echo "         (Cowork may not be installed, or no skills have been imported yet)" >&2
  exit 0
fi

# Track which skill names we've seen on the Code side (for the reverse pass).
CODE_NAMES_FILE=$(mktemp)
trap 'rm -f "$CODE_NAMES_FILE"' EXIT

# For each Code-side dir, check each skill against Cowork.
for dir in "${SKILL_DIRS[@]}"; do
  DIR_CANON=$(canonical "$dir" || echo "$dir")
  if [ "$DIR_CANON" = "$PERSONAL_CANON" ]; then
    origin="personal"
  else
    origin="project-local"
  fi

  for code_skill in "$dir"/*/SKILL.md; do
    [ -f "$code_skill" ] || continue
    name=$(basename "$(dirname "$code_skill")")
    echo "$name" >> "$CODE_NAMES_FILE"
    cowork_skill="$COWORK_SKILLS_DIR/$name/SKILL.md"

    if [ ! -f "$cowork_skill" ]; then
      echo "NOT-IN-COWORK ($origin): $name"
    elif ! diff -q "$code_skill" "$cowork_skill" >/dev/null 2>&1; then
      echo "DRIFTED ($origin):       $name"
    fi
  done
done

# Reverse pass: skills in Cowork but no Code counterpart anywhere.
for cowork_skill in "$COWORK_SKILLS_DIR"/*/SKILL.md; do
  [ -f "$cowork_skill" ] || continue
  name=$(basename "$(dirname "$cowork_skill")")
  if ! grep -qx "$name" "$CODE_NAMES_FILE"; then
    echo "COWORK-ONLY:             $name"
  fi
done

4. Cowork's file import only takes single-file SKILL.md. If your skill has bundled assets (helper scripts, reference docs), Cowork's import UI won't accept the directory. Options: inline the assets into SKILL.md, package as a plugin (~/.claude/plugins/marketplaces/<name>/), or accept that the skill is Code-only.

5. claude is a reserved word in skill names. Cowork's name validator rejects any skill name containing claude. Other product names may have similar constraints. Substitute (e.g., I named one bridging-agents-md-cowork-md after the validator rejected bridging-agents-md-claude-md).

Net. The portable form across both surfaces is: AGENTS.md as canonical brief, CLAUDE.md as a symlink to it, skills authored as single-file SKILL.md with neutral names, imported once to Cowork, then audited periodically for drift. None of this is in the docs as far as I can tell—discovered empirically.

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