plugins/dev/skills/context-detection/SKILL.md
Use when detecting project technology stack from files/configs/directory structure, auto-loading framework-specific skills, or analyzing multi-stack fullstack projects (e.g., React + Go).
npx skillsauth add madappgang/magus context-detectionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Run the helper script to discover ALL skills available to a project:
node "${CLAUDE_PLUGIN_ROOT}/skills/context-detection/scripts/discover-skills.js" "$(pwd)"
This searches all 7 official Claude Code skill locations:
~/.claude/skills/.claude/skills/**/.claude/skills/.claude/commands/~/.claude/plugins/marketplaces/{m}/plugins/{p}/skills/.claude-plugin/skills/, plugins/*/skills/Output: JSON with summary stats and full skill metadata.
For detailed documentation on the script, see scripts/discover-skills.js.
The context detection skill provides systematic patterns for analyzing any project to determine its technology stack(s). It enables the dev plugin to auto-load appropriate framework-specific skills based on what's actually in the project.
Key Innovation: Multi-stack detection for fullstack projects (e.g., React + Go).
Detection follows a priority order from most explicit to most inferred:
Source: .claude/settings.json
{
"pluginSettings": {
"dev": {
"stack": ["react-typescript", "golang"],
"features": {
"testing": "vitest",
"api": "rest"
}
}
}
}
When to use:
Source: File extension of current editing context
extension_mappings:
".tsx": ["react-typescript", "testing-frontend"]
".vue": ["vue-typescript", "testing-frontend"]
".go": ["golang", "testing-strategies"]
".dingo": ["dingo", "golang", "testing-strategies"]
".rs": ["rust", "testing-strategies"]
".py": ["python", "testing-strategies"]
When to use:
Source: Project configuration files
config_file_patterns:
package.json:
check: "dependencies.react exists"
skills: ["react-typescript", "state-management", "testing-frontend"]
mode: "frontend"
package.json:
check: "dependencies.vue exists"
skills: ["vue-typescript", "state-management", "testing-frontend"]
mode: "frontend"
go.mod:
check: "file exists"
skills: ["golang", "api-design", "database-patterns"]
mode: "backend"
go.mod + *.dingo:
check: "go.mod exists AND any .dingo files present"
skills: ["dingo", "golang", "api-design", "database-patterns"]
mode: "backend"
note: "Dingo projects always include golang skill since Dingo transpiles to Go"
Cargo.toml:
check: "file exists"
skills: ["rust", "api-design"]
mode: "backend"
pyproject.toml:
check: "file exists"
skills: ["python", "api-design"]
mode: "backend"
bun.lockb:
check: "file exists AND no react/vue in package.json"
skills: ["bunjs", "api-design"]
mode: "backend"
When to use:
Source: Common directory layouts
directory_patterns:
"src/routes/":
indicator: "React Router structure"
skills: ["react-typescript"]
"src/components/":
indicator: "Component-based frontend"
skills: ["react-typescript", "vue-typescript"]
"cmd/":
indicator: "Go standard project layout"
skills: ["golang"]
"src/main.rs":
indicator: "Rust binary crate"
skills: ["rust"]
"frontend/":
indicator: "Separate frontend directory (fullstack)"
note: "Check for backend/ as well"
"backend/":
indicator: "Separate backend directory (fullstack)"
note: "Check for frontend/ as well"
When to use:
CRITICAL: Always check for MULTIPLE stacks. Projects can be fullstack.
# Step 1: Find ALL config files (not just first match)
find_all_configs() {
configs=()
[ -f "package.json" ] && configs+=("package.json")
[ -f "go.mod" ] && configs+=("go.mod")
[ -f "Cargo.toml" ] && configs+=("Cargo.toml")
[ -f "pyproject.toml" ] && configs+=("pyproject.toml")
[ -f "bun.lockb" ] && configs+=("bun.lockb")
# Check for Dingo files (go.mod must also exist)
# Note: "dingo" is a detection indicator, not an actual config file name
if [ -f "go.mod" ] && find . -name "*.dingo" -type f ! -path "./.git/*" ! -path "./node_modules/*" ! -path "./vendor/*" -print -quit | grep -q .; then
configs+=("dingo")
fi
echo "${configs[@]}"
}
# Step 2: Analyze EACH config file
analyze_all_configs() {
local detected_stacks=()
# Check package.json
if [ -f "package.json" ]; then
if grep -q '"react"' package.json; then
detected_stacks+=("react-typescript")
elif grep -q '"vue"' package.json; then
detected_stacks+=("vue-typescript")
fi
fi
# Check go.mod (and optionally Dingo)
if [ -f "go.mod" ]; then
# Check if this is a Dingo project
if find . -name "*.dingo" -type f ! -path "./.git/*" ! -path "./node_modules/*" ! -path "./vendor/*" -print -quit | grep -q .; then
detected_stacks+=("dingo")
# Dingo always co-loads golang
detected_stacks+=("golang")
else
detected_stacks+=("golang")
fi
fi
# Check Cargo.toml
if [ -f "Cargo.toml" ]; then
detected_stacks+=("rust")
fi
# Check pyproject.toml
if [ -f "pyproject.toml" ]; then
detected_stacks+=("python")
fi
# Check bun.lockb (only if NOT frontend)
if [ -f "bun.lockb" ] && ! grep -q '"react"\|"vue"' package.json 2>/dev/null; then
detected_stacks+=("bunjs")
fi
echo "${detected_stacks[@]}"
}
# Step 3: Determine mode
determine_mode() {
local stacks=("$@")
local has_frontend=false
local has_backend=false
for stack in "${stacks[@]}"; do
case "$stack" in
react-typescript|vue-typescript)
has_frontend=true
;;
golang|rust|python|bunjs|dingo)
has_backend=true
;;
esac
done
if [ "$has_frontend" = true ] && [ "$has_backend" = true ]; then
echo "fullstack"
elif [ "$has_frontend" = true ]; then
echo "frontend"
elif [ "$has_backend" = true ]; then
echo "backend"
else
echo "unknown"
fi
}
# Step 4: Map stacks to skills
map_stacks_to_skills() {
local stacks=("$@")
local skills=(
# Core skills (ALWAYS included)
"universal-patterns"
"testing-strategies"
"debugging-strategies"
)
for stack in "${stacks[@]}"; do
case "$stack" in
react-typescript)
skills+=("react-typescript" "state-management" "testing-frontend")
;;
vue-typescript)
skills+=("vue-typescript" "state-management" "testing-frontend")
;;
golang)
skills+=("golang" "api-design" "database-patterns")
;;
dingo)
skills+=("dingo" "golang" "api-design" "database-patterns")
;;
rust)
skills+=("rust" "api-design")
;;
python)
skills+=("python" "api-design")
;;
bunjs)
skills+=("bunjs" "api-design")
;;
esac
done
# Deduplicate
echo "${skills[@]}" | tr ' ' '\n' | sort -u
}
# Step 5: Complete detection
detect_project_stack() {
# Check explicit preference first
local explicit_stack=$(jq -r '.pluginSettings.dev.stack // empty' .claude/settings.json 2>/dev/null)
if [ -n "$explicit_stack" ]; then
echo "Using explicit stack from .claude/settings.json"
return
fi
# Auto-detect all stacks
local detected_stacks=($(analyze_all_configs))
if [ ${#detected_stacks[@]} -eq 0 ]; then
echo "ERROR: No stack detected"
return 1
fi
# Determine mode
local mode=$(determine_mode "${detected_stacks[@]}")
# Map to skills
local skills=($(map_stacks_to_skills "${detected_stacks[@]}"))
# Output result
echo "Detected: ${detected_stacks[*]}"
echo "Mode: $mode"
echo "Skills: ${skills[*]}"
}
detect_react() {
# Check package.json
if [ -f "package.json" ]; then
# React in dependencies or devDependencies
if jq -e '.dependencies.react // .devDependencies.react' package.json >/dev/null 2>&1; then
# Check for TypeScript
local has_typescript=false
if jq -e '.dependencies["@types/react"] // .devDependencies["@types/react"]' package.json >/dev/null 2>&1; then
has_typescript=true
fi
# Get version
local react_version=$(jq -r '.dependencies.react // .devDependencies.react' package.json | sed 's/[^0-9.]//g')
echo "react-typescript"
echo "version: $react_version"
echo "typescript: $has_typescript"
return 0
fi
fi
return 1
}
detect_vue() {
if [ -f "package.json" ]; then
if jq -e '.dependencies.vue' package.json >/dev/null 2>&1; then
local vue_version=$(jq -r '.dependencies.vue' package.json | sed 's/[^0-9.]//g')
echo "vue-typescript"
echo "version: $vue_version"
return 0
fi
fi
return 1
}
detect_go() {
if [ -f "go.mod" ]; then
local go_version=$(grep '^go ' go.mod | awk '{print $2}')
local module_name=$(grep '^module ' go.mod | awk '{print $2}')
echo "golang"
echo "version: $go_version"
echo "module: $module_name"
return 0
fi
return 1
}
detect_rust() {
if [ -f "Cargo.toml" ]; then
local package_name=$(grep '^\[package\]' -A 5 Cargo.toml | grep '^name' | cut -d'"' -f2)
local edition=$(grep '^\[package\]' -A 5 Cargo.toml | grep '^edition' | cut -d'"' -f2)
echo "rust"
echo "edition: $edition"
echo "package: $package_name"
return 0
fi
return 1
}
detect_python() {
if [ -f "pyproject.toml" ]; then
local has_fastapi=$(grep -i 'fastapi' pyproject.toml)
local has_django=$(grep -i 'django' pyproject.toml)
echo "python"
[ -n "$has_fastapi" ] && echo "framework: fastapi"
[ -n "$has_django" ] && echo "framework: django"
return 0
fi
return 1
}
detect_bun() {
# Bun backend (NOT frontend with Bun runtime)
if [ -f "bun.lockb" ]; then
# Check if this is a frontend project
if [ -f "package.json" ]; then
if jq -e '.dependencies.react // .dependencies.vue' package.json >/dev/null 2>&1; then
# This is frontend using Bun runtime, not Bun backend
return 1
fi
fi
# This is a Bun backend project
echo "bunjs"
return 0
fi
return 1
}
Quality checks are automatically determined based on detected stack(s):
react-typescript:
- command: "bun run format"
tool: "Biome"
purpose: "Code formatting"
- command: "bun run lint"
tool: "Biome"
purpose: "Linting"
- command: "bun run typecheck"
tool: "TypeScript"
purpose: "Type checking"
- command: "bun test"
tool: "Vitest"
purpose: "Unit tests"
vue-typescript:
- command: "bun run format"
tool: "Biome"
- command: "bun run lint"
tool: "Biome"
- command: "bun run typecheck"
tool: "TypeScript"
- command: "bun test"
tool: "Vitest"
golang:
- command: "go fmt ./..."
purpose: "Format Go code"
- command: "go vet ./..."
purpose: "Static analysis"
- command: "golangci-lint run"
purpose: "Comprehensive linting"
- command: "go test ./..."
purpose: "Run tests"
dingo:
- command: "dingo fmt"
purpose: "Format Dingo source files"
- command: "dingo go"
purpose: "Transpile Dingo to Go"
- command: "go vet ./.dingo/..."
purpose: "Static analysis on generated Go"
- command: "golangci-lint run ./.dingo/..."
purpose: "Comprehensive linting on generated Go"
- command: "go test ./.dingo/..."
purpose: "Run tests on generated Go"
rust:
- command: "cargo fmt --check"
purpose: "Check formatting"
- command: "cargo clippy -- -D warnings"
purpose: "Linting"
- command: "cargo test"
purpose: "Run tests"
python:
- command: "black --check ."
purpose: "Check formatting"
- command: "ruff check ."
purpose: "Linting"
- command: "mypy ."
purpose: "Type checking"
- command: "pytest"
purpose: "Run tests"
bunjs:
- command: "bun run format"
tool: "Biome"
- command: "bun run lint"
tool: "Biome"
- command: "bun run typecheck"
tool: "TypeScript"
- command: "bun test"
tool: "Bun test runner"
For fullstack projects, run checks for BOTH stacks:
fullstack (react + go):
frontend:
directory: "./frontend"
checks:
- "cd frontend && bun run format"
- "cd frontend && bun run lint"
- "cd frontend && bun run typecheck"
- "cd frontend && bun test"
backend:
directory: "."
checks:
- "go fmt ./..."
- "go vet ./..."
- "golangci-lint run"
- "go test ./..."
Convert detected stacks to skill file paths using ${PLUGIN_ROOT} placeholder:
generate_skill_paths() {
local stacks=("$@")
local skill_paths=(
# Core skills (ALWAYS)
'${PLUGIN_ROOT}/skills/core/universal-patterns/SKILL.md'
'${PLUGIN_ROOT}/skills/core/testing-strategies/SKILL.md'
'${PLUGIN_ROOT}/skills/core/debugging-strategies/SKILL.md'
)
for stack in "${stacks[@]}"; do
case "$stack" in
react-typescript)
skill_paths+=(
'${PLUGIN_ROOT}/skills/frontend/react-typescript/SKILL.md'
'${PLUGIN_ROOT}/skills/frontend/state-management/SKILL.md'
'${PLUGIN_ROOT}/skills/frontend/testing-frontend/SKILL.md'
)
;;
vue-typescript)
skill_paths+=(
'${PLUGIN_ROOT}/skills/frontend/vue-typescript/SKILL.md'
'${PLUGIN_ROOT}/skills/frontend/state-management/SKILL.md'
'${PLUGIN_ROOT}/skills/frontend/testing-frontend/SKILL.md'
)
;;
golang)
skill_paths+=(
'${PLUGIN_ROOT}/skills/backend/golang/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/database-patterns/SKILL.md'
)
;;
dingo)
skill_paths+=(
'${PLUGIN_ROOT}/skills/backend/dingo/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/golang/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/database-patterns/SKILL.md'
)
;;
rust)
skill_paths+=(
'${PLUGIN_ROOT}/skills/backend/rust/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md'
)
;;
python)
skill_paths+=(
'${PLUGIN_ROOT}/skills/backend/python/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md'
)
;;
bunjs)
skill_paths+=(
'${PLUGIN_ROOT}/skills/backend/bunjs/SKILL.md'
'${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md'
)
;;
esac
done
# Print unique paths
printf '%s\n' "${skill_paths[@]}" | sort -u
}
CRITICAL: Always use ${PLUGIN_ROOT} placeholder, NOT hardcoded paths. This placeholder is expanded at runtime to the actual plugin directory.
error: "no_stack_detected"
trigger: "No config files found AND no recognizable directory structure"
recovery:
1. Prompt user for manual stack selection
2. Offer generic "unknown" stack with only core skills
3. Save user choice to ${SESSION_PATH}/context.json
4. Proceed with core skills only
error: "ambiguous_stack"
trigger: "Multiple possible interpretations"
recovery:
1. Present all possibilities to user
2. Ask for confirmation
3. Save confirmed stack to context
error: "incomplete_detection"
trigger: "Found some indicators but missing key files"
recovery:
1. Report partial detection
2. Load available skills
3. Warn user about potential missing skills
Project Structure:
project/
├── package.json (with react: "^19.0.0")
├── src/
│ ├── routes/
│ └── components/
└── .tsx files
Detection Result:
{
"detected_stack": "react-typescript",
"mode": "frontend",
"stacks": ["react-typescript"],
"skill_paths": [
"${PLUGIN_ROOT}/skills/core/universal-patterns/SKILL.md",
"${PLUGIN_ROOT}/skills/core/testing-strategies/SKILL.md",
"${PLUGIN_ROOT}/skills/frontend/react-typescript/SKILL.md",
"${PLUGIN_ROOT}/skills/frontend/state-management/SKILL.md",
"${PLUGIN_ROOT}/skills/frontend/testing-frontend/SKILL.md"
]
}
Project Structure:
project/
├── go.mod
├── cmd/
│ └── server/
└── internal/
Detection Result:
{
"detected_stack": "golang",
"mode": "backend",
"stacks": ["golang"],
"skill_paths": [
"${PLUGIN_ROOT}/skills/core/universal-patterns/SKILL.md",
"${PLUGIN_ROOT}/skills/core/testing-strategies/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/golang/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/database-patterns/SKILL.md"
]
}
Project Structure:
project/
├── frontend/
│ ├── package.json (with react)
│ └── src/
├── go.mod
├── cmd/
└── internal/
Detection Result:
{
"detected_stack": "react-typescript + golang",
"mode": "fullstack",
"stacks": ["react-typescript", "golang"],
"skill_paths": [
"${PLUGIN_ROOT}/skills/core/universal-patterns/SKILL.md",
"${PLUGIN_ROOT}/skills/core/testing-strategies/SKILL.md",
"${PLUGIN_ROOT}/skills/frontend/react-typescript/SKILL.md",
"${PLUGIN_ROOT}/skills/frontend/state-management/SKILL.md",
"${PLUGIN_ROOT}/skills/frontend/testing-frontend/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/golang/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/database-patterns/SKILL.md"
],
"quality_checks": {
"frontend": ["cd frontend && bun run format", "..."],
"backend": ["go fmt ./...", "..."]
}
}
Project Structure:
project/
├── go.mod
├── cmd/
│ └── api/
│ └── main.dingo
├── internal/
│ ├── handlers/
│ │ └── user.dingo
│ └── services/
│ └── user.dingo
└── .dingo/ # Generated .go files (gitignored)
Detection Result:
{
"detected_stack": "dingo + golang",
"mode": "backend",
"stacks": ["dingo", "golang"],
"skill_paths": [
"${PLUGIN_ROOT}/skills/core/universal-patterns/SKILL.md",
"${PLUGIN_ROOT}/skills/core/testing-strategies/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/dingo/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/golang/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/api-design/SKILL.md",
"${PLUGIN_ROOT}/skills/backend/database-patterns/SKILL.md"
],
"quality_checks": {
"backend": [
"dingo fmt",
"dingo go",
"go vet ./.dingo/...",
"golangci-lint run ./.dingo/...",
"go test ./.dingo/..."
]
}
}
The stack-detector agent implements this skill:
Commands read context.json and pass skill paths to implementation agents:
Task: universal-developer
Prompt: |
SESSION_PATH: ${SESSION_PATH}
Read these skills before implementing:
- ${PLUGIN_ROOT}/skills/frontend/react-typescript/SKILL.md
- ${PLUGIN_ROOT}/skills/frontend/testing-frontend/SKILL.md
Then implement: Create user profile component
Agents use Read tool to load skill files:
1. Read skill file paths from prompt
2. Use Read tool to load each skill
3. Extract relevant patterns
4. Apply patterns during implementation
testing
A test skill for validation testing. Use when testing skill parsing and validation logic.
tools
--- name: bad-skill description: This skill has invalid YAML in frontmatter allowed-tools: [invalid, array, syntax prerequisites: not-an-array --- # Bad Skill This skill has malformed frontmatter that should fail parsing. The YAML has: - Unclosed array bracket - Wrong type for prerequisites (should be array, not string)
development
Sync model aliases from the curated Firebase database. Fetches default model assignments, short aliases, team compositions, and known model metadata from the claudish API. Run this to get fresh model recommendations.
tools
Release one or more Magus plugins to the distribution repos (magus, magus-alpha, magus-marketing). Handles version inference from git history, marketplace.json updates, tagging, and force-push to lean dist repos. Use whenever the user says "release kanban", "release the dev plugin", "cut a new version of gtd", "bump kanban to 1.7", or hands you a batch like "release kanban and gtd". Also use for multi-plugin releases and for checking what a release would contain before committing.