Files
Codewalkers/.planning/phases/07-file-system-ui/07-03-PLAN.md
Lukas May dbab535915 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
2026-01-30 21:19:22 +01:00

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
07-01
src/filesync/watcher.ts
src/filesync/parser.ts
src/filesync/watcher.test.ts
src/filesync/index.ts
package.json
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.

<execution_context> @/.claude/get-shit-done/workflows/execute-plan.md @/.claude/get-shit-done/templates/summary.md </execution_context>

@.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:
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<MessageFileFormat | null>

    • 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:
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

<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>
After completion, create `.planning/phases/07-file-system-ui/07-03-SUMMARY.md`