diff --git a/packages/app/app.json b/packages/app/app.json
index 626f76be4..227587664 100644
--- a/packages/app/app.json
+++ b/packages/app/app.json
@@ -24,9 +24,7 @@
"android": {
"adaptiveIcon": {
"backgroundColor": "#E6F4FE",
- "foregroundImage": "./assets/images/android-icon-foreground.png",
- "backgroundImage": "./assets/images/android-icon-background.png",
- "monochromeImage": "./assets/images/android-icon-monochrome.png"
+ "foregroundImage": "./assets/images/android-icon-foreground.png"
},
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false,
diff --git a/packages/app/assets/images/android-icon-foreground-backup.png b/packages/app/assets/images/android-icon-foreground-backup.png
new file mode 100644
index 000000000..ae5077dee
Binary files /dev/null and b/packages/app/assets/images/android-icon-foreground-backup.png differ
diff --git a/packages/app/assets/images/android-icon-foreground.png b/packages/app/assets/images/android-icon-foreground.png
index 27c94262e..d5fe93ef4 100644
Binary files a/packages/app/assets/images/android-icon-foreground.png and b/packages/app/assets/images/android-icon-foreground.png differ
diff --git a/packages/app/assets/images/splash-icon.png b/packages/app/assets/images/splash-icon.png
index 03d6f6b6c..ae5077dee 100644
Binary files a/packages/app/assets/images/splash-icon.png and b/packages/app/assets/images/splash-icon.png differ
diff --git a/packages/app/src/components/create-agent-modal.tsx b/packages/app/src/components/create-agent-modal.tsx
index be2c31650..e58706356 100644
--- a/packages/app/src/components/create-agent-modal.tsx
+++ b/packages/app/src/components/create-agent-modal.tsx
@@ -13,6 +13,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
import { X } from "lucide-react-native";
import { StyleSheet } from "react-native-unistyles";
import { theme as defaultTheme } from "@/styles/theme";
+import { useRecentPaths } from "@/hooks/use-recent-paths";
interface CreateAgentModalProps {
isVisible: boolean;
@@ -40,6 +41,7 @@ export function CreateAgentModal({
}: CreateAgentModalProps) {
const insets = useSafeAreaInsets();
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
+ const { recentPaths, addRecentPath } = useRecentPaths();
const [workingDir, setWorkingDir] = useState("");
const [selectedMode, setSelectedMode] = useState("plan");
@@ -56,13 +58,23 @@ export function CreateAgentModal({
};
});
- function handleCreate() {
+ async function handleCreate() {
if (!workingDir.trim()) {
setErrorMessage("Working directory is required");
return;
}
- onCreateAgent(workingDir.trim(), selectedMode);
+ const path = workingDir.trim();
+
+ // Save to recent paths
+ try {
+ await addRecentPath(path);
+ } catch (error) {
+ console.error("[CreateAgentModal] Failed to save recent path:", error);
+ // Continue anyway - don't block agent creation
+ }
+
+ onCreateAgent(path, selectedMode);
handleClose();
}
@@ -123,6 +135,28 @@ export function CreateAgentModal({
Absolute path to the project directory
)}
+
+ {/* Recent Paths Chips */}
+ {recentPaths.length > 0 && (
+
+ {recentPaths.map((path) => (
+ setWorkingDir(path)}
+ >
+
+ {path}
+
+
+ ))}
+
+ )}
{/* Mode Selector */}
@@ -339,4 +373,23 @@ const styles = StyleSheet.create((theme) => ({
fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold,
},
+ recentPathsContainer: {
+ flexDirection: "row",
+ gap: theme.spacing[2],
+ paddingVertical: theme.spacing[3],
+ },
+ recentPathChip: {
+ backgroundColor: theme.colors.muted,
+ borderRadius: theme.borderRadius.full,
+ paddingHorizontal: theme.spacing[4],
+ paddingVertical: theme.spacing[2],
+ borderWidth: theme.borderWidth[1],
+ borderColor: theme.colors.border,
+ maxWidth: 200,
+ },
+ recentPathChipText: {
+ color: theme.colors.foreground,
+ fontSize: theme.fontSize.sm,
+ fontWeight: theme.fontWeight.medium,
+ },
}));
diff --git a/packages/app/src/components/empty-state.tsx b/packages/app/src/components/empty-state.tsx
index 361ebc3c7..f1a135f33 100644
--- a/packages/app/src/components/empty-state.tsx
+++ b/packages/app/src/components/empty-state.tsx
@@ -12,11 +12,9 @@ export function EmptyState({ onCreateAgent }: EmptyStateProps) {
return (
Hammock
-
- What would you like to work on?
-
+ What would you like to work on?
-
+
New agent
diff --git a/packages/app/src/components/headers/home-header.tsx b/packages/app/src/components/headers/home-header.tsx
index c9219efb9..ebe9ce124 100644
--- a/packages/app/src/components/headers/home-header.tsx
+++ b/packages/app/src/components/headers/home-header.tsx
@@ -34,10 +34,7 @@ export function HomeHeader({ onCreateAgent }: HomeHeaderProps) {
>
-
+
@@ -74,6 +71,5 @@ const styles = StyleSheet.create((theme) => ({
iconButton: {
padding: theme.spacing[3],
borderRadius: theme.borderRadius.lg,
- backgroundColor: theme.colors.muted,
},
}));
diff --git a/packages/app/src/hooks/use-recent-paths.ts b/packages/app/src/hooks/use-recent-paths.ts
new file mode 100644
index 000000000..1b049823e
--- /dev/null
+++ b/packages/app/src/hooks/use-recent-paths.ts
@@ -0,0 +1,68 @@
+import { useState, useEffect, useCallback } from 'react';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+
+const STORAGE_KEY = '@voice-dev:recent-paths';
+const MAX_RECENT_PATHS = 3;
+
+export interface UseRecentPathsReturn {
+ recentPaths: string[];
+ isLoading: boolean;
+ addRecentPath: (path: string) => Promise;
+ clearRecentPaths: () => Promise;
+}
+
+export function useRecentPaths(): UseRecentPathsReturn {
+ const [recentPaths, setRecentPaths] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+
+ // Load recent paths from AsyncStorage on mount
+ useEffect(() => {
+ loadRecentPaths();
+ }, []);
+
+ async function loadRecentPaths() {
+ try {
+ const stored = await AsyncStorage.getItem(STORAGE_KEY);
+ if (stored) {
+ const parsed = JSON.parse(stored) as string[];
+ setRecentPaths(parsed);
+ }
+ } catch (error) {
+ console.error('[RecentPaths] Failed to load recent paths:', error);
+ // Continue with empty array
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ const addRecentPath = useCallback(async (path: string) => {
+ try {
+ // Remove duplicates and add to front
+ const filtered = recentPaths.filter((p) => p !== path);
+ const updated = [path, ...filtered].slice(0, MAX_RECENT_PATHS);
+
+ setRecentPaths(updated);
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
+ } catch (error) {
+ console.error('[RecentPaths] Failed to save recent path:', error);
+ throw error;
+ }
+ }, [recentPaths]);
+
+ const clearRecentPaths = useCallback(async () => {
+ try {
+ setRecentPaths([]);
+ await AsyncStorage.removeItem(STORAGE_KEY);
+ } catch (error) {
+ console.error('[RecentPaths] Failed to clear recent paths:', error);
+ throw error;
+ }
+ }, []);
+
+ return {
+ recentPaths,
+ isLoading,
+ addRecentPath,
+ clearRecentPaths,
+ };
+}
diff --git a/packages/server/src/server/acp/agent-manager.ts b/packages/server/src/server/acp/agent-manager.ts
index 336010427..eabaeed04 100644
--- a/packages/server/src/server/acp/agent-manager.ts
+++ b/packages/server/src/server/acp/agent-manager.ts
@@ -15,6 +15,7 @@ import {
type WriteTextFileResponse,
} from "@agentclientprotocol/sdk";
import { v4 as uuidv4 } from "uuid";
+import { expandTilde } from "../terminal-mcp/tmux.js";
import type {
AgentStatus,
AgentInfo,
@@ -113,7 +114,7 @@ export class AgentManager {
*/
async createAgent(options: CreateAgentOptions): Promise {
const agentId = uuidv4();
- const cwd = options.cwd;
+ const cwd = expandTilde(options.cwd);
// Validate that the working directory exists
try {
diff --git a/packages/server/src/server/terminal-mcp/tmux.ts b/packages/server/src/server/terminal-mcp/tmux.ts
index d12a527c1..31a912764 100644
--- a/packages/server/src/server/terminal-mcp/tmux.ts
+++ b/packages/server/src/server/terminal-mcp/tmux.ts
@@ -380,7 +380,7 @@ export async function createSession(name: string): Promise {
/**
* Expand tilde in path to home directory
*/
-function expandTilde(path: string): string {
+export function expandTilde(path: string): string {
if (path.startsWith("~/")) {
const homeDir = process.env.HOME || os.homedir();
return path.replace("~", homeDir);