279 lines
12 KiB
Markdown
279 lines
12 KiB
Markdown
# Auto Language Initialization
|
|
|
|
Automatic language detection and configuration that runs once per session to set up environment variables for all subsequent BMAD tasks.
|
|
|
|
[[LLM: This task runs automatically on first BMAD command to detect project language and configure all subsequent tasks]]
|
|
|
|
## Auto-Initialization System
|
|
|
|
### 1. **Session-Based Auto-Detection** (50-100 tokens)
|
|
|
|
```bash
|
|
# Auto-initialize language environment if not already done
|
|
auto_init_language_environment() {
|
|
local CACHE_FILE="tmp/bmad-session.json"
|
|
|
|
# Check if already initialized this session
|
|
if [ -f "$CACHE_FILE" ]; then
|
|
SESSION_AGE=$(jq -r '.initialized_at // "1970-01-01"' "$CACHE_FILE")
|
|
if [ "$(date -d "$SESSION_AGE" +%s)" -gt "$(date -d '2 hours ago' +%s)" ]; then
|
|
# Load cached environment variables
|
|
source <(jq -r '.environment | to_entries[] | "export " + .key + "=\"" + .value + "\""' "$CACHE_FILE")
|
|
echo "🔄 Using cached language environment: $BMAD_PRIMARY_LANGUAGE"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
echo "🔍 Auto-detecting project language..."
|
|
|
|
# Rapid language detection
|
|
PROJECT_DIR="${1:-.}"
|
|
PRIMARY_LANGUAGE="unknown"
|
|
BUILD_COMMAND="echo 'No build system detected'"
|
|
TEST_COMMAND="echo 'No test system detected'"
|
|
SIMULATION_PATTERNS="TODO|FIXME|HACK"
|
|
ERROR_PATTERNS="error:|Error:"
|
|
COMPONENT_PATTERNS="[A-Z][a-zA-Z]*Service|[A-Z][a-zA-Z]*Controller|[A-Z][a-zA-Z]*Repository"
|
|
FILE_EXTENSIONS="*.*"
|
|
|
|
# Multi-tier detection strategy for new/existing projects
|
|
|
|
# Tier 1: Config files (most reliable)
|
|
if [ -f "$PROJECT_DIR/package.json" ]; then
|
|
if grep -q '"typescript":\|"@types/\|"ts-' "$PROJECT_DIR/package.json" || [ -f "$PROJECT_DIR/tsconfig.json" ]; then
|
|
PRIMARY_LANGUAGE="typescript"
|
|
BUILD_COMMAND="npm run build 2>/dev/null || tsc --noEmit"
|
|
TEST_COMMAND="npm test"
|
|
SIMULATION_PATTERNS="Math\.random|jest\.fn|sinon\.|TODO|FIXME"
|
|
ERROR_PATTERNS="TS[0-9]{4}|error TS"
|
|
FILE_EXTENSIONS="*.ts|*.tsx"
|
|
else
|
|
PRIMARY_LANGUAGE="javascript"
|
|
BUILD_COMMAND="npm run build 2>/dev/null || echo 'No build step'"
|
|
TEST_COMMAND="npm test"
|
|
SIMULATION_PATTERNS="Math\.random|jest\.fn|sinon\.|TODO|FIXME"
|
|
ERROR_PATTERNS="Error:|SyntaxError:"
|
|
FILE_EXTENSIONS="*.js|*.jsx"
|
|
fi
|
|
elif ls "$PROJECT_DIR"/*.csproj >/dev/null 2>&1 || [ -f "$PROJECT_DIR"/*.sln ]; then
|
|
PRIMARY_LANGUAGE="csharp"
|
|
BUILD_COMMAND="dotnet build --verbosity quiet"
|
|
TEST_COMMAND="dotnet test --verbosity quiet"
|
|
SIMULATION_PATTERNS="Random\.NextDouble|Task\.FromResult|NotImplementedException|Mock\.|Fake\.|Stub\."
|
|
ERROR_PATTERNS="CS[0-9]{4}"
|
|
FILE_EXTENSIONS="*.cs"
|
|
elif [ -f "$PROJECT_DIR/pom.xml" ] || [ -f "$PROJECT_DIR/build.gradle" ]; then
|
|
PRIMARY_LANGUAGE="java"
|
|
BUILD_COMMAND="mvn compile -q 2>/dev/null || gradle build -q"
|
|
TEST_COMMAND="mvn test -q 2>/dev/null || gradle test -q"
|
|
SIMULATION_PATTERNS="Math\.random|Mockito\.|@Mock|TODO|FIXME"
|
|
ERROR_PATTERNS="error:"
|
|
FILE_EXTENSIONS="*.java"
|
|
elif [ -f "$PROJECT_DIR/Cargo.toml" ]; then
|
|
PRIMARY_LANGUAGE="rust"
|
|
BUILD_COMMAND="cargo build --quiet"
|
|
TEST_COMMAND="cargo test --quiet"
|
|
SIMULATION_PATTERNS="todo!|unimplemented!|panic!|TODO|FIXME"
|
|
ERROR_PATTERNS="error\[E[0-9]{4}\]"
|
|
FILE_EXTENSIONS="*.rs"
|
|
elif [ -f "$PROJECT_DIR/go.mod" ]; then
|
|
PRIMARY_LANGUAGE="go"
|
|
BUILD_COMMAND="go build ./..."
|
|
TEST_COMMAND="go test ./..."
|
|
SIMULATION_PATTERNS="rand\.|mock\.|TODO|FIXME"
|
|
ERROR_PATTERNS="cannot find package|undefined:"
|
|
FILE_EXTENSIONS="*.go"
|
|
elif [ -f "$PROJECT_DIR/requirements.txt" ] || [ -f "$PROJECT_DIR/pyproject.toml" ] || [ -f "$PROJECT_DIR/setup.py" ]; then
|
|
PRIMARY_LANGUAGE="python"
|
|
BUILD_COMMAND="python -m py_compile *.py 2>/dev/null || echo 'Syntax check complete'"
|
|
TEST_COMMAND="python -m pytest"
|
|
SIMULATION_PATTERNS="random\.|mock\.|Mock\.|TODO|FIXME"
|
|
ERROR_PATTERNS="SyntaxError:|IndentationError:|NameError:"
|
|
FILE_EXTENSIONS="*.py"
|
|
elif [ -f "$PROJECT_DIR/Gemfile" ]; then
|
|
PRIMARY_LANGUAGE="ruby"
|
|
BUILD_COMMAND="ruby -c *.rb 2>/dev/null || echo 'Ruby syntax check'"
|
|
TEST_COMMAND="bundle exec rspec"
|
|
SIMULATION_PATTERNS="rand|mock|double|TODO|FIXME"
|
|
ERROR_PATTERNS="SyntaxError:|NameError:"
|
|
FILE_EXTENSIONS="*.rb"
|
|
elif [ -f "$PROJECT_DIR/composer.json" ]; then
|
|
PRIMARY_LANGUAGE="php"
|
|
BUILD_COMMAND="php -l *.php 2>/dev/null || echo 'PHP syntax check'"
|
|
TEST_COMMAND="vendor/bin/phpunit"
|
|
SIMULATION_PATTERNS="rand|mock|TODO|FIXME"
|
|
ERROR_PATTERNS="Parse error:|Fatal error:"
|
|
FILE_EXTENSIONS="*.php"
|
|
|
|
# Tier 2: File extension analysis (for new projects)
|
|
elif find "$PROJECT_DIR" -maxdepth 3 -name "*.ts" -o -name "*.tsx" | head -1 | grep -q .; then
|
|
PRIMARY_LANGUAGE="typescript"
|
|
BUILD_COMMAND="tsc --noEmit 2>/dev/null || echo 'TypeScript check (install: npm i -g typescript)'"
|
|
TEST_COMMAND="npm test 2>/dev/null || echo 'Install test framework'"
|
|
SIMULATION_PATTERNS="Math\.random|jest\.fn|TODO|FIXME"
|
|
ERROR_PATTERNS="TS[0-9]{4}|error TS"
|
|
FILE_EXTENSIONS="*.ts|*.tsx"
|
|
echo "💡 New TypeScript project detected - consider: npm init && npm install typescript"
|
|
elif find "$PROJECT_DIR" -maxdepth 3 -name "*.cs" | head -1 | grep -q .; then
|
|
PRIMARY_LANGUAGE="csharp"
|
|
BUILD_COMMAND="dotnet build --verbosity quiet 2>/dev/null || echo 'C# files found (install: dotnet CLI)'"
|
|
TEST_COMMAND="dotnet test --verbosity quiet"
|
|
SIMULATION_PATTERNS="Random\.NextDouble|Task\.FromResult|NotImplementedException"
|
|
ERROR_PATTERNS="CS[0-9]{4}"
|
|
FILE_EXTENSIONS="*.cs"
|
|
echo "💡 New C# project detected - consider: dotnet new console/webapi/classlib"
|
|
elif find "$PROJECT_DIR" -maxdepth 3 -name "*.java" | head -1 | grep -q .; then
|
|
PRIMARY_LANGUAGE="java"
|
|
BUILD_COMMAND="javac *.java 2>/dev/null || echo 'Java files found (setup: mvn/gradle)'"
|
|
TEST_COMMAND="mvn test 2>/dev/null || echo 'Setup Maven/Gradle'"
|
|
SIMULATION_PATTERNS="Math\.random|TODO|FIXME"
|
|
ERROR_PATTERNS="error:"
|
|
FILE_EXTENSIONS="*.java"
|
|
echo "💡 New Java project detected - consider: mvn archetype:generate"
|
|
elif find "$PROJECT_DIR" -maxdepth 3 -name "*.rs" | head -1 | grep -q .; then
|
|
PRIMARY_LANGUAGE="rust"
|
|
BUILD_COMMAND="rustc --version >/dev/null 2>&1 && echo 'Rust files found' || echo 'Install Rust toolchain'"
|
|
TEST_COMMAND="cargo test 2>/dev/null || echo 'Run: cargo init'"
|
|
SIMULATION_PATTERNS="todo!|unimplemented!|TODO"
|
|
ERROR_PATTERNS="error\[E[0-9]{4}\]"
|
|
FILE_EXTENSIONS="*.rs"
|
|
echo "💡 New Rust project detected - consider: cargo init"
|
|
elif find "$PROJECT_DIR" -maxdepth 3 -name "*.go" | head -1 | grep -q .; then
|
|
PRIMARY_LANGUAGE="go"
|
|
BUILD_COMMAND="go version >/dev/null 2>&1 && echo 'Go files found' || echo 'Install Go'"
|
|
TEST_COMMAND="go test ./... 2>/dev/null || echo 'Run: go mod init'"
|
|
SIMULATION_PATTERNS="TODO|FIXME"
|
|
ERROR_PATTERNS="undefined:|cannot find"
|
|
FILE_EXTENSIONS="*.go"
|
|
echo "💡 New Go project detected - consider: go mod init"
|
|
elif find "$PROJECT_DIR" -maxdepth 3 -name "*.py" | head -1 | grep -q .; then
|
|
PRIMARY_LANGUAGE="python"
|
|
BUILD_COMMAND="python -m py_compile *.py 2>/dev/null || echo 'Python files found'"
|
|
TEST_COMMAND="python -m pytest 2>/dev/null || echo 'Install: pip install pytest'"
|
|
SIMULATION_PATTERNS="random\.|TODO|FIXME"
|
|
ERROR_PATTERNS="SyntaxError:|NameError:"
|
|
FILE_EXTENSIONS="*.py"
|
|
echo "💡 New Python project detected - consider: pip install -r requirements.txt"
|
|
elif find "$PROJECT_DIR" -maxdepth 3 -name "*.js" -o -name "*.jsx" | head -1 | grep -q .; then
|
|
PRIMARY_LANGUAGE="javascript"
|
|
BUILD_COMMAND="node --version >/dev/null 2>&1 && echo 'JavaScript files found' || echo 'Install Node.js'"
|
|
TEST_COMMAND="npm test 2>/dev/null || echo 'Run: npm init'"
|
|
SIMULATION_PATTERNS="Math\.random|TODO|FIXME"
|
|
ERROR_PATTERNS="Error:|SyntaxError:"
|
|
FILE_EXTENSIONS="*.js|*.jsx"
|
|
echo "💡 New JavaScript project detected - consider: npm init"
|
|
|
|
# Tier 3: Directory/filename hints (empty projects)
|
|
elif [ -d "$PROJECT_DIR/src/main/java" ] || [ -d "$PROJECT_DIR/app/src/main/java" ]; then
|
|
PRIMARY_LANGUAGE="java"
|
|
BUILD_COMMAND="echo 'Java project structure detected - setup Maven/Gradle'"
|
|
TEST_COMMAND="echo 'Setup Maven: mvn archetype:generate'"
|
|
SIMULATION_PATTERNS="TODO|FIXME"
|
|
ERROR_PATTERNS="error:"
|
|
FILE_EXTENSIONS="*.java"
|
|
echo "💡 Java project structure detected - run: mvn archetype:generate"
|
|
elif [ -d "$PROJECT_DIR/src" ] && [ ! -f "$PROJECT_DIR/package.json" ] && [ ! -f "$PROJECT_DIR/*.csproj" ]; then
|
|
PRIMARY_LANGUAGE="generic"
|
|
BUILD_COMMAND="echo 'Generic project with src/ folder detected'"
|
|
TEST_COMMAND="echo 'Setup appropriate build system'"
|
|
SIMULATION_PATTERNS="TODO|FIXME|HACK"
|
|
ERROR_PATTERNS="error:|Error:"
|
|
FILE_EXTENSIONS="*.*"
|
|
echo "💡 Generic project structure - specify language manually if needed"
|
|
fi
|
|
|
|
# Cache environment for session
|
|
mkdir -p tmp
|
|
cat > "$CACHE_FILE" << EOF
|
|
{
|
|
"initialized_at": "$(date -Iseconds)",
|
|
"environment": {
|
|
"BMAD_PRIMARY_LANGUAGE": "$PRIMARY_LANGUAGE",
|
|
"BMAD_BUILD_COMMAND": "$BUILD_COMMAND",
|
|
"BMAD_TEST_COMMAND": "$TEST_COMMAND",
|
|
"BMAD_SIMULATION_PATTERNS": "$SIMULATION_PATTERNS",
|
|
"BMAD_ERROR_PATTERNS": "$ERROR_PATTERNS",
|
|
"BMAD_COMPONENT_PATTERNS": "$COMPONENT_PATTERNS",
|
|
"BMAD_FILE_EXTENSIONS": "$FILE_EXTENSIONS"
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Export environment variables for current session
|
|
export BMAD_PRIMARY_LANGUAGE="$PRIMARY_LANGUAGE"
|
|
export BMAD_BUILD_COMMAND="$BUILD_COMMAND"
|
|
export BMAD_TEST_COMMAND="$TEST_COMMAND"
|
|
export BMAD_SIMULATION_PATTERNS="$SIMULATION_PATTERNS"
|
|
export BMAD_ERROR_PATTERNS="$ERROR_PATTERNS"
|
|
export BMAD_COMPONENT_PATTERNS="$COMPONENT_PATTERNS"
|
|
export BMAD_FILE_EXTENSIONS="$FILE_EXTENSIONS"
|
|
|
|
echo "✅ Language environment initialized: $PRIMARY_LANGUAGE"
|
|
}
|
|
|
|
# Call auto-initialization (runs automatically when this task is loaded)
|
|
auto_init_language_environment
|
|
```
|
|
|
|
## Integration Method
|
|
|
|
### 2. **Automatic Task Wrapper**
|
|
|
|
Instead of individual tasks calling language detection, each optimized task starts with:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Auto-initialize language environment if needed
|
|
if [ -z "$BMAD_PRIMARY_LANGUAGE" ]; then
|
|
Read tool: bmad-core/tasks/auto-language-init.md
|
|
fi
|
|
|
|
# Now use language-specific variables directly
|
|
echo "🔍 Smart Build Context Analysis ($BMAD_PRIMARY_LANGUAGE)"
|
|
BUILD_OUTPUT=$($BMAD_BUILD_COMMAND 2>&1)
|
|
# ... rest of task logic
|
|
```
|
|
|
|
### 3. **Agent-Level Auto-Initialization**
|
|
|
|
Add to both dev and qa agent startup:
|
|
|
|
```yaml
|
|
session_initialization:
|
|
- auto_init_language_environment # Runs once per agent session
|
|
|
|
enhanced_commands:
|
|
- "*smart-build-context" # Uses pre-initialized environment
|
|
- "*smart-reality-audit" # Uses pre-initialized environment
|
|
- "*smart-story-mapping" # Uses pre-initialized environment
|
|
```
|
|
|
|
## Execution Flow
|
|
|
|
### **How It Works in Practice:**
|
|
|
|
```bash
|
|
# User runs: *smart-reality-audit story.md
|
|
|
|
1. Agent starts executing smart-reality-audit task
|
|
2. Task checks: "Is BMAD_PRIMARY_LANGUAGE set?"
|
|
3. If not: Runs auto-language-init.md (50-100 tokens, once per session)
|
|
4. If yes: Skips initialization (0 tokens)
|
|
5. Task uses $BMAD_BUILD_COMMAND, $BMAD_SIMULATION_PATTERNS directly
|
|
6. All subsequent tasks in session use cached environment (0 additional tokens)
|
|
```
|
|
|
|
### **Token Usage:**
|
|
- **First task in session**: 50-100 tokens for initialization
|
|
- **All subsequent tasks**: 0 additional tokens (uses cached environment)
|
|
- **Session reuse**: Environment cached for 2 hours
|
|
|
|
## Benefits of This Approach
|
|
|
|
✅ **Fully Automatic** - No manual commands needed
|
|
✅ **Session Efficient** - Initialize once, use everywhere
|
|
✅ **Zero Integration Overhead** - Tasks just check environment variables
|
|
✅ **Language Agnostic** - Works with any supported language
|
|
✅ **Minimal Token Cost** - 50-100 tokens per session vs per task
|
|
|
|
This makes language adaptation **completely transparent** to the user while maintaining all optimization benefits! |