feat: add install command detection to worktree creation
After creating a worktree, detect the project type and suggest the appropriate install command: - Node.js: bun/pnpm/yarn/npm based on lock file - Python: uv/poetry/pip based on lock file - Rust: cargo build - Go: go mod download - Generic: Makefile Custom scripts/setup-worktree.sh takes precedence over auto-detection. Ported from coherence-mcp. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
19602c632b
commit
1afdd05ea6
1 changed files with 105 additions and 2 deletions
|
|
@ -7,11 +7,79 @@
|
||||||
//! - Branch: `feature-description` (number prefix stripped)
|
//! - Branch: `feature-description` (number prefix stripped)
|
||||||
//! - Worktree: `feature-description`
|
//! - Worktree: `feature-description`
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use blue_core::{DocType, ProjectState, Worktree as StoreWorktree};
|
use blue_core::{DocType, ProjectState, Worktree as StoreWorktree};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::error::ServerError;
|
use crate::error::ServerError;
|
||||||
|
|
||||||
|
/// Detect the appropriate install command for a project
|
||||||
|
///
|
||||||
|
/// Checks for package manager lock files and project files in priority order.
|
||||||
|
/// Returns None if a setup script exists (takes precedence) or no package manager detected.
|
||||||
|
fn detect_install_command(path: &Path) -> Option<String> {
|
||||||
|
// Custom setup script takes precedence
|
||||||
|
if path.join("scripts/setup-worktree.sh").exists() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node.js - check lock files for package manager
|
||||||
|
if path.join("package.json").exists() {
|
||||||
|
if path.join("bun.lockb").exists() {
|
||||||
|
return Some("bun install".into());
|
||||||
|
}
|
||||||
|
if path.join("pnpm-lock.yaml").exists() {
|
||||||
|
return Some("pnpm install".into());
|
||||||
|
}
|
||||||
|
if path.join("yarn.lock").exists() {
|
||||||
|
return Some("yarn install".into());
|
||||||
|
}
|
||||||
|
return Some("npm install".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Python
|
||||||
|
if path.join("pyproject.toml").exists() {
|
||||||
|
if path.join("uv.lock").exists() {
|
||||||
|
return Some("uv sync".into());
|
||||||
|
}
|
||||||
|
if path.join("poetry.lock").exists() {
|
||||||
|
return Some("poetry install".into());
|
||||||
|
}
|
||||||
|
return Some("pip install -e .".into());
|
||||||
|
}
|
||||||
|
if path.join("requirements.txt").exists() {
|
||||||
|
return Some("pip install -r requirements.txt".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rust
|
||||||
|
if path.join("Cargo.toml").exists() {
|
||||||
|
return Some("cargo build".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go
|
||||||
|
if path.join("go.mod").exists() {
|
||||||
|
return Some("go mod download".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic Makefile
|
||||||
|
if path.join("Makefile").exists() {
|
||||||
|
return Some("make".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check for a custom setup script
|
||||||
|
fn detect_setup_script(path: &Path) -> Option<String> {
|
||||||
|
let script_path = path.join("scripts/setup-worktree.sh");
|
||||||
|
if script_path.exists() {
|
||||||
|
Some("./scripts/setup-worktree.sh".into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Strip RFC number prefix from title
|
/// Strip RFC number prefix from title
|
||||||
///
|
///
|
||||||
/// Converts `0007-consistent-branch-naming` to `consistent-branch-naming`
|
/// Converts `0007-consistent-branch-naming` to `consistent-branch-naming`
|
||||||
|
|
@ -92,15 +160,50 @@ pub fn handle_create(state: &ProjectState, args: &Value) -> Result<Value, Server
|
||||||
let _ = state.store.update_document_status(DocType::Rfc, title, "in-progress");
|
let _ = state.store.update_document_status(DocType::Rfc, title, "in-progress");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect install command and setup script
|
||||||
|
let install_command = detect_install_command(&worktree_path);
|
||||||
|
let setup_script = detect_setup_script(&worktree_path);
|
||||||
|
|
||||||
|
// Build hint message
|
||||||
|
let setup_hint = if let Some(ref script) = setup_script {
|
||||||
|
format!("Run `{}` to set up.", script)
|
||||||
|
} else if let Some(ref cmd) = install_command {
|
||||||
|
format!("Run `{}` to install dependencies.", cmd)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let hint = format!(
|
||||||
|
"cd {} to start working. {}",
|
||||||
|
worktree_path.display(),
|
||||||
|
setup_hint
|
||||||
|
);
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"title": title,
|
"title": title,
|
||||||
"branch": branch_name,
|
"branch": branch_name,
|
||||||
"path": worktree_path.display().to_string(),
|
"path": worktree_path.display().to_string(),
|
||||||
|
"install_command": install_command,
|
||||||
|
"setup_script": setup_script,
|
||||||
"message": blue_core::voice::success(
|
"message": blue_core::voice::success(
|
||||||
&format!("Created worktree for '{}'", title),
|
&format!("Created worktree for '{}'", title),
|
||||||
Some(&format!("cd {} to start working", worktree_path.display()))
|
Some(&hint.trim())
|
||||||
)
|
),
|
||||||
|
"next_action": {
|
||||||
|
"tool": if setup_script.is_some() || install_command.is_some() {
|
||||||
|
"Bash"
|
||||||
|
} else {
|
||||||
|
"blue_rfc_validate"
|
||||||
|
},
|
||||||
|
"hint": if setup_script.is_some() {
|
||||||
|
"Run setup script to configure the worktree"
|
||||||
|
} else if install_command.is_some() {
|
||||||
|
"Install dependencies before starting work"
|
||||||
|
} else {
|
||||||
|
"Check RFC plan progress as you implement"
|
||||||
|
}
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Err(e) => Ok(json!({
|
Err(e) => Ok(json!({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue