Files
slopus--happy/docs/plans/remove-profiles-wizard-cli-detection.md
wehub-resource-sync 98e40dac97
CLI Smoke Test / smoke-test-linux (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-linux (24) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (24) (push) Has been cancelled
Expo App TypeScript typecheck / typecheck (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:40:49 +08:00

259 lines
12 KiB
Markdown

# Plan: Remove Profiles & Wizard, Move CLI Detection to Daemon
## Context
The profiles feature (AI Backend Profiles) is half-baked — it provides env var management per session via a complex sync/schema system, but it's barely used and adds significant complexity. The wizard (new session creation flow) is tightly coupled to profiles. Both should be ripped out.
Simultaneously, CLI availability detection currently happens on-demand from the app via RPC bash calls (`useCLIDetection` hook). This should move to the daemon, which detects availability at boot and re-checks every 20 seconds via the keep-alive, pushing results as part of `MachineMetadata`. The detection must be cross-platform (POSIX + Windows).
The new session creation screen UI will be implemented separately — this plan only covers the cleanup and data flow changes.
---
## Part 1: Move CLI Detection to Daemon (MachineMetadata)
### 1.1 Add `cliAvailability` to `MachineMetadata` schema
**File:** `packages/happy-cli/src/api/types.ts` (line 130)
Add to `MachineMetadataSchema`:
```typescript
cliAvailability: z.object({
claude: z.boolean(),
codex: z.boolean(),
gemini: z.boolean(),
openclaw: z.boolean(),
detectedAt: z.number(),
}).optional()
```
Using `.optional()` so older daemons without this field still parse fine.
### 1.2 Add matching field to app-side schema
**File:** `packages/happy-app/sources/sync/storageTypes.ts` (line 119)
Add same `cliAvailability` optional field to the app's `MachineMetadataSchema`.
### 1.3 Implement cross-platform CLI detection in the daemon
**File:** `packages/happy-cli/src/daemon/run.ts`
Add a `detectCLIAvailability()` function that:
- Checks `os.platform()` to pick POSIX vs Windows detection
- **POSIX:** Uses `child_process.execSync` with `command -v claude`, `command -v codex`, `command -v gemini`, and the OpenClaw triple-check (command + config file + env var) — same logic as current `useCLIDetection.ts` lines 66-72
- **Windows:** Uses PowerShell `Get-Command` checks — same logic as `useCLIDetection.ts` lines 74-81
- Returns `{ claude: boolean, codex: boolean, gemini: boolean, openclaw: boolean, detectedAt: number }`
- Wraps in try/catch — on failure, returns all `false` with current timestamp
### 1.4 Run detection at daemon boot
**File:** `packages/happy-cli/src/daemon/run.ts` (line 27-34)
Update `initialMachineMetadata` construction to call `detectCLIAvailability()` and include the result:
```typescript
export const initialMachineMetadata: MachineMetadata = {
host: os.hostname(),
platform: os.platform(),
happyCliVersion: packageJson.version,
homeDir: os.homedir(),
happyHomeDir: configuration.happyHomeDir,
happyLibDir: projectPath(),
cliAvailability: detectCLIAvailability(),
};
```
### 1.5 Re-detect every 20 seconds on keep-alive
**File:** `packages/happy-cli/src/api/apiMachine.ts` (line 299-312)
Modify `startKeepAlive()` to also run `detectCLIAvailability()` every 20 seconds. If the result differs from the last known state, call `updateMachineMetadata()` to push the change. This avoids unnecessary metadata updates when nothing changed.
```
startKeepAlive():
every 20s:
emit machine-alive (existing)
newAvailability = detectCLIAvailability()
if (newAvailability differs from lastKnownAvailability):
update machine metadata with new cliAvailability
lastKnownAvailability = newAvailability
```
The `detectCLIAvailability` function needs to be importable from apiMachine.ts — put it in a shared util like `packages/happy-cli/src/utils/detectCLI.ts`.
### 1.6 Delete `useCLIDetection` hook from app
**File:** `packages/happy-app/sources/hooks/useCLIDetection.ts`**DELETE**
The app now reads `machine.metadata.cliAvailability` directly from the machine record (already decrypted and available via `useMachine()`). No RPC bash call needed.
---
## Part 2: Remove Profiles Feature
### 2.1 Files to DELETE entirely
| File | Reason |
|------|--------|
| `packages/happy-app/sources/sync/profileUtils.ts` | Built-in profile definitions & docs |
| `packages/happy-app/sources/sync/profileSync.ts` | Profile sync service |
| `packages/happy-app/sources/components/ProfileEditForm.tsx` | Profile editor form |
| `packages/happy-app/sources/components/EnvironmentVariablesList.tsx` | Env var list component |
| `packages/happy-app/sources/components/EnvironmentVariableCard.tsx` | Env var card component |
| `packages/happy-app/sources/hooks/useEnvironmentVariables.ts` | Queries daemon env vars for profiles |
| `packages/happy-app/sources/hooks/envVarUtils.ts` | Env var substitution utils |
| `packages/happy-app/sources/hooks/useCLIDetection.ts` | Replaced by daemon-side detection |
| `packages/happy-app/sources/app/(app)/settings/profiles.tsx` | Profile settings page |
### 2.2 Files to EDIT — remove profile references
**`packages/happy-app/sources/sync/settings.ts`**
- Remove: `AIBackendProfileSchema`, `AnthropicConfigSchema`, `OpenAIConfigSchema`, `AzureOpenAIConfigSchema`, `TogetherAIConfigSchema`, `TmuxConfigSchema`, `EnvironmentVariableSchema`, `ProfileCompatibilitySchema`
- Remove: `getProfileEnvironmentVariables()`, `validateProfileForAgent()`
- Remove from `SettingsSchema`: `profiles` field, `lastUsedProfile` field
- Keep: `lastUsedAgent`, `lastUsedPermissionMode`, `lastUsedModelMode`, `recentMachinePaths` (still useful)
**`packages/happy-app/sources/components/AgentInput.tsx`**
- Remove: imports from `profileUtils` and `settings` (lines 25-26)
- Remove: `profileId` and `onProfileClick` props (lines 77-78)
- Remove: profile data computation (lines 342-351)
- Remove: profile selector button UI (lines 991-1022)
**`packages/happy-app/sources/components/SettingsView.tsx`**
- Remove: profiles settings Item (lines 325-330)
**`packages/happy-app/sources/sync/ops.ts`**
- Remove `environmentVariables` from `SpawnSessionOptions` interface entirely
- Remove `environmentVariables` from the RPC params type in `machineSpawnNewSession()`
- The daemon only uses its own process.env + auth tokens
**`packages/happy-cli/src/persistence.ts`**
- Remove: `AIBackendProfileSchema` duplicate, `validateProfileForAgent()`, `getProfileEnvironmentVariables()`, `readSettings()` profile-related code, `activeProfileId` handling
**`packages/happy-cli/src/daemon/run.ts`**
- Remove: `getProfileEnvironmentVariablesForAgent()` function (lines 37-65)
- Remove: Layer 2 profile env var logic in `spawnSession()` (lines 293-324) — simplify to just auth env + daemon's process.env
- The env merge becomes: `{ ...authEnv }` only, expanded against `process.env`
---
## Part 3: Remove Wizard
### 3.1 Files to DELETE entirely
| File | Reason |
|------|--------|
| `packages/happy-app/sources/app/(app)/new/index.tsx` | Main wizard page — will be replaced with new simpler screen |
| `packages/happy-app/sources/app/(app)/new/pick/machine.tsx` | Machine picker sub-screen (uses SearchableListSelector with favorites) |
| `packages/happy-app/sources/app/(app)/new/pick/path.tsx` | Path picker sub-screen (uses SearchableListSelector with favorites) |
| `packages/happy-app/sources/app/(app)/new/pick/profile-edit.tsx` | Profile edit sub-screen |
| `packages/happy-app/sources/components/NewSessionWizard.tsx` | Legacy wizard component |
| `packages/happy-app/sources/utils/tempDataStore.ts` | Temp data between wizard screens |
Note: `SearchableListSelector` component itself is NOT deleted — it's a generic reusable component. Only its wizard-specific usages (favorites, double-section layout) go away. The new session screen's machine/path pickers will use simpler UI (implemented separately).
### 3.2 Files to EDIT — remove wizard references
**`packages/happy-app/sources/app/(app)/_layout.tsx`**
- Remove: Stack.Screen entries for `new/index`, `new/pick/machine`, `new/pick/path`, `new/pick/profile-edit` (lines 300-327)
**`packages/happy-app/sources/components/MainView.tsx`** — keep `router.push('/new')` (route stays the same)
**`packages/happy-app/sources/components/SidebarView.tsx`** — keep `router.push('/new')`
**`packages/happy-app/sources/components/HomeHeader.tsx`** — keep `router.push('/new')`
**`packages/happy-app/sources/components/CommandPalette/CommandPaletteProvider.tsx`** — keep `router.push('/new')`
**`packages/happy-app/sources/components/EmptySessionsTablet.tsx`** — keep `router.push('/new')`
All navigation stays at `/new` — no route changes needed.
**`packages/happy-app/sources/sync/persistence.ts`**
- KEEP: `NewSessionDraft` type, `loadNewSessionDraft()`, `saveNewSessionDraft()`, `clearNewSessionDraft()` — useful for the new simpler session screen too (stores machine, path, agent, permissions)
**`packages/happy-app/sources/sync/settings.ts`**
- Remove: `useEnhancedSessionWizard` from settings
- Remove: `favoriteDirectories` — only used by wizard's SearchableListSelector
- Remove: `favoriteMachines` — only used by wizard's SearchableListSelector
### 3.3 Translations cleanup
Remove from ALL language files (`en.ts`, `ru.ts`, `pl.ts`, `es.ts`, `ca.ts`, `it.ts`, `pt.ts`, `ja.ts`, `zh-Hans.ts`):
- `profiles.*` section entirely
- `newSession.*` section entirely
- `settings.profiles` and `settings.profilesSubtitle` keys
- `settingsFeatures.enhancedSessionWizard*` keys
---
## Part 4: Placeholder for New Session Route
Since the new UI will be implemented separately, create a minimal placeholder:
**`packages/happy-app/sources/app/(app)/new/index.tsx`** — replace with stub that:
- Shows "New Session" header
- Has the `AgentInput` composer at the bottom (reuse existing component)
- Reads `lastUsedAgent`, `lastUsedPermissionMode`, `lastUsedModelMode` from settings for defaults
- Calls `machineSpawnNewSession()` with machineId + directory + agent
- This is a temporary bridge — the full UI from the mockup will be built later
---
## Data Flow: Before vs After
### BEFORE (Current)
```
APP (wizard) DAEMON
├─ User picks machine
├─ useCLIDetection() ──RPC bash──→ ├─ runs `command -v` checks
│ ← parses stdout ────────────── │
├─ User picks agent (filtered)
├─ User picks profile
├─ getProfileEnvironmentVariables()
├─ machineSpawnNewSession({
│ machineId, dir, agent,
│ environmentVariables })
│ ──RPC──────────────────────────→ ├─ receives env vars
│ ├─ merges: profileEnv + authEnv
│ ├─ expands ${VAR} refs
│ ├─ spawns agent with merged env
│ └─
```
### AFTER (New)
```
DAEMON BOOT
├─ detectCLIAvailability()
├─ include in MachineMetadata
├─ POST /v1/machines (or update)
DAEMON KEEP-ALIVE (every 20s)
├─ emit machine-alive
├─ re-detect CLI availability
├─ if changed → machine-update-metadata
APP (new session screen)
├─ Read machine.metadata.cliAvailability (already there, no RPC)
├─ User picks agent (filtered by availability)
├─ machineSpawnNewSession({
│ machineId, dir, agent })
│ ──RPC──────────────────────────→ DAEMON
│ ├─ auth env only (no profiles)
│ ├─ spawns with process.env + authEnv
│ └─
```
---
## Verification
1. **Typecheck:** `yarn typecheck` in `happy-app` and `happy-cli` — must pass with no errors
2. **Daemon boot:** Start daemon, verify `initialMachineMetadata` includes `cliAvailability` in logs
3. **Keep-alive re-detection:** Install/uninstall a CLI tool, verify metadata updates within 20s
4. **App reads availability:** Open app, select a machine, verify availability shows from metadata (no bash RPC)
5. **Session spawn:** Create session from app — verify it spawns without profile env vars, using daemon's process.env
6. **Cross-platform:** Test detection commands on macOS (POSIX) — Windows testing if available
7. **No orphaned data:** Verify old `profiles` key in MMKV is ignored (schema no longer reads it)
8. **Navigation:** Verify all FAB/sidebar/header buttons navigate to the new session route