mirror of
https://github.com/gnekt/My-Brain-Is-Full-Crew.git
synced 2026-09-13 01:06:50 +00:00
REVAMPED
This commit is contained in:
124
scripts/generate-skills.py
Normal file
124
scripts/generate-skills.py
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate skills/ from agents/ — single source of truth stays in agents/*.md.
|
||||
|
||||
Each agent becomes a skill at skills/{name}/SKILL.md with:
|
||||
- tools: → allowed-tools:
|
||||
- disallowedTools: → (dropped if allowed-tools is set)
|
||||
- model: → model:
|
||||
- description: → description:
|
||||
+ context: fork → run in isolated subagent (like a real subagent)
|
||||
|
||||
Usage: python3 scripts/generate-skills.py
|
||||
"""
|
||||
|
||||
import os, re, sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
AGENTS_DIR = os.path.join(ROOT, "agents")
|
||||
SKILLS_DIR = os.path.join(ROOT, "skills")
|
||||
|
||||
|
||||
def parse_agent(path: str):
|
||||
"""Return (frontmatter_dict, body_str) from an agent .md file."""
|
||||
with open(path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
|
||||
# Split on the two --- delimiters
|
||||
m = re.match(r"^---\n(.*?\n)---\n(.*)", text, re.DOTALL)
|
||||
if not m:
|
||||
return None, text
|
||||
|
||||
raw_fm = m.group(1)
|
||||
body = m.group(2)
|
||||
|
||||
# Simple YAML-ish parser for the flat fields we care about
|
||||
fm = {}
|
||||
current_key = None
|
||||
current_val = ""
|
||||
|
||||
for line in raw_fm.split("\n"):
|
||||
# New key
|
||||
key_match = re.match(r"^(\w[\w-]*):\s*(.*)", line)
|
||||
if key_match:
|
||||
if current_key:
|
||||
fm[current_key] = current_val.strip()
|
||||
current_key = key_match.group(1)
|
||||
val = key_match.group(2)
|
||||
if val == ">":
|
||||
current_val = ""
|
||||
else:
|
||||
current_val = val
|
||||
elif current_key and line.startswith(" "):
|
||||
current_val += " " + line.strip()
|
||||
|
||||
if current_key:
|
||||
fm[current_key] = current_val.strip()
|
||||
|
||||
return fm, body
|
||||
|
||||
|
||||
def build_skill_frontmatter(fm: dict) -> str:
|
||||
"""Convert agent frontmatter to skill frontmatter."""
|
||||
lines = ["---"]
|
||||
|
||||
name = fm.get("name", "unknown")
|
||||
lines.append(f"name: {name}")
|
||||
|
||||
if "description" in fm:
|
||||
desc = fm["description"]
|
||||
lines.append("description: >")
|
||||
# Wrap description at ~90 chars, indent with exactly 2 spaces
|
||||
words = desc.split()
|
||||
line = ""
|
||||
for w in words:
|
||||
if line and len(line) + len(w) + 1 > 88:
|
||||
lines.append(" " + line)
|
||||
line = w
|
||||
else:
|
||||
line = line + " " + w if line else w
|
||||
if line:
|
||||
lines.append(" " + line)
|
||||
|
||||
# Skills only support: name, description, argument-hint, compatibility,
|
||||
# disable-model-invocation, license, metadata, user-invocable.
|
||||
# No tool restrictions, model override, or fork context.
|
||||
|
||||
lines.append("---")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
if not os.path.isdir(AGENTS_DIR):
|
||||
print(f"Error: {AGENTS_DIR} not found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
count = 0
|
||||
for fname in sorted(os.listdir(AGENTS_DIR)):
|
||||
if not fname.endswith(".md"):
|
||||
continue
|
||||
|
||||
agent_path = os.path.join(AGENTS_DIR, fname)
|
||||
fm, body = parse_agent(agent_path)
|
||||
if fm is None:
|
||||
print(f" ! Skipped {fname} (no frontmatter)")
|
||||
continue
|
||||
|
||||
name = fm.get("name", fname.replace(".md", ""))
|
||||
skill_dir = os.path.join(SKILLS_DIR, name)
|
||||
os.makedirs(skill_dir, exist_ok=True)
|
||||
|
||||
skill_content = build_skill_frontmatter(fm) + "\n" + body
|
||||
skill_path = os.path.join(skill_dir, "SKILL.md")
|
||||
|
||||
with open(skill_path, "w", encoding="utf-8") as f:
|
||||
f.write(skill_content)
|
||||
|
||||
print(f" ✓ {name}/SKILL.md")
|
||||
count += 1
|
||||
|
||||
print(f"\n Generated {count} skills in skills/")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
148
scripts/launchme.sh
Executable file
148
scripts/launchme.sh
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# My Brain Is Full - Crew :: Installer
|
||||
# =============================================================================
|
||||
# Run this from inside the cloned repo, which should be inside your vault:
|
||||
#
|
||||
# cd /path/to/your-vault/My-Brain-Is-Full-Crew
|
||||
# bash scripts/launchme.sh
|
||||
#
|
||||
# It copies agents and references into your vault's .claude/ directory.
|
||||
# =============================================================================
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# ── Colors ──────────────────────────────────────────────────────────────────
|
||||
if [[ -t 1 ]]; then
|
||||
GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m'
|
||||
else
|
||||
GREEN=''; CYAN=''; YELLOW=''; RED=''; BOLD=''; DIM=''; NC=''
|
||||
fi
|
||||
|
||||
info() { echo -e " ${CYAN}>${NC} $*"; }
|
||||
success() { echo -e " ${GREEN}✓${NC} $*"; }
|
||||
warn() { echo -e " ${YELLOW}!${NC} $*"; }
|
||||
die() { echo -e "\n ${RED}Error: $*${NC}\n" >&2; exit 1; }
|
||||
|
||||
# ── Find paths ──────────────────────────────────────────────────────────────
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
VAULT_DIR="$(cd "$REPO_DIR/.." && pwd)"
|
||||
|
||||
# Sanity checks
|
||||
[[ -d "$REPO_DIR/agents" ]] || die "Can't find agents/ in $REPO_DIR — are you running this from the repo?"
|
||||
[[ -d "$REPO_DIR/references" ]] || die "Can't find references/ in $REPO_DIR"
|
||||
|
||||
# ── Banner ──────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}╔══════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BOLD}║ My Brain Is Full - Crew :: Setup ║${NC}"
|
||||
echo -e "${BOLD}╚══════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo -e " Repo: ${BOLD}${REPO_DIR}${NC}"
|
||||
echo -e " Vault: ${BOLD}${VAULT_DIR}${NC}"
|
||||
echo ""
|
||||
|
||||
# ── Confirm vault location ─────────────────────────────────────────────────
|
||||
echo -e "${BOLD}Is this your Obsidian vault folder?${NC}"
|
||||
echo -e " ${DIM}${VAULT_DIR}${NC}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}y)${NC} Yes, install here"
|
||||
echo -e " ${BOLD}n)${NC} No, let me type the correct path"
|
||||
read -r -p " > " CONFIRM
|
||||
|
||||
if [[ "$CONFIRM" =~ ^[Nn]$ ]]; then
|
||||
echo ""
|
||||
echo -e "${BOLD}Enter the full path to your Obsidian vault:${NC}"
|
||||
read -r -p " > " VAULT_DIR
|
||||
VAULT_DIR="${VAULT_DIR/#\~/$HOME}"
|
||||
[[ -d "$VAULT_DIR" ]] || die "Directory not found: $VAULT_DIR"
|
||||
fi
|
||||
|
||||
# ── Copy agents ─────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
info "Creating .claude/agents/ in vault..."
|
||||
mkdir -p "$VAULT_DIR/.claude/agents"
|
||||
|
||||
AGENT_COUNT=0
|
||||
for agent in "$REPO_DIR/agents/"*.md; do
|
||||
cp "$agent" "$VAULT_DIR/.claude/agents/"
|
||||
AGENT_COUNT=$((AGENT_COUNT + 1))
|
||||
done
|
||||
success "Copied $AGENT_COUNT agents"
|
||||
|
||||
# ── Copy references ─────────────────────────────────────────────────────────
|
||||
info "Creating .claude/references/ in vault..."
|
||||
mkdir -p "$VAULT_DIR/.claude/references"
|
||||
cp "$REPO_DIR/references/"*.md "$VAULT_DIR/.claude/references/"
|
||||
success "Copied references"
|
||||
|
||||
# ── Generate and copy skills ──────────────────────────────────────────────────
|
||||
if command -v python3 >/dev/null 2>&1 && [[ -f "$REPO_DIR/scripts/generate-skills.py" ]]; then
|
||||
info "Generating skills from agents..."
|
||||
python3 "$REPO_DIR/scripts/generate-skills.py" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [[ -d "$REPO_DIR/skills" ]]; then
|
||||
info "Creating .claude/skills/ in vault..."
|
||||
SKILL_COUNT=0
|
||||
for skill_dir in "$REPO_DIR/skills/"*/; do
|
||||
skill_name="$(basename "$skill_dir")"
|
||||
mkdir -p "$VAULT_DIR/.claude/skills/$skill_name"
|
||||
cp "$skill_dir"* "$VAULT_DIR/.claude/skills/$skill_name/" 2>/dev/null || true
|
||||
SKILL_COUNT=$((SKILL_COUNT + 1))
|
||||
done
|
||||
success "Copied $SKILL_COUNT skills"
|
||||
fi
|
||||
|
||||
# ── Copy CLAUDE.md ───────────────────────────────────────────────────────────
|
||||
if [[ -f "$REPO_DIR/CLAUDE.md" ]]; then
|
||||
cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md"
|
||||
success "Copied CLAUDE.md"
|
||||
fi
|
||||
|
||||
# ── MCP servers (Gmail + Calendar) ──────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}Do you use Gmail or Google Calendar?${NC}"
|
||||
echo -e " ${DIM}The Postman agent can read your inbox and calendar.${NC}"
|
||||
echo -e " ${DIM}You can always add this later.${NC}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}y)${NC} Yes, set up Gmail + Calendar"
|
||||
echo -e " ${BOLD}n)${NC} No, skip for now"
|
||||
read -r -p " > " MCP_ANSWER
|
||||
|
||||
if [[ "$MCP_ANSWER" =~ ^[Yy]$ ]]; then
|
||||
if [[ -f "$VAULT_DIR/.mcp.json" ]]; then
|
||||
warn ".mcp.json already exists — skipping (won't overwrite)"
|
||||
else
|
||||
cp "$REPO_DIR/.mcp.json" "$VAULT_DIR/.mcp.json"
|
||||
success "Created .mcp.json (Gmail + Google Calendar)"
|
||||
fi
|
||||
else
|
||||
info "Skipped MCP setup"
|
||||
fi
|
||||
|
||||
# ── Done ────────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${GREEN}${BOLD} Setup complete!${NC}"
|
||||
echo ""
|
||||
echo -e " Your vault is ready. Here's what was installed:"
|
||||
echo ""
|
||||
echo -e " ${VAULT_DIR}/"
|
||||
echo -e " ├── .claude/"
|
||||
echo -e " │ ├── agents/ ${DIM}← ${AGENT_COUNT} crew agents (CLI)${NC}"
|
||||
echo -e " │ ├── skills/ ${DIM}← ${SKILL_COUNT:-0} crew skills (Cowork/Desktop)${NC}"
|
||||
echo -e " │ └── references/ ${DIM}← shared docs${NC}"
|
||||
echo -e " ├── CLAUDE.md ${DIM}← project instructions${NC}"
|
||||
if [[ "$MCP_ANSWER" =~ ^[Yy]$ ]]; then
|
||||
echo -e " └── .mcp.json ${DIM}← Gmail + Calendar${NC}"
|
||||
fi
|
||||
echo ""
|
||||
echo -e " ${BOLD}Next steps:${NC}"
|
||||
echo -e " 1. Open Claude Code in your vault folder"
|
||||
echo -e " 2. Say: ${BOLD}\"Initialize my vault\"${NC}"
|
||||
echo -e " 3. The Architect will guide you through setup"
|
||||
echo ""
|
||||
echo -e " ${DIM}To update after a git pull: bash scripts/updateme.sh${NC}"
|
||||
echo ""
|
||||
118
scripts/updateme.sh
Executable file
118
scripts/updateme.sh
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# My Brain Is Full - Crew :: Updater
|
||||
# =============================================================================
|
||||
# After pulling new changes from the repo, run this to update the agents
|
||||
# in your vault:
|
||||
#
|
||||
# cd /path/to/your-vault/My-Brain-Is-Full-Crew
|
||||
# git pull
|
||||
# bash scripts/updateme.sh
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# ── Colors ──────────────────────────────────────────────────────────────────
|
||||
if [[ -t 1 ]]; then
|
||||
GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m'
|
||||
else
|
||||
GREEN=''; CYAN=''; YELLOW=''; RED=''; BOLD=''; DIM=''; NC=''
|
||||
fi
|
||||
|
||||
info() { echo -e " ${CYAN}>${NC} $*"; }
|
||||
success() { echo -e " ${GREEN}✓${NC} $*"; }
|
||||
warn() { echo -e " ${YELLOW}!${NC} $*"; }
|
||||
die() { echo -e "\n ${RED}Error: $*${NC}\n" >&2; exit 1; }
|
||||
|
||||
# ── Find paths ──────────────────────────────────────────────────────────────
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
VAULT_DIR="$(cd "$REPO_DIR/.." && pwd)"
|
||||
|
||||
[[ -d "$REPO_DIR/agents" ]] || die "Can't find agents/ — are you running this from the repo?"
|
||||
|
||||
# ── Check vault has been set up ─────────────────────────────────────────────
|
||||
if [[ ! -d "$VAULT_DIR/.claude/agents" ]]; then
|
||||
die "No .claude/agents/ found in $VAULT_DIR — run launchme.sh first"
|
||||
fi
|
||||
|
||||
# ── Banner ──────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}╔══════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BOLD}║ My Brain Is Full - Crew :: Update ║${NC}"
|
||||
echo -e "${BOLD}╚══════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# ── Update agents ───────────────────────────────────────────────────────────
|
||||
AGENT_COUNT=0
|
||||
for agent in "$REPO_DIR/agents/"*.md; do
|
||||
name="$(basename "$agent")"
|
||||
if [[ -f "$VAULT_DIR/.claude/agents/$name" ]]; then
|
||||
if ! diff -q "$agent" "$VAULT_DIR/.claude/agents/$name" >/dev/null 2>&1; then
|
||||
cp "$agent" "$VAULT_DIR/.claude/agents/"
|
||||
info "Updated $name"
|
||||
AGENT_COUNT=$((AGENT_COUNT + 1))
|
||||
fi
|
||||
else
|
||||
cp "$agent" "$VAULT_DIR/.claude/agents/"
|
||||
info "Added $name (new agent)"
|
||||
AGENT_COUNT=$((AGENT_COUNT + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Update references ───────────────────────────────────────────────────────
|
||||
REF_COUNT=0
|
||||
mkdir -p "$VAULT_DIR/.claude/references"
|
||||
for ref in "$REPO_DIR/references/"*.md; do
|
||||
name="$(basename "$ref")"
|
||||
if ! diff -q "$ref" "$VAULT_DIR/.claude/references/$name" >/dev/null 2>&1; then
|
||||
cp "$ref" "$VAULT_DIR/.claude/references/"
|
||||
info "Updated reference: $name"
|
||||
REF_COUNT=$((REF_COUNT + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Regenerate and update skills ───────────────────────────────────────────
|
||||
SKILL_COUNT=0
|
||||
if command -v python3 >/dev/null 2>&1 && [[ -f "$REPO_DIR/scripts/generate-skills.py" ]]; then
|
||||
python3 "$REPO_DIR/scripts/generate-skills.py" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [[ -d "$REPO_DIR/skills" ]]; then
|
||||
for skill_dir in "$REPO_DIR/skills/"*/; do
|
||||
skill_name="$(basename "$skill_dir")"
|
||||
src="$skill_dir/SKILL.md"
|
||||
dst="$VAULT_DIR/.claude/skills/$skill_name/SKILL.md"
|
||||
if [[ -f "$src" ]]; then
|
||||
if [[ ! -f "$dst" ]] || ! diff -q "$src" "$dst" >/dev/null 2>&1; then
|
||||
mkdir -p "$VAULT_DIR/.claude/skills/$skill_name"
|
||||
cp "$src" "$dst"
|
||||
info "Updated skill: $skill_name"
|
||||
SKILL_COUNT=$((SKILL_COUNT + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ── Update CLAUDE.md ──────────────────────────────────────────────────────
|
||||
CLAUDE_MD_UPDATED=""
|
||||
if [[ -f "$REPO_DIR/CLAUDE.md" ]]; then
|
||||
if [[ ! -f "$VAULT_DIR/CLAUDE.md" ]] || ! diff -q "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md" >/dev/null 2>&1; then
|
||||
cp "$REPO_DIR/CLAUDE.md" "$VAULT_DIR/CLAUDE.md"
|
||||
info "Updated CLAUDE.md"
|
||||
CLAUDE_MD_UPDATED="1"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Summary ─────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
if [[ $AGENT_COUNT -eq 0 && $REF_COUNT -eq 0 && $SKILL_COUNT -eq 0 && -z "$CLAUDE_MD_UPDATED" ]]; then
|
||||
success "Everything is already up to date!"
|
||||
else
|
||||
success "Updated $AGENT_COUNT agent(s), $SKILL_COUNT skill(s), and $REF_COUNT reference(s)"
|
||||
fi
|
||||
echo ""
|
||||
echo -e " ${DIM}Restart Claude Code to pick up the changes.${NC}"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user