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
6.1 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous
| phase | plan | type | wave | depends_on | files_modified | autonomous | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 07-file-system-ui | 03 | execute | 2 |
|
|
true |
Purpose: User can respond to agents by editing/creating files, changes sync to DB. Output: FileWatcher class using chokidar, MessageFileParser for reading files.
<execution_context>
@/.claude/get-shit-done/workflows/execute-plan.md
@/.claude/get-shit-done/templates/summary.md
</execution_context>
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: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:-
parseFile(filePath: string): Promise<MessageFileFormat | null>
- Read file content
- Parse YAML frontmatter (between --- markers)
- Extract markdown body
- Return null if file format invalid (log warning)
-
serializeToFile(data: MessageFileFormat): string
- Convert MessageFileFormat to file content string
- YAML frontmatter + markdown body
- Used by writer (shared logic)
-
extractAgentNameFromPath(filePath: string): string | null
- Parse {agentName}-{messageId}.md pattern
- Return agentName or null if pattern doesn't match
-
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:-
Constructor takes:
- messagesDir: string
- messageRepository: MessageRepository
- eventBus?: EventBus
- debounceMs?: number (default 100ms)
-
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
-
-
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)
-
-
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
-
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
-
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
-
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:export * from './types.js';
export * from './writer.js';
export * from './parser.js';
export * from './watcher.js';
<success_criteria>
- 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) </success_criteria>