Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 0 additions & 54 deletions staged/src-tauri/src/actions/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,60 +91,6 @@ fn resolve_branch_repo_context(
Ok((repo.to_string(), project.subpath.clone()))
}

/// Detect available actions from a project's build files using AI.
///
/// If a local clone already exists on disk we read files from the filesystem.
/// Otherwise we use the GitHub API (via `gh`) to inspect the repository
/// remotely, avoiding an expensive clone just for action detection.
#[tauri::command]
pub async fn detect_project_actions(
project_id: String,
app: AppHandle,
store: State<'_, Mutex<Option<Arc<Store>>>>,
) -> Result<Vec<SuggestedAction>, String> {
let store = get_store(&store)?;

// Get the project
let project = store
.get_project(&project_id)
.map_err(|e| format!("Failed to get project: {e}"))?
.ok_or_else(|| "Project not found".to_string())?;
let github_repo = project
.primary_repo()
.ok_or_else(|| "Project has no repository attached".to_string())?;

let context = store
.get_or_create_action_context(github_repo, project.subpath.as_deref())
.map_err(|e| format!("Failed to get action context: {e}"))?;
store
.set_action_context_detecting(&context.id, true)
.map_err(|e| format!("Failed to set detection status: {e}"))?;
let _ = app.emit(
"repo-actions-detection",
DetectingActionsEvent {
github_repo: github_repo.to_string(),
subpath: project.subpath.clone(),
detecting: true,
},
);

let result = detect_actions_for_repo_context(github_repo, project.subpath.as_deref()).await;

store
.mark_action_context_detected(&context.id)
.map_err(|e| format!("Failed to update detection status: {e}"))?;
let _ = app.emit(
"repo-actions-detection",
DetectingActionsEvent {
github_repo: github_repo.to_string(),
subpath: project.subpath.clone(),
detecting: false,
},
);

result
}

/// Detect available actions for a specific repo+subpath context using AI.
#[tauri::command(rename_all = "camelCase")]
pub async fn detect_repo_actions(
Expand Down
39 changes: 13 additions & 26 deletions staged/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,40 +696,29 @@ fn get_or_create_project_action_context(
.map_err(|e| e.to_string())
}

#[tauri::command]
#[tauri::command(rename_all = "camelCase")]
fn list_project_actions(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
project_id: String,
project_repo_id: Option<String>,
) -> Result<Vec<store::models::RepoAction>, String> {
let store = get_store(&store)?;
let context = get_or_create_project_action_context(&store, &project_id)?;
let context = if let Some(repo_id) = project_repo_id {
let repo = store
.get_project_repo(&repo_id)
.map_err(|e| e.to_string())?
.ok_or_else(|| format!("Project repo not found: {repo_id}"))?;
store
.get_or_create_action_context(&repo.github_repo, repo.subpath.as_deref())
.map_err(|e| e.to_string())?
} else {
get_or_create_project_action_context(&store, &project_id)?
};
store
.list_repo_actions(&context.id)
.map_err(|e| e.to_string())
}

#[tauri::command(rename_all = "camelCase")]
fn create_project_action(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
project_id: String,
name: String,
command: String,
action_type: String,
sort_order: i32,
auto_commit: bool,
) -> Result<store::models::RepoAction, String> {
let store = get_store(&store)?;
let context = get_or_create_project_action_context(&store, &project_id)?;
let parsed_type = builderbot_actions::ActionType::parse(&action_type)
.ok_or_else(|| format!("Invalid action type: {action_type}"))?;
let action = store::models::RepoAction::new(context.id, name, command, parsed_type, sort_order)
.with_auto_commit(auto_commit);
store
.create_repo_action(&action)
.map_err(|e| e.to_string())?;
Ok(action)
}

#[tauri::command(rename_all = "camelCase")]
fn update_project_action(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
Expand Down Expand Up @@ -2349,7 +2338,6 @@ pub fn run() {
branches::get_workspace_info,
branches::poll_workspace_status,
list_project_actions,
create_project_action,
update_project_action,
delete_project_action,
list_action_contexts,
Expand Down Expand Up @@ -2390,7 +2378,6 @@ pub fn run() {
session_commands::delete_session,
session_commands::start_branch_session,
// Actions
actions::commands::detect_project_actions,
actions::commands::detect_repo_actions,
actions::commands::run_branch_action,
actions::commands::stop_branch_action,
Expand Down
14 changes: 13 additions & 1 deletion staged/src-tauri/src/session_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,19 @@ pub async fn start_branch_session(
.ok_or_else(|| format!("No worktree for branch: {branch_id}"))?;

let mut worktree_path = PathBuf::from(&workdir.path);
if let Some(ref subpath) = project.subpath {
// Use the project_repo's subpath when the branch is attached to a specific
// repo (e.g. a secondary repo with no subpath), rather than always falling
// back to the project-level subpath which may belong to a different repo.
let effective_subpath = if let Some(repo_id) = branch.project_repo_id.as_deref() {
store
.get_project_repo(repo_id)
.ok()
.flatten()
.and_then(|repo| repo.subpath)
} else {
project.subpath.clone()
};
if let Some(ref subpath) = effective_subpath {
worktree_path = worktree_path.join(subpath);
}

Expand Down
23 changes: 4 additions & 19 deletions staged/src/lib/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,26 +307,11 @@ export interface ProjectAction {
updatedAt: number;
}

export function listProjectActions(projectId: string): Promise<ProjectAction[]> {
return invoke('list_project_actions', { projectId });
}

export function createProjectAction(
export function listProjectActions(
projectId: string,
name: string,
command: string,
actionType: string,
sortOrder: number,
autoCommit: boolean
): Promise<ProjectAction> {
return invoke('create_project_action', {
projectId,
name,
command,
actionType,
sortOrder,
autoCommit,
});
projectRepoId?: string | null
): Promise<ProjectAction[]> {
return invoke('list_project_actions', { projectId, projectRepoId: projectRepoId ?? null });
}

export function updateProjectAction(
Expand Down
8 changes: 0 additions & 8 deletions staged/src/lib/features/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,6 @@ export interface RunningActionInfo {
startedAt: number;
}

/**
* Detect available actions from a project's build files using AI.
* Scans package.json, justfile, Makefile, etc. and suggests relevant actions.
*/
export function detectProjectActions(projectId: string): Promise<SuggestedAction[]> {
return invoke<SuggestedAction[]>('detect_project_actions', { projectId });
}

/** Detect available actions for a repo+subpath context. */
export function detectRepoActions(
githubRepo: string,
Expand Down
2 changes: 1 addition & 1 deletion staged/src/lib/features/branches/BranchCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@
async function loadActions() {
try {
// Load actions for this branch's project
actions = await commands.listProjectActions(branch.projectId);
actions = await commands.listProjectActions(branch.projectId, branch.projectRepoId);
} catch (e) {
console.error('Failed to load actions:', e);
actions = [];
Expand Down