agentleFS
AirMileAirMile/claude-config/skills/project-add/SKILL.md

project-add

Skill0 starsChanged 4 months agoMITView on GitHub

Add or clone a project into the multi-project setup. Use with /project-add.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 2 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use the detected platform to resolve {projects_root} and {config_repo} from paths.yaml (see Configuration section below).

    line 43
  2. Very specific:

    If GH_AVAILABLE=false: show only "Create new project" (Clone requires gh).

    line 92
  3. Distinctive:

    No spaces or special characters

    line 124
  4. Very specific:

    Not existing in {projects_root}

    line 125
  5. Very specific:

    Check that {projects_root}/[name] does not already exist

    line 178
  6. Very specific:

    If .gitignore does not exist, create it with the above entries.

    line 335
  7. Very specific:

    Write a setup marker so a later /core-setup run starts in the right mode.

    line 391
  8. Very specific:

    If GH_AVAILABLE=false: skip this phase.

    line 418
  9. Very specific:

    Read user input → store as REPO_DESC (can be empty).

    line 446
  10. Distinctive:

    Create GitHub repo and push:

    line 448
  11. Very specific:

    Check with gh auth status before starting

    line 470

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Project Add

Adds a project — create a new project or clone an existing GitHub repo — with symlinks (macOS) or junctions (Windows) to the shared claude-config.

Trigger

/project-add [name] or /project-add

Process

PHASE 0: Pre-flight Checks

Detect platform:

# Detect OS
case "$(uname -s)" in
  Darwin)                PLATFORM="macos" ;;
  Linux)                 PLATFORM="linux" ;;
  MINGW*|CYGWIN*|MSYS*) PLATFORM="windows" ;;
  *) echo "Unsupported platform: $(uname -s)" >&2; exit 1 ;;
esac

Use the detected platform to resolve {projects_root} and {config_repo} from paths.yaml (see Configuration section below).

Before anything is created, validate:

# Fallback: if config_repo is missing but ~/.claude is already linked to a
# real claude-config, adopt that location instead of aborting (handles
# non-standard install locations).
if [ ! -d "$CONFIG_REPO" ] && [ -L "$HOME/.claude/skills" ]; then
  CONFIG_REPO="$(dirname "$(readlink "$HOME/.claude/skills")")"
  echo "config_repo not at default — resolved via ~/.claude/skills → $CONFIG_REPO"
fi

# Check claude-config exists and is complete
test -d "{config_repo}"
test -d "{config_repo}/scripts"

# Check gh CLI authenticated (needed for clone mode and publish)
gh auth status

If config check fails:

❌ claude-config not found or incomplete

Expected: {config_repo}
With folders: agents/, skills/, scripts/

Solution:
1. Clone claude-config repo to {config_repo}
2. Or set path via CLAUDE_CONFIG_REPO environment variable

→ Stop command, do NOT create any folders

If gh auth fails: → Store: GH_AVAILABLE=false. Show: gh not available — clone mode and GitHub publish skipped.

If checks pass: → Continue to PHASE 1

PHASE 1: Mode Selection

If name provided via /project-add [name]: → Assume: new project mode. Validate the name immediately (same rules as PHASE 2 (new): lowercase letters/digits/hyphens, no spaces or special characters, not existing in {projects_root}). On validation error: show the error and stop. If valid: store name and go to PHASE 3 (skip PHASE 2 (new) name question).

If no name provided:

If GH_AVAILABLE=false: show only "Create new project" (Clone requires gh).

question: "What do you want to do?"
header: "Mode"
options:
  - label: "Create new project"
    description: "Create an empty project with claude-config symlinks"
  - label: "Clone existing repo" # only show if GH_AVAILABLE=true
    description: "Clone a GitHub repo and configure claude-config symlinks"
multiSelect: false

→ New project: go to PHASE 2 (new) → Clone: go to PHASE 2 (clone)

PHASE 2 (new): Project Name

Ask name:

question: "What is the name of the new project?"
header: "Project"
options:
  - label: "Type a name"
    description: "Short, lowercase name without spaces (e.g. my-app)"
multiSelect: false

Validation:

  • Lowercase letters, digits, hyphens
  • No spaces or special characters
  • Not existing in {projects_root}

→ Go to PHASE 3

PHASE 2 (clone): Repo Selection

Two sub-options:

question: "How do you want to select the repo?"
header: "Repo"
options:
  - label: "Browse my repos (Recommended)"
    description: "Show list of your GitHub repos"
  - label: "Enter manually"
    description: "Type owner/repo or full GitHub URL"
multiSelect: false

Browse mode:

gh repo list --limit 30 --json name,description,isPrivate,url --jq '.[] | "\(.name)\t\(.description // "-")\t\(if .isPrivate then "🔒" else "🌐" end)\t\(.url)"'

Show as numbered list in plain text:

Available repos:

 1. my-app          — My cool app             🔒
 2. website         — Personal site            🌐
 3. api-backend     — REST API service         🔒
...

M. Load more
Q. Enter manually

Which repo? (number)
  • User chooses number → select that repo
  • M → load next 30 (--limit 30 with offset)
  • Q → switch to manual entry

Manual mode:

User types owner/repo or full GitHub URL (e.g. https://github.com/owner/repo). Parse to owner/repo format.

After repo selection:

  1. Extract project name from repo name
  2. Check that {projects_root}/[name] does not already exist
  3. Clone:
gh repo clone <owner/repo> {projects_root}/[name]

→ Go to PHASE 3

PHASE 3: Setup Directories

Create project subdirectories (mkdir -p is safe for both modes):

mkdir -p {projects_root}/[name]/.claude/docs
mkdir -p {projects_root}/[name]/.claude/research
mkdir -p {projects_root}/[name]/.project/session
mkdir -p {projects_root}/[name]/.project/plans
mkdir -p {projects_root}/[name]/.project/features

New mode: creates everything from scratch. Clone mode: project root already exists, only creates .claude/ and .project/ subdirs.

PHASE 4: Base Files

New mode:

Copy templates:

Write initial project files:

macOS / Linux:

cat > "{projects_root}/[name]/.project/project.json" << 'ENDJSON'
{
  "schemaVersion": 2,
  "seed": { "name": "[name]", "pitch": "", "content": "" },
  "localUrl": "",
  "theme": {
    "colors": { "main": [], "accent": [], "semantic": [] },
    "typography": { "families": { "heading": "", "body": "", "mono": "" }, "sizes": [] },
    "spacing": { "base": "", "scale": [] },
    "breakpoints": [],
    "borderRadius": [],
    "shadows": [],
    "modes": {},
    "cssVars": ""
  },
  "stack": { "framework": "", "language": "", "styling": "", "db": "", "auth": "", "hosting": "", "packages": [] },
  "data": { "entities": [] },
  "endpoints": [],
  "team": { "mode": "solo" },
  "thinking": []
}
ENDJSON

cat > "{projects_root}/[name]/.project/project-context.json" << 'ENDJSON'
{
  "schemaVersion": 2,
  "architecture": { "routes": [], "components": [], "endpoints": [], "entities": [], "diagram": "", "dataFlow": "" },
  "context": { "structure": "", "routing": [], "patterns": [] },
  "learnings": []
}
ENDJSON

Windows (PowerShell):

$projectJson = '{
  "schemaVersion": 2,
  "seed": { "name": "[name]", "pitch": "", "content": "" },
  "localUrl": "",
  "theme": {
    "colors": { "main": [], "accent": [], "semantic": [] },
    "typography": { "families": { "heading": "", "body": "", "mono": "" }, "sizes": [] },
    "spacing": { "base": "", "scale": [] },
    "breakpoints": [], "borderRadius": [], "shadows": [], "modes": {}, "cssVars": ""
  },
  "stack": { "framework": "", "language": "", "styling": "", "db": "", "auth": "", "hosting": "", "packages": [] },
  "data": { "entities": [] },
  "endpoints": [], "team": { "mode": "solo" }, "thinking": []
}'
Set-Content -Path "{projects_root}\[name]\.project\project.json" -Value $projectJson -Encoding UTF8

$ctxJson = '{
  "schemaVersion": 2,
  "architecture": { "routes": [], "components": [], "endpoints": [], "entities": [], "diagram": "", "dataFlow": "" },
  "context": { "structure": "", "routing": [], "patterns": [] },
  "learnings": []
}'
Set-Content -Path "{projects_root}\[name]\.project\project-context.json" -Value $ctxJson -Encoding UTF8

Replace [name] literally with the actual project name in both files.

# settings.local.json with default permissions
echo '{"permissions": {"allow": []}}' > {projects_root}/[name]/.claude/settings.local.json

.gitignore with standard content:

# Dependencies
node_modules/

# Build output
dist/
build/

# Environment
.env
.env.local
.env.*.local

# IDE
.idea/
.vscode/
*.swp

# OS
.DS_Store
Thumbs.db

# Claude tooling & local project data — not committed to the project repo
# (managed by the shared claude-config / master repo, not this one).
# Wholesale entries (no trailing slash) so symlinks, real dirs and sub-paths all match.
.claude/
.project/
CLAUDE.md
AGENTS.md

Clone mode:

Create settings.local.json:

echo '{"permissions": {"allow": []}}' > {projects_root}/[name]/.claude/settings.local.json

.gitignore — append claude-specific entries if not already present:

Check each entry below; append only the ones not already present (a wholesale entry like .claude/ already covers every sub-path, so skip any that are redundant). Use no trailing slash — .claude/agents is a symlink file, and a trailing-slash pattern (.claude/agents/) would only match a directory and silently fail to ignore it:

# Claude tooling & local project data — not committed to the project repo
# (managed by the shared claude-config / master repo, not this one).
.claude/
.project/
CLAUDE.md
AGENTS.md

If .gitignore does not exist, create it with the above entries.

PHASE 5: Config Symlinks

Link the shared claude-config into the project so skills/agents/hooks/scripts resolve at runtime. These four links are gitignored (PHASE 4) — per-device pointers, not tracked content. Runs for both new and clone mode.

macOS / Linux:

for d in agents hooks skills scripts; do
  test -d "{config_repo}/$d" || { echo "WARN: {config_repo}/$d missing — skipping"; continue; }
  ln -sfn "{config_repo}/$d" "{projects_root}/[name]/.claude/$d"
done

# Verify each link resolves
for d in agents hooks skills scripts; do
  test -e "{projects_root}/[name]/.claude/$d" && echo "$d: OK" || echo "$d: BROKEN"
done

Windows (PowerShell — junctions, no admin needed):

foreach ($d in 'agents','hooks','skills','scripts') {
  $target = "{config_repo}\$d"
  if (!(Test-Path $target)) { Write-Warning "$target missing — skipping"; continue }
  $link = "{projects_root}\[name]\.claude\$d"
  if (Test-Path $link) { Remove-Item $link -Recurse -Force }
  New-Item -ItemType Junction -Path $link -Target $target | Out-Null
}

PHASE 6: Git Initialization

New mode:

cd {projects_root}/[name]
git init
git add .gitignore

Clone mode:

Git init is skipped (repo is already initialized by gh repo clone). Instead, restore durable .project/ state if this project was synced from another device:

cd {projects_root}/[name]
git ls-remote --heads origin "claude/state*"
  • A claude/state* branch matches → follow shared/STATE-SYNC.md § 9 (Clone restore): discover the branch (§ 2 precedence), add a detached temp worktree at its tip, run state-files.py restore, write .project/session/state-sync.json, remove the worktree. Script path: {config_repo}/skills/project-sync/scripts/state-files.py. Show: State restored: {N} files from {branch} ({shortsha}).
  • No match → skip silently (scaffold-only clone, unchanged behavior).

PHASE 7: Project Configuration

Write a setup marker so a later /core-setup run starts in the right mode. Do NOT run or offer to run /core-setup as part of this flow — it belongs in a fresh session, never chained into project-add.

Determine intended core-setup mode:

  • New mode → setup_mode = "greenfield"
  • Clone mode → setup_mode = "mature" (cloned repo may already have source code)

Write marker (always — no prompt): set stateRestored to true only when PHASE 6 clone-mode restored state from a claude/state* branch, else false. A later /core-setup --mode=mature reads it: restored learnings/backlog already exist, so the mature scan runs additively (learnings dedup, team.mode skip-if-set) and its "pre-existing learnings" modal defaults to Continue.

mkdir -p .project/session
cat > ".project/session/setup-pending.json" << ENDJSON
{
  "source": "project-add",
  "mode": "{setup_mode}",
  "stateRestored": {state_restored},
  "createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
ENDJSON

Show: Setup marker written — a later /core-setup run starts directly in {setup_mode} mode.

PHASE 8: GitHub Publish

New mode:

If GH_AVAILABLE=false: skip this phase. Show: GitHub publish skipped — gh not authenticated. Go to PHASE 9.

question: "Do you want to publish the repo to GitHub?"
header: "Publish"
options:
  - label: "Yes, create private repo (Recommended)"
    description: "Publish as private GitHub repository"
  - label: "Yes, create public repo"
    description: "Publish as public GitHub repository"
  - label: "No, later"
    description: "Skip, publish manually later"
multiSelect: false

If publish desired:

  1. Stage all files and create initial commit:
cd {projects_root}/[name]
git add -A
git commit -m "feat: initial commit - [name]"
  1. Ask short description (optional — free text):

Show: Short GitHub description (optional, Enter to skip): Read user input → store as REPO_DESC (can be empty).

  1. Create GitHub repo and push:
# Build description argument as bash array (empty = no flag)
if [ -n "$REPO_DESC" ]; then
  DESC_FLAG=(--description "$REPO_DESC")
else
  DESC_FLAG=()
fi

# Private repo
gh repo create [name] --private --source=. --push "${DESC_FLAG[@]}"

# OR public repo
gh repo create [name] --public --source=. --push "${DESC_FLAG[@]}"
  1. Show repo URL after successful publish

Requirements for publish:

  • gh CLI installed and authenticated
  • Check with gh auth status before starting

Clone mode:

→ Skip (repo is already on GitHub)

Show: GitHub: [repo URL]

PHASE 9: Shell Alias

Ask:

question: "Do you want to create a shell alias to quickly open this project?"
header: "Alias"
options:
  - label: "Yes, create alias"
    description: "Add alias to ~/.bashrc that runs cd + claude"
  - label: "No, skip"
    description: "Don't create alias"
multiSelect: false

If alias desired:

Suggest a short alias based on the project name (first letters, abbreviation, or initials). Let the user confirm or change it.

question: "Which alias do you want to use?"
header: "Alias"
options:
  - label: "[suggestion] (Recommended)"
    description: "alias [suggestion]='cd {projects_root}/[name] && claude'"
  - label: "Different name"
    description: "Type your own alias name"
multiSelect: false

Validation:

  • Alias must not already exist in the target rc-file
  • Lowercase letters only, max 4 characters (short and fast)

Add:

Detect shell and choose rc-file:

case "$SHELL" in
  */zsh)  RC_FILE="$HOME/.zshrc" ;;
  */bash) RC_FILE="$HOME/.bashrc" ;;
  */fish) RC_FILE="$HOME/.config/fish/config.fish" ;;
  *)      RC_FILE="$HOME/.profile" ;;
esac

echo "alias [alias]='cd {projects_root}/[name] && claude'" >> "$RC_FILE"

# Reload the rc-file so the alias is registered (errors suppressed —
# interactive-only rc lines may warn in a non-interactive shell)
source "$RC_FILE" 2>/dev/null || true

Note: source runs in the skill's subprocess, so the alias is not active in the user's current terminal (a separate process). It only takes effect in a new terminal or after the user runs source $RC_FILE themselves — reflect that in the confirm message.

Confirm:

Alias created: [alias] → cd {projects_root}/[name] && claude
Added to: $RC_FILE
Active in new terminals — for this terminal, run: source $RC_FILE

PHASE 10: Wrap Up

Output (new mode):

✅ Project [name] created

Structure:
{projects_root}/[name]/
├── .claude/
│   ├── docs/
│   ├── research/
│   └── CLAUDE.md (or yet to configure)
├── .project/
└── .gitignore

Alias: [alias] → cd {projects_root}/[name] && claude (if created)
GitHub: https://github.com/[user]/[name] (if published)

Output (clone mode):

✅ Project [name] cloned and configured

Source: https://github.com/[owner]/[repo]

Structure:
{projects_root}/[name]/
├── .claude/
│   ├── docs/
│   ├── research/
│   └── CLAUDE.md (or yet to configure)
├── .project/
├── .gitignore (updated with claude entries)
└── [existing repo files]

Alias: [alias] → cd {projects_root}/[name] && claude (if created)
GitHub: https://github.com/[owner]/[repo]

Configuration

Paths are configurable per device. Defaults are platform-dependent:

PlaceholdermacOS DefaultWindows DefaultEnvironment Variable
{projects_root}$HOME/projectsC:\ProjectsCLAUDE_PROJECTS_ROOT
{config_repo}$HOME/claude-configC:\Projects\claude-configCLAUDE_CONFIG_REPO

Resolution order (first match wins):

  1. Environment variable
  2. .claude/paths.local.yaml (local per project, not in git)
  3. paths.yaml (skill root — shared defaults, platform section)

Restrictions

  • Supported on macOS/Linux (symlinks) and Windows (junctions)
  • Project name must be unique in {projects_root}
  • Master config must exist in {config_repo}
  • Clone mode requires gh CLI authenticated
  • GitHub publish requires gh CLI authenticated

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.