feat: custom commands (#162)

This commit is contained in:
Carlos Florêncio
2026-02-03 01:56:56 +00:00
committed by GitHub
parent 4eaf467950
commit e474c0c0b5
21 changed files with 586 additions and 224 deletions
+12 -2
View File
@@ -23,8 +23,18 @@ export async function listWorkspaces(options?: ApiRequestOptions) {
}
// Repository operations
export async function listRepositories(workspaceId: string, options?: ApiRequestOptions) {
return fetchJson<ListRepositoriesResponse>(`/api/v1/workspaces/${workspaceId}/repositories`, options);
export async function listRepositories(
workspaceId: string,
params?: { includeScripts?: boolean },
options?: ApiRequestOptions
) {
const searchParams = new URLSearchParams();
if (params?.includeScripts) {
searchParams.set('include_scripts', 'true');
}
const queryString = searchParams.toString();
const url = `/api/v1/workspaces/${workspaceId}/repositories${queryString ? `?${queryString}` : ''}`;
return fetchJson<ListRepositoriesResponse>(url, options);
}
export async function listRepositoryBranches(repositoryId: string, options?: ApiRequestOptions) {
+22
View File
@@ -1,5 +1,27 @@
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
// Session Storage helpers (cleared when browser tab closes)
export function getSessionStorage<T extends JsonValue>(key: string, fallback: T): T {
if (typeof window === 'undefined') return fallback;
try {
const raw = window.sessionStorage.getItem(key);
if (!raw) return fallback;
return JSON.parse(raw) as T;
} catch {
return fallback;
}
}
export function setSessionStorage<T extends JsonValue>(key: string, value: T): void {
if (typeof window === 'undefined') return;
try {
window.sessionStorage.setItem(key, JSON.stringify(value));
} catch {
// Ignore write failures (storage full, blocked, etc.)
}
}
// Local Storage helpers (persists across browser sessions)
export function getLocalStorage<T extends JsonValue>(key: string, fallback: T): T {
if (typeof window === 'undefined') return fallback;
try {
+1 -1
View File
@@ -27,7 +27,7 @@ export type { UISlice, UISliceState, UISliceActions } from './ui/types';
// Re-export commonly used types from each domain
export type { KanbanState, BoardState, TaskState } from './kanban/types';
export type { WorkspaceState, RepositoriesState, RepositoryBranchesState } from './workspace/types';
export type { WorkspaceState, RepositoriesState, RepositoryBranchesState, RepositoryScriptsState } from './workspace/types';
export type {
ExecutorsState,
EnvironmentsState,
+10 -1
View File
@@ -1,4 +1,4 @@
import type { Repository, Branch } from '@/lib/types/http';
import type { Repository, Branch, RepositoryScript } from '@/lib/types/http';
export type WorkspaceState = {
items: Array<{
@@ -27,10 +27,17 @@ export type RepositoryBranchesState = {
loadedByRepositoryId: Record<string, boolean>;
};
export type RepositoryScriptsState = {
itemsByRepositoryId: Record<string, RepositoryScript[]>;
loadingByRepositoryId: Record<string, boolean>;
loadedByRepositoryId: Record<string, boolean>;
};
export type WorkspaceSliceState = {
workspaces: WorkspaceState;
repositories: RepositoriesState;
repositoryBranches: RepositoryBranchesState;
repositoryScripts: RepositoryScriptsState;
};
export type WorkspaceSliceActions = {
@@ -40,6 +47,8 @@ export type WorkspaceSliceActions = {
setRepositoriesLoading: (workspaceId: string, loading: boolean) => void;
setRepositoryBranches: (repositoryId: string, branches: Branch[]) => void;
setRepositoryBranchesLoading: (repositoryId: string, loading: boolean) => void;
setRepositoryScripts: (repositoryId: string, scripts: RepositoryScript[]) => void;
setRepositoryScriptsLoading: (repositoryId: string, loading: boolean) => void;
};
export type WorkspaceSlice = WorkspaceSliceState & WorkspaceSliceActions;
@@ -5,6 +5,7 @@ export const defaultWorkspaceState: WorkspaceSliceState = {
workspaces: { items: [], activeId: null },
repositories: { itemsByWorkspaceId: {}, loadingByWorkspaceId: {}, loadedByWorkspaceId: {} },
repositoryBranches: { itemsByRepositoryId: {}, loadingByRepositoryId: {}, loadedByRepositoryId: {} },
repositoryScripts: { itemsByRepositoryId: {}, loadingByRepositoryId: {}, loadedByRepositoryId: {} },
};
export const createWorkspaceSlice: StateCreator<
@@ -49,4 +50,14 @@ export const createWorkspaceSlice: StateCreator<
set((draft) => {
draft.repositoryBranches.loadingByRepositoryId[repositoryId] = loading;
}),
setRepositoryScripts: (repositoryId, scripts) =>
set((draft) => {
draft.repositoryScripts.itemsByRepositoryId[repositoryId] = scripts;
draft.repositoryScripts.loadingByRepositoryId[repositoryId] = false;
draft.repositoryScripts.loadedByRepositoryId[repositoryId] = true;
}),
setRepositoryScriptsLoading: (repositoryId, loading) =>
set((draft) => {
draft.repositoryScripts.loadingByRepositoryId[repositoryId] = loading;
}),
});
+8 -1
View File
@@ -1,7 +1,7 @@
import { createStore } from 'zustand/vanilla';
import { immer } from 'zustand/middleware/immer';
import { hydrateState, type HydrationOptions } from './hydration/hydrator';
import type { Repository, Branch, Message, Turn, TaskSession } from '@/lib/types/http';
import type { Repository, Branch, RepositoryScript, Message, Turn, TaskSession } from '@/lib/types/http';
import {
createKanbanSlice,
createWorkspaceSlice,
@@ -49,6 +49,7 @@ export type {
WorkspaceState,
RepositoriesState,
RepositoryBranchesState,
RepositoryScriptsState,
ExecutorsState,
EnvironmentsState,
SettingsAgentsState,
@@ -103,6 +104,7 @@ export type AppState = {
workspaces: typeof defaultWorkspaceState['workspaces'];
repositories: typeof defaultWorkspaceState['repositories'];
repositoryBranches: typeof defaultWorkspaceState['repositoryBranches'];
repositoryScripts: typeof defaultWorkspaceState['repositoryScripts'];
// Settings slice
executors: typeof defaultSettingsState['executors'];
@@ -164,6 +166,8 @@ export type AppState = {
setRepositoriesLoading: (workspaceId: string, loading: boolean) => void;
setRepositoryBranches: (repositoryId: string, branches: Branch[]) => void;
setRepositoryBranchesLoading: (repositoryId: string, loading: boolean) => void;
setRepositoryScripts: (repositoryId: string, scripts: RepositoryScript[]) => void;
setRepositoryScriptsLoading: (repositoryId: string, loading: boolean) => void;
setSettingsData: (next: Partial<SettingsDataState>) => void;
setEditors: (editors: EditorsState['items']) => void;
setEditorsLoading: (loading: boolean) => void;
@@ -256,6 +260,7 @@ const defaultState = {
workspaces: defaultWorkspaceState.workspaces,
repositories: defaultWorkspaceState.repositories,
repositoryBranches: defaultWorkspaceState.repositoryBranches,
repositoryScripts: defaultWorkspaceState.repositoryScripts,
executors: defaultSettingsState.executors,
environments: defaultSettingsState.environments,
settingsAgents: defaultSettingsState.settingsAgents,
@@ -306,6 +311,7 @@ function mergeInitialState(initialState?: Partial<AppState>): typeof defaultStat
workspaces: { ...defaultState.workspaces, ...initialState.workspaces },
repositories: { ...defaultState.repositories, ...initialState.repositories },
repositoryBranches: { ...defaultState.repositoryBranches, ...initialState.repositoryBranches },
repositoryScripts: { ...defaultState.repositoryScripts, ...initialState.repositoryScripts },
executors: { ...defaultState.executors, ...initialState.executors },
environments: { ...defaultState.environments, ...initialState.environments },
settingsAgents: { ...defaultState.settingsAgents, ...initialState.settingsAgents },
@@ -369,6 +375,7 @@ export function createAppStore(initialState?: Partial<AppState>) {
workspaces: merged.workspaces,
repositories: merged.repositories,
repositoryBranches: merged.repositoryBranches,
repositoryScripts: merged.repositoryScripts,
executors: merged.executors,
environments: merged.environments,
settingsAgents: merged.settingsAgents,
+1
View File
@@ -140,6 +140,7 @@ export type Repository = {
provider_owner: string;
provider_name: string;
default_branch: string;
scripts?: RepositoryScript[];
worktree_branch_prefix: string;
pull_before_worktree: boolean;
setup_script: string;