Thanks to visit codestin.com
Credit goes to github.com

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
8 changes: 5 additions & 3 deletions staged/src/lib/AgentDropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { Check, ExternalLink, RefreshCw } from 'lucide-svelte';
import Spinner from './Spinner.svelte';
import { agentState, refreshProviders, KNOWN_AGENTS } from './stores/agent.svelte';
import { preferences, setAiAgent } from './stores/preferences.svelte';
import { setAiAgent, getPreferredAgent } from './stores/preferences.svelte';
import { openUrl } from './commands';

interface Props {
Expand All @@ -21,6 +21,8 @@
let dropdownRef = $state<HTMLDivElement | null>(null);
let refreshing = $state(false);

let preferredId = $derived(getPreferredAgent(agentState.providers));

function isInstalled(id: string): boolean {
return agentState.providers.some((p) => p.id === id);
}
Expand Down Expand Up @@ -70,14 +72,14 @@
{#if installed}
<button
class="agent-item"
class:active={preferences.aiAgent === agent.id}
class:active={preferredId === agent.id}
onclick={() => select(agent.id)}
>
<div class="agent-info">
<span class="agent-name">{agent.label}</span>
<span class="agent-status installed">Installed</span>
</div>
{#if preferences.aiAgent === agent.id}
{#if preferredId === agent.id}
<Check size={14} />
{/if}
</button>
Expand Down
12 changes: 6 additions & 6 deletions staged/src/lib/AgentSelector.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { onMount, onDestroy } from 'svelte';
import { ChevronDown, Check, Bot } from 'lucide-svelte';
import { agentState, REMOTE_AGENTS } from './stores/agent.svelte';
import { preferences, setAiAgent } from './stores/preferences.svelte';
import { setAiAgent, getPreferredAgent } from './stores/preferences.svelte';

interface Props {
disabled?: boolean;
Expand All @@ -27,9 +27,9 @@

let agents = $derived(remote ? REMOTE_AGENTS : agentState.providers);

let currentLabel = $derived(
agents.find((p) => p.id === preferences.aiAgent)?.label ?? preferences.aiAgent ?? 'Agent'
);
let preferredId = $derived(getPreferredAgent(agents));

let currentLabel = $derived(agents.find((p) => p.id === preferredId)?.label ?? 'Agent');

onMount(() => {
document.addEventListener('click', handleClickOutside);
Expand Down Expand Up @@ -79,11 +79,11 @@
<button
type="button"
class="selector-option"
class:selected={preferences.aiAgent === provider.id}
class:selected={preferredId === provider.id}
onclick={() => select(provider.id)}
>
<span class="option-label">{provider.label}</span>
{#if preferences.aiAgent === provider.id}
{#if preferredId === provider.id}
<Check size={14} />
{/if}
</button>
Expand Down
6 changes: 4 additions & 2 deletions staged/src/lib/NewSessionModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import type { Branch, BranchSessionType } from './types';
import * as commands from './commands';
import AgentSelector from './AgentSelector.svelte';
import { preferences } from './stores/preferences.svelte';
import { agentState, REMOTE_AGENTS } from './stores/agent.svelte';
import { getPreferredAgent } from './stores/preferences.svelte';

interface Props {
branch: Branch;
Expand Down Expand Up @@ -73,11 +74,12 @@
error = null;

try {
const agents = remote ? REMOTE_AGENTS : agentState.providers;
const result = await commands.startBranchSession(
branch.id,
prompt.trim(),
currentMode,
preferences.aiAgent ?? undefined
getPreferredAgent(agents) ?? undefined
);
onStarted({ sessionId: result.sessionId, artifactId: result.artifactId });
} catch (e) {
Expand Down
9 changes: 7 additions & 2 deletions staged/src/lib/SessionLauncher.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
import { startSession, deleteSession } from './commands';
import SessionModal from './SessionModal.svelte';
import Spinner from './Spinner.svelte';
import { preferences } from './stores/preferences.svelte';
import { agentState } from './stores/agent.svelte';
import { getPreferredAgent } from './stores/preferences.svelte';

interface Props {
onClose: () => void;
Expand Down Expand Up @@ -83,7 +84,11 @@
// We need a working directory — use the user's home dir as a default
// for these standalone debug sessions.
const workingDir = '/tmp';
const s = await startSession(text, workingDir, preferences.aiAgent ?? undefined);
const s = await startSession(
text,
workingDir,
getPreferredAgent(agentState.providers) ?? undefined
);
sessions = [...sessions, s];
prompt = '';
} catch (e) {
Expand Down
52 changes: 42 additions & 10 deletions staged/src/lib/stores/preferences.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ const SIZE_DEFAULT = 13;

const SIZE_STORE_KEY = 'size-base';
const SYNTAX_THEME_STORE_KEY = 'syntax-theme';
const AI_AGENT_STORE_KEY = 'ai-agent';
const RECENT_AGENTS_STORE_KEY = 'recent-agents';
/** Maximum number of recent agents to remember. */
const RECENT_AGENTS_MAX = 10;

const DEFAULT_SYNTAX_THEME: SyntaxThemeName = 'laserwave';

Expand All @@ -58,8 +60,11 @@ export const preferences = $state({
sizeBase: SIZE_DEFAULT,
/** Current syntax theme name */
syntaxTheme: DEFAULT_SYNTAX_THEME as string,
/** Selected AI agent provider ID (e.g. "goose", "claude"). Null = not yet chosen. */
aiAgent: null as string | null,
/**
* Ordered list of recently used AI agent IDs, most-recent first.
* Used to pick the best available agent for a given context (local vs remote).
*/
recentAgents: [] as string[],
/** Whether all preferences have been loaded from storage */
loaded: false,
});
Expand Down Expand Up @@ -114,10 +119,17 @@ export async function initPreferences(): Promise<void> {
await setSyntaxTheme(preferences.syntaxTheme as SyntaxThemeName);
applyAdaptiveTheme();

// Load AI agent preference
const savedAgent = await getStoreValue<string>(AI_AGENT_STORE_KEY);
if (savedAgent) {
preferences.aiAgent = savedAgent;
// Load recent agents list (with migration from legacy single-agent key)
const savedRecent = await getStoreValue<string[]>(RECENT_AGENTS_STORE_KEY);
if (savedRecent && Array.isArray(savedRecent) && savedRecent.length > 0) {
preferences.recentAgents = savedRecent;
} else {
// Migrate from legacy single-agent preference
const legacyAgent = await getStoreValue<string>('ai-agent');
if (legacyAgent) {
preferences.recentAgents = [legacyAgent];
await setStoreValue(RECENT_AGENTS_STORE_KEY, [legacyAgent]);
}
}

preferences.loaded = true;
Expand Down Expand Up @@ -184,9 +196,29 @@ export function resetSize(): void {
// =============================================================================

/**
* Set the preferred AI agent provider.
* Record an agent as the most recently used.
*
* Moves `agentId` to the front of `recentAgents`, removing any prior
* occurrence so the list stays deduplicated. The list is capped at
* RECENT_AGENTS_MAX entries and persisted to disk.
*/
export function setAiAgent(agentId: string): void {
preferences.aiAgent = agentId;
setStoreValue(AI_AGENT_STORE_KEY, agentId);
const filtered = preferences.recentAgents.filter((id) => id !== agentId);
preferences.recentAgents = [agentId, ...filtered].slice(0, RECENT_AGENTS_MAX);
setStoreValue(RECENT_AGENTS_STORE_KEY, preferences.recentAgents);
}

/**
* Return the most recently used agent that is present in `available`.
*
* Walks `recentAgents` in order and returns the first match, so local
* and remote contexts each get the best agent for their environment.
* Returns `null` if no recent agent is available.
*/
export function getPreferredAgent(available: { id: string }[]): string | null {
const ids = new Set(available.map((a) => a.id));
for (const agentId of preferences.recentAgents) {
if (ids.has(agentId)) return agentId;
}
return null;
}