diff --git a/CLAUDE.md b/CLAUDE.md index 12129459..7ebcf35d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ When user says "COM": 1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`) 2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web` -**Version**: 0.1551 (must match `package.json` for npm publish) +**Version**: 0.1552 (must match `package.json` for npm publish) ## Project Overview @@ -124,13 +124,13 @@ journalctl --user -u claudeman-web -f | `src/file-stream-manager.ts` | Manages `tail -f` processes for live log viewing | | `src/plan-orchestrator.ts` | Multi-agent plan generation with research and planning phases | | `src/prompts/index.ts` | Barrel export for all agent prompts | -| `src/prompts/*.ts` | Agent prompts (research-agent, code-reviewer, planner) | +| `src/prompts/*.ts` | Agent prompts (research-agent, planner) | | `src/templates/claude-md.ts` | CLAUDE.md generation for new cases | | `src/cli.ts` | Command-line interface handlers | -| `src/web/server.ts` | Fastify REST API + SSE at `/api/events` (~90 routes) | +| `src/web/server.ts` | Fastify REST API + SSE at `/api/events` (~99 routes) | | `src/web/schemas.ts` | Zod v4 validation schemas with path/env security allowlists | | `src/web/public/app.js` | Frontend: xterm.js, tab management, subagent windows, mobile support (~16K lines) | -| `src/types.ts` | All TypeScript interfaces (~100 types, ~1500 lines) | +| `src/types.ts` | All TypeScript interfaces (~100 types, ~1400 lines) | **Large files** (>50KB): `app.js`, `ralph-tracker.ts`, `respawn-controller.ts`, `session.ts`, `subagent-watcher.ts` — these contain complex state machines; read `docs/respawn-state-machine.md` before modifying. @@ -232,7 +232,7 @@ The frontend is a single 16K-line vanilla JS file with these key systems: ### API Route Categories -~90 routes in `server.ts:buildServer()`. Key groups: +~99 routes in `server.ts:buildServer()`. Key groups: | Group | Prefix | Count | Key endpoints | |-------|--------|-------|---------------| @@ -295,6 +295,8 @@ npx vitest run -t "pattern" **Respawn tests**: Use MockSession from `test/respawn-test-utils.ts` to avoid spawning real Claude processes. +**Mobile tests**: Separate Playwright-based suite in `mobile-test/` with 135 device profiles. Run via `npx vitest run --config mobile-test/vitest.config.ts`. See `mobile-test/README.md`. + ## Screenshots ("sc") When the user says "check the sc", "screenshot", or "sc", they mean uploaded screenshots from their mobile device. Screenshots are saved to `~/.claudeman/screenshots/` and uploaded via `/upload.html` on the Claudeman web UI. To view them, use the Read tool on the image files: @@ -402,6 +404,9 @@ Use `LRUMap` for bounded caches with eviction, `StaleExpirationMap` for TTL-base | **Mobile testing** | `docs/mobile-testing-report.md` | | **Run summary design** | `docs/run-summary-plan.md` | | **Performance audit** | `docs/perf-audit-first-load.md` | +| **First-load optimization** | `docs/first-load-optimization-plan.md` | +| **Dead code audit** | `docs/cleanup-findings.md` | +| **Mobile test suite** | `mobile-test/README.md` | ## Scripts diff --git a/package.json b/package.json index 68b093dd..f1078326 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1551", + "version": "0.1552", "description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/web/public/app.js b/src/web/public/app.js index 4849c6b7..632290e1 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -970,6 +970,12 @@ class LocalEchoOverlay { } } + // Re-render overlay at current prompt position without clearing pending text. + // Used after terminal resets/redraws that move the prompt. + rerender() { + if (this.pendingText) this._render(); + } + get hasPending() { return this.pendingText.length > 0; } dispose() { @@ -2337,6 +2343,12 @@ class ClaudemanApp { if (this._wasAtBottomBeforeWrite) { this.terminal.scrollToBottom(); } + + // Re-position local echo overlay after terminal writes — Ink redraws can + // move the ❯ prompt to a different row, making the overlay invisible. + if (this._localEchoOverlay?.hasPending) { + this._localEchoOverlay.rerender(); + } } /** @@ -2646,6 +2658,8 @@ class ClaudemanApp { this.terminal.reset(); await this.chunkedTerminalWrite(data.terminalBuffer); this.terminal.scrollToBottom(); + // Re-position local echo overlay at new prompt location + this._localEchoOverlay?.rerender(); } } catch (err) { console.error('needsRefresh reload failed:', err); @@ -2672,6 +2686,8 @@ class ClaudemanApp { // Send resize to ensure proper dimensions await this.sendResize(data.id); + // Re-position local echo overlay at new prompt location + this._localEchoOverlay?.rerender(); } catch (err) { console.error('clearTerminal refresh failed:', err); } @@ -3717,8 +3733,10 @@ class ClaudemanApp { this.writeFrameScheduled = false; this._isLoadingBuffer = false; this._loadBufferQueue = null; - // Clear local echo overlay on SSE reconnect (re-enable via _updateLocalEchoState) - this._localEchoOverlay?.clear(); + // Preserve local echo overlay text across SSE reconnect — just hide until + // terminal buffer reloads and prompt is visible again. _render() re-scans + // for the ❯ prompt on every call, so rerender() after buffer load repositions it. + this._localEchoOverlay?.rerender(); // Clear pending hooks this.pendingHooks.clear(); // Clear subagent activity/results maps (prevents leaks if data.subagents is missing) @@ -3864,6 +3882,8 @@ class ClaudemanApp { // Guard: skip if a newer handleInit has already started (race between loadState + SSE init). if (gen !== this._initGeneration) return; const previousActiveId = this.activeSessionId; + // Save local echo pending text before selectSession clears it + const savedEchoText = this._localEchoOverlay?.pendingText || ''; this.activeSessionId = null; if (this.sessionOrder.length > 0) { // Priority: current active > localStorage > first session @@ -3877,6 +3897,14 @@ class ClaudemanApp { this.selectSession(this.sessionOrder[0]); } } + // Restore local echo text that was cleared by selectSession during SSE reconnect. + // selectSession loads the terminal buffer, so we schedule rerender after it settles. + if (savedEchoText && this._localEchoOverlay) { + this._localEchoOverlay.pendingText = savedEchoText; + this._localEchoOverlay._persist(); + // Delay rerender until buffer load completes and prompt is visible + setTimeout(() => this._localEchoOverlay?.rerender(), 500); + } } async loadState() {