Skip to content

Refactor: split large files, extract shared patterns, improve type safety#3

Open
bebekim wants to merge 10 commits intomainfrom
claude/refactor-codebase-C5IGx
Open

Refactor: split large files, extract shared patterns, improve type safety#3
bebekim wants to merge 10 commits intomainfrom
claude/refactor-codebase-C5IGx

Conversation

@bebekim
Copy link
Copy Markdown
Owner

@bebekim bebekim commented Mar 19, 2026

Summary

  • Extract withConfirm() wrapper — eliminates confirm/plan boilerplate across 9 command files
  • Split cli.ts (396→23 lines) into 10 domain-specific command modules
  • Replace as any with typed sort resolution, SQL conditions, PdfConfig interface
  • Split container-runner.ts (708→380) into container-mounts.ts + ipc-snapshots.ts
  • Split db.ts (698→240) into db-messages.ts, db-tasks.ts, db-state.ts
  • Extract import-parser.ts from contacts/import.ts for independent testability
  • Add parseSortOption() to deduplicate sort logic in list commands
  • Type fixture overrides in test-helpers.ts

Test plan

  • CRM: 323 tests passing (was 281, +42 new)
  • NanoClaw: 272 tests passing (was 268, +4 new)
  • Zero regressions — all existing imports preserved via re-exports
  • TDD throughout — tests written before implementation

🤖 Generated with Claude Code

claude and others added 10 commits March 19, 2026 09:51
Every WRITE and RECEIPT tier command repeated the same pattern:
if (!confirm) return needsConfirm(null, plan) else execute().

New shared/with-confirm.ts provides withConfirm() that encapsulates
this branching. Applied to 9 command files: contacts add/edit/import,
donations add/void, receipts generate/batch/void, orgs add, config set.

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
cli.ts (396 lines) → 23 lines + 10 command modules in commands/.
Each domain (contacts, donations, receipts, reports, orgs, config,
jobs, search, stubs) registers its own subcommands via a
register*Commands() function. createProgram() composes them all.

cli.ts now only calls createProgram().parseAsync().

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
- Add resolveSortColumn() helper to replace (schema.table as any)[field]
  in contacts/list.ts and donations/list.ts
- Type condition arrays as SQL[] instead of any[]
- Add PdfConfig interface for receipt PDF generation
- Type raw SQL query results (sequence, search, summary)
- Replace (existing as any)[colName] with typed access in config/set.ts

Production code now has zero `as any` (remaining are in test helpers).

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
- container-mounts.ts: buildVolumeMounts() + buildContainerArgs() (210 lines)
- ipc-snapshots.ts: writeTasksSnapshot() + writeGroupsSnapshot() (70 lines)
- container-runner.ts: runContainerAgent() stays, imports from new modules

Re-exports from container-runner.ts preserve all existing import paths.

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
- db-messages.ts: storeMessage, getNewMessages, getMessagesSince (110 lines)
- db-tasks.ts: createTask, updateTask, deleteTask, getDueTasks, etc. (130 lines)
- db-state.ts: chat metadata, router state, sessions, registered groups (250 lines)
- db.ts: schema, init, migration, getDb() accessor + re-exports (240 lines)

All existing imports from './db.js' continue to work via re-exports.

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
Column mapping (COLUMN_ALIASES, SALESFORCE_PRESET, buildColumnMap) and
row validation (parseRows) extracted from import.ts (386→210 lines)
into import-parser.ts (170 lines). Parser is now independently testable
without DB mocks.

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
Both contacts/list.ts and donations/list.ts had identical 4-line
sort parsing blocks. New parseSortOption() in db/sort.ts returns
{ descending, column, orderFn } in one call.

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
Replace `any` with Partial<typeof schema.*.inferSelect> for donation,
receipt, and receiptConfig fixture factories. Catches typos in test
overrides at compile time.

https://claude.ai/code/session_01D3rGyrcsVT96ZepUP66rjT
… retry

The test used mockImplementationOnce which only threw on the first docker info
call. On the second retry, the mock returned undefined (no throw), so the
function succeeded instead of exhausting retries. Changed to mockImplementation
with a cmd filter so all docker info calls throw while sleep calls succeed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants