From dbab535915b135ba437ec7c827b816717911a25b Mon Sep 17 00:00:00 2001 From: Lukas May Date: Fri, 30 Jan 2026 21:19:22 +0100 Subject: [PATCH] docs(07): create phase plan Phase 07: File System UI - 4 plans in 3 waves - 2 parallel (07-02, 07-03 in Wave 2) - Requirements: FSUI-01, FSUI-02, FSUI-03, FSUI-04 - Ready for execution --- .../phases/07-file-system-ui/07-01-PLAN.md | 139 +++++++++ .../phases/07-file-system-ui/07-02-PLAN.md | 160 +++++++++++ .../phases/07-file-system-ui/07-03-PLAN.md | 211 ++++++++++++++ .../phases/07-file-system-ui/07-04-PLAN.md | 268 ++++++++++++++++++ 4 files changed, 778 insertions(+) create mode 100644 .planning/phases/07-file-system-ui/07-01-PLAN.md create mode 100644 .planning/phases/07-file-system-ui/07-02-PLAN.md create mode 100644 .planning/phases/07-file-system-ui/07-03-PLAN.md create mode 100644 .planning/phases/07-file-system-ui/07-04-PLAN.md diff --git a/.planning/phases/07-file-system-ui/07-01-PLAN.md b/.planning/phases/07-file-system-ui/07-01-PLAN.md new file mode 100644 index 0000000..7e5239f --- /dev/null +++ b/.planning/phases/07-file-system-ui/07-01-PLAN.md @@ -0,0 +1,139 @@ +--- +phase: 07-file-system-ui +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: [src/filesync/types.ts, src/filesync/index.ts, src/events/types.ts] +autonomous: true +--- + + +Define FileSystemSync port interface and file sync events following hexagonal architecture. + +Purpose: Establish the foundation for bidirectional SQLite-filesystem sync with typed events. +Output: FileSystemSync port interface, filesync events, exports. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +# Prior patterns to follow: +@src/events/types.ts +@src/coordination/types.ts +@src/db/repositories/message-repository.ts + + + + + + Task 1: Define filesync events + src/events/types.ts + +Add file sync events to DomainEventMap following existing patterns: + +1. FileSyncedEvent (filesync:synced) - File synced to/from filesystem + - payload: { messageId, direction: 'db-to-fs' | 'fs-to-db', filePath } + +2. FileSyncConflictEvent (filesync:conflict) - Sync conflict detected + - payload: { messageId, filePath, dbTimestamp, fsTimestamp } + +3. FileSyncErrorEvent (filesync:error) - Sync operation failed + - payload: { messageId?, filePath, error: string } + +4. FileWatcherStartedEvent (filesync:watcher-started) - Watcher began monitoring + - payload: { directory: string } + +5. FileWatcherStoppedEvent (filesync:watcher-stopped) - Watcher stopped + - payload: { directory: string } + +Add all five to DomainEventMap union type. + + tsc --noEmit passes + Five filesync events added to DomainEventMap with typed payloads + + + + Task 2: Create FileSystemSync port interface + src/filesync/types.ts + +Create src/filesync/ directory and types.ts with: + +1. MessageFileFormat interface: + - messageId: string + - agentName: string + - messageType: 'question' | 'info' | 'error' | 'response' + - requiresResponse: boolean + - status: 'pending' | 'read' | 'responded' + - parentMessageId?: string + - content: string (markdown body) + - createdAt: Date + - updatedAt: Date + +2. SyncDirection type: 'db-to-fs' | 'fs-to-db' + +3. SyncResult interface: + - success: boolean + - direction: SyncDirection + - messageId: string + - filePath: string + - error?: string + +4. FileSystemSync port interface: + - start(): Promise - Start watching and initial sync + - stop(): Promise - Stop watching + - syncToFilesystem(messageId: string): Promise - Sync single message DB→FS + - syncFromFilesystem(filePath: string): Promise - Sync single file FS→DB + - syncAll(): Promise - Full bidirectional sync + - getWatcherStatus(): { running: boolean; directory: string } + +Use Message type from db/schema for reference but keep interface independent. + + tsc --noEmit passes + FileSystemSync port interface defined with sync operations + + + + Task 3: Create module exports + src/filesync/index.ts + +Create index.ts with re-exports: + +```typescript +export * from './types.js'; +``` + +Simple barrel export for now - adapter will be added in 07-02/07-03. + + tsc --noEmit passes, import { FileSystemSync } from './src/filesync/index.js' works + Module exports configured + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] Five filesync events in DomainEventMap +- [ ] FileSystemSync port interface defined +- [ ] Types importable from src/filesync/index.ts + + + + +- All tasks completed +- All verification checks pass +- No errors or warnings introduced +- Port interface follows existing patterns (EventBus, WorktreeManager, CoordinationManager) + + + +After completion, create `.planning/phases/07-file-system-ui/07-01-SUMMARY.md` + diff --git a/.planning/phases/07-file-system-ui/07-02-PLAN.md b/.planning/phases/07-file-system-ui/07-02-PLAN.md new file mode 100644 index 0000000..acfc116 --- /dev/null +++ b/.planning/phases/07-file-system-ui/07-02-PLAN.md @@ -0,0 +1,160 @@ +--- +phase: 07-file-system-ui +plan: 02 +type: execute +wave: 2 +depends_on: ["07-01"] +files_modified: [src/filesync/writer.ts, src/filesync/writer.test.ts, src/filesync/index.ts] +autonomous: true +--- + + +Implement message file writer for DB→FS sync direction (FSUI-02). + +Purpose: Agent messages automatically appear as files in designated directory. +Output: MessageFileWriter class that creates/updates/deletes message files from DB state. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +# Depends on 07-01: +@src/filesync/types.ts +@src/events/types.ts + +# Message schema for reference: +@src/db/schema.ts +@src/db/repositories/message-repository.ts + + + + + + Task 1: Create MessageFileWriter class + src/filesync/writer.ts + +Create MessageFileWriter class that handles DB→FS direction: + +1. Constructor takes: + - messagesDir: string (e.g., '.cw/messages') + - eventBus?: EventBus (optional for events) + +2. Methods: + - writeMessage(message: Message, agentName: string): Promise + - Create/update file at {messagesDir}/{agentName}-{messageId}.md + - Format: YAML frontmatter + markdown body (see format below) + - Emit filesync:synced event on success + - Emit filesync:error event on failure + + - deleteMessage(messageId: string, agentName: string): Promise + - Delete file if exists + - No error if file doesn't exist (idempotent) + + - writeAllMessages(messages: Array<{ message: Message; agentName: string }>): Promise + - Batch write for initial sync + + - ensureDirectory(): Promise + - Create messagesDir if not exists + +3. File format (YAML frontmatter + markdown): +``` +--- +messageId: abc123 +agentName: gastown +type: question +requiresResponse: true +status: pending +parentMessageId: null +createdAt: 2026-01-30T12:00:00.000Z +updatedAt: 2026-01-30T12:00:00.000Z +--- + +What color should the button be? +``` + +4. Use js-yaml for YAML serialization (add to dependencies if needed). + Use gray-matter for frontmatter parsing (already common pattern). + If neither available, use simple string template (YAML is simple enough). + +5. File naming: {agentName}-{messageId}.md + - Human-readable agent name for easy identification + - messageId for uniqueness + + tsc --noEmit passes + MessageFileWriter class created with write/delete operations + + + + Task 2: Add unit tests + src/filesync/writer.test.ts + +Create comprehensive tests for MessageFileWriter: + +1. Setup: + - Use tmp directory for test isolation + - Create mock EventBus for event verification + - Create test message objects + +2. Test cases: + - writeMessage creates file with correct format + - writeMessage updates existing file + - writeMessage emits filesync:synced event + - deleteMessage removes file + - deleteMessage is idempotent (no error if file missing) + - writeAllMessages batch creates files + - ensureDirectory creates directory if missing + - Error cases emit filesync:error event + +3. Verify file content: + - Parse written file, check frontmatter fields match + - Check markdown body matches content + +Use vitest patterns from existing tests (e.g., src/coordination/manager.test.ts). + + npm test -- src/filesync/writer.test.ts passes + Writer tests pass, verify file format correctness + + + + Task 3: Update module exports + src/filesync/index.ts + +Add MessageFileWriter to exports: + +```typescript +export * from './types.js'; +export * from './writer.js'; +``` + + import { MessageFileWriter } from './src/filesync/index.js' works + Writer exported from module + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] `npm test -- src/filesync/writer.test.ts` passes +- [ ] File format is correct YAML frontmatter + markdown +- [ ] Events emitted correctly + + + + +- All tasks completed +- All verification checks pass +- No errors or warnings introduced +- FSUI-02 requirement satisfied (agent messages appear as files) + + + +After completion, create `.planning/phases/07-file-system-ui/07-02-SUMMARY.md` + diff --git a/.planning/phases/07-file-system-ui/07-03-PLAN.md b/.planning/phases/07-file-system-ui/07-03-PLAN.md new file mode 100644 index 0000000..faa59ec --- /dev/null +++ b/.planning/phases/07-file-system-ui/07-03-PLAN.md @@ -0,0 +1,211 @@ +--- +phase: 07-file-system-ui +plan: 03 +type: execute +wave: 2 +depends_on: ["07-01"] +files_modified: [src/filesync/watcher.ts, src/filesync/parser.ts, src/filesync/watcher.test.ts, src/filesync/index.ts, package.json] +autonomous: true +--- + + +Implement file watcher and parser for FS→DB sync direction (FSUI-03, FSUI-04). + +Purpose: User can respond to agents by editing/creating files, changes sync to DB. +Output: FileWatcher class using chokidar, MessageFileParser for reading files. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +# Depends on 07-01: +@src/filesync/types.ts +@src/events/types.ts + +# Message repository for DB updates: +@src/db/repositories/message-repository.ts + + + + + + Task 1: Install chokidar + package.json + +Add chokidar for file watching: + +```bash +npm install chokidar +npm install -D @types/chokidar # if needed, check if bundled +``` + +chokidar is the de facto standard for Node.js file watching - cross-platform, +handles edge cases like rapid changes, atomic writes, etc. + + npm ls chokidar shows installed + chokidar added to dependencies + + + + Task 2: Create MessageFileParser + src/filesync/parser.ts + +Create MessageFileParser for reading message files: + +1. parseFile(filePath: string): Promise + - Read file content + - Parse YAML frontmatter (between --- markers) + - Extract markdown body + - Return null if file format invalid (log warning) + +2. serializeToFile(data: MessageFileFormat): string + - Convert MessageFileFormat to file content string + - YAML frontmatter + markdown body + - Used by writer (shared logic) + +3. extractAgentNameFromPath(filePath: string): string | null + - Parse {agentName}-{messageId}.md pattern + - Return agentName or null if pattern doesn't match + +4. extractMessageIdFromPath(filePath: string): string | null + - Parse {agentName}-{messageId}.md pattern + - Return messageId or null if pattern doesn't match + +Use simple string parsing for YAML - format is predictable and simple. +Don't need full YAML parser for this specific format. + + tsc --noEmit passes + Parser created with read/serialize operations + + + + Task 3: Create FileWatcher class + src/filesync/watcher.ts + +Create FileWatcher class that monitors message directory: + +1. Constructor takes: + - messagesDir: string + - messageRepository: MessageRepository + - eventBus?: EventBus + - debounceMs?: number (default 100ms) + +2. Methods: + - start(): Promise + - Initialize chokidar watcher on messagesDir + - Watch for: add, change, unlink events + - Emit filesync:watcher-started event + + - stop(): Promise + - Close chokidar watcher + - Emit filesync:watcher-stopped event + + - isRunning(): boolean + +3. Event handlers: + - onFileAdded(filePath: string) + - Parse file with MessageFileParser + - If messageId exists in DB, skip (file was created by writer) + - If new file (user response), create message in DB + - Emit filesync:synced event + + - onFileChanged(filePath: string) + - Parse file + - Update message in DB with new content/status + - Emit filesync:synced event + - Use debounce to avoid rapid fire during saves + + - onFileRemoved(filePath: string) + - Optional: Update message status or delete from DB + - For v1: Just log, don't delete from DB (safer) + +4. Loop prevention: + - Track "in-flight syncs" - set of messageIds currently being written + - When writer writes file, add messageId to set + - When watcher sees change, check if messageId in set + - If in set, skip (change came from us) + - Clear from set after debounce period + +5. Use chokidar options: + - persistent: true + - ignoreInitial: false (process existing files on start) + - awaitWriteFinish: { stabilityThreshold: 100 } (wait for writes to complete) + + tsc --noEmit passes + FileWatcher created with chokidar integration + + + + Task 4: Add unit tests + src/filesync/watcher.test.ts + +Create tests for FileWatcher and MessageFileParser: + +1. MessageFileParser tests: + - parseFile returns correct MessageFileFormat + - parseFile returns null for invalid format + - serializeToFile creates correct string + - extractAgentNameFromPath works for valid paths + - extractMessageIdFromPath works for valid paths + +2. FileWatcher tests: + - start() emits watcher-started event + - stop() emits watcher-stopped event + - onFileChanged updates message in DB + - Loop prevention skips in-flight syncs + - Invalid files are ignored (logged but not error) + +Use tmp directory for test isolation. +Create test files programmatically. +Mock MessageRepository for DB operations. + + npm test -- src/filesync/watcher.test.ts passes + Watcher and parser tests pass + + + + Task 5: Update module exports + src/filesync/index.ts + +Add new exports: + +```typescript +export * from './types.js'; +export * from './writer.js'; +export * from './parser.js'; +export * from './watcher.js'; +``` + + Imports work from src/filesync/index.js + All filesync components exported + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] `npm test -- src/filesync/` passes all tests +- [ ] chokidar installed and working +- [ ] Loop prevention verified in tests + + + + +- All tasks completed +- All verification checks pass +- No errors or warnings introduced +- FSUI-03 requirement satisfied (user can respond via files) +- FSUI-04 requirement satisfied (file watcher detects edits) + + + +After completion, create `.planning/phases/07-file-system-ui/07-03-SUMMARY.md` + diff --git a/.planning/phases/07-file-system-ui/07-04-PLAN.md b/.planning/phases/07-file-system-ui/07-04-PLAN.md new file mode 100644 index 0000000..ef095f3 --- /dev/null +++ b/.planning/phases/07-file-system-ui/07-04-PLAN.md @@ -0,0 +1,268 @@ +--- +phase: 07-file-system-ui +plan: 04 +type: execute +wave: 3 +depends_on: ["07-02", "07-03"] +files_modified: [src/filesync/manager.ts, src/filesync/manager.test.ts, src/trpc/router.ts, src/cli/index.ts, src/server/index.ts, src/filesync/index.ts] +autonomous: true +--- + + +Create FileSystemSyncManager adapter, tRPC procedures, and CLI commands (FSUI-01). + +Purpose: Wire filesync into server, expose via API, enable CLI control. +Output: Complete bidirectional sync system with tRPC and CLI integration. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +# Depends on 07-02 and 07-03: +@src/filesync/types.ts +@src/filesync/writer.ts +@src/filesync/watcher.ts +@src/filesync/parser.ts + +# Integration points: +@src/trpc/router.ts +@src/trpc/context.ts +@src/cli/index.ts +@src/server/index.ts + + + + + + Task 1: Create FileSystemSyncManager adapter + src/filesync/manager.ts + +Create FileSystemSyncManager class implementing FileSystemSync port: + +1. Constructor takes: + - messagesDir: string (default: '.cw/messages') + - messageRepository: MessageRepository + - agentRepository: AgentRepository (to get agent names) + - eventBus?: EventBus + +2. Internal components: + - writer: MessageFileWriter + - watcher: FileWatcher + - parser: MessageFileParser + +3. Implement FileSystemSync interface: + - start(): + - Ensure directory exists + - Start watcher + - Perform initial sync (DB→FS) + + - stop(): + - Stop watcher + + - syncToFilesystem(messageId: string): + - Get message from repository + - Get agent name from agentRepository + - Call writer.writeMessage() + + - syncFromFilesystem(filePath: string): + - Parse file + - Create or update message in repository + + - syncAll(): + - Get all messages from repository + - Write all to filesystem + - Return results + + - getWatcherStatus(): + - Return { running: watcher.isRunning(), directory: messagesDir } + +4. Subscribe to message events: + - When new message created in DB, auto-sync to FS + - Use eventBus to listen for message creation events + - (Or integrate with MessageRepository directly) + +5. Coordinate writer and watcher: + - Share "in-flight" set between writer and watcher + - Writer adds to set before write, watcher checks set + + tsc --noEmit passes + FileSystemSyncManager adapter implementing port interface + + + + Task 2: Add manager tests + src/filesync/manager.test.ts + +Create integration tests for FileSystemSyncManager: + +1. Setup: + - Use tmp directory + - Create in-memory test database + - Create test repositories + - Create test agent and messages + +2. Test cases: + - start() creates directory and initial sync + - syncToFilesystem() creates correct file + - syncFromFilesystem() updates DB + - syncAll() syncs all messages + - stop() stops watcher + - Bidirectional sync works (FSUI-01) + +3. Integration tests: + - Create message in DB → appears as file + - Edit file → message updated in DB + - Create new file (response) → new message in DB + + npm test -- src/filesync/manager.test.ts passes + Manager tests verify bidirectional sync + + + + Task 3: Add tRPC procedures + src/trpc/router.ts, src/trpc/context.ts + +Add filesync procedures to tRPC router: + +1. Update TRPCContext to include optional fileSystemSync: + ```typescript + fileSystemSync?: FileSystemSync; + ``` + +2. Add procedures: + - filesync.getStatus - Returns watcher status + - Input: none + - Output: { running: boolean, directory: string } + + - filesync.start - Start sync + - Input: none + - Output: { success: boolean } + + - filesync.stop - Stop sync + - Input: none + - Output: { success: boolean } + + - filesync.syncAll - Manual full sync + - Input: none + - Output: { results: SyncResult[] } + + - filesync.syncMessage - Sync single message to FS + - Input: { messageId: string } + - Output: SyncResult + +Guard all procedures: if fileSystemSync not in context, throw error. +Follow pattern from coordination procedures. + + tsc --noEmit passes + tRPC procedures added for filesync operations + + + + Task 4: Add CLI commands + src/cli/index.ts + +Add filesync subcommands following existing CLI patterns: + +1. cw sync status + - Call filesync.getStatus + - Display: "File sync: running|stopped, Directory: {path}" + +2. cw sync start + - Call filesync.start + - Display: "File sync started" + +3. cw sync stop + - Call filesync.stop + - Display: "File sync stopped" + +4. cw sync run + - Call filesync.syncAll + - Display: "Synced {n} messages" + +Follow pattern from existing CLI commands (agent, task, coordinate). +Use Commander.js subcommand pattern. + + cw sync --help shows commands + CLI sync commands implemented + + + + Task 5: Wire into server + src/server/index.ts + +Create and inject FileSystemSyncManager in server startup: + +1. In startServer(): + - Create FileSystemSyncManager with: + - messagesDir from config or default '.cw/messages' + - messageRepository + - agentRepository + - eventBus + +2. Add to TRPCContext: + - fileSystemSync: manager + +3. On server start: + - Optionally auto-start sync (configurable) + - Or leave manual (user runs `cw sync start`) + +4. On server shutdown: + - Stop filesync watcher in graceful shutdown handler + +Keep sync disabled by default for v1 - user explicitly starts it. + + Server starts with filesync available in context + FileSystemSync wired into server + + + + Task 6: Update module exports + src/filesync/index.ts + +Add manager to exports: + +```typescript +export * from './types.js'; +export * from './writer.js'; +export * from './parser.js'; +export * from './watcher.js'; +export * from './manager.js'; +``` + + All filesync components importable + Complete module exports + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] `npm test -- src/filesync/` passes all tests +- [ ] `cw sync status` works via CLI +- [ ] Server starts with filesync in context +- [ ] FSUI-01 satisfied (bidirectional sync works) + + + + +- All tasks completed +- All verification checks pass +- No errors or warnings introduced +- All FSUI requirements satisfied: + - FSUI-01: Bidirectional sync works + - FSUI-02: Messages appear as files (via 07-02) + - FSUI-03: User responses via files (via 07-03) + - FSUI-04: File watcher detects changes (via 07-03) + + + +After completion, create `.planning/phases/07-file-system-ui/07-04-SUMMARY.md` +