Claude-skill-registry code-refactoring
Guide for refactoring duplicate code in the Orient. Use when extracting shared logic, consolidating services, or improving code organization. Covers tool-calling extraction, database unification, and monorepo migration patterns.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/code-refactoring" ~/.claude/skills/majiayu000-claude-skill-registry-code-refactoring-ae2960 && rm -rf "$T"
skills/data/code-refactoring/SKILL.mdCode Refactoring Patterns
Overview
This skill provides patterns and checklists for refactoring code in the Orient monorepo. Use this when:
- Extracting shared logic from duplicate implementations
- Migrating services between
andsrc/packages/ - Unifying database access patterns
- Consolidating tool definitions
Pattern 1: Service Extraction
When to use: Two or more services share identical logic (>50 lines or complex logic).
Checklist
-
Identify Shared Code
- Find duplicate implementations in the codebase
- Document the differences (usually import paths and configuration)
- Verify the logic is semantically identical
-
Design the Shared Interface
// Define configuration options export interface ServiceConfig { option1?: string; option2?: number; } // Define the executor/adapter pattern export type ServiceExecutor = (input: Input, context?: Context) => Promise<Result>; -
Create the Shared Service
- Place in
or appropriate packagesrc/services/ - Make all platform-specific parts configurable
- Add comprehensive JSDoc comments
- Place in
-
Update Consumers
- Import from the new shared service
- Pass platform-specific configuration
- Remove duplicate inline implementations
-
Write Tests
- Unit tests for the shared service
- Integration tests for each consumer
- Before/after comparison tests if needed
Example: Tool-Calling Service
The
toolCallingService.ts extracts the common tool-calling loop from AgentService and WhatsAppAgentService:
// Shared service with configurable behavior export async function executeToolLoop( anthropic: Anthropic, messages: MessageParam[], tools: Tool[], executor: ToolExecutor, // Platform-specific config: ToolCallingConfig, context?: unknown // Platform-specific context ): Promise<ToolCallingResult>;
Consumers pass their own tool executor while benefiting from shared guardrails.
Pattern 2: Package Migration
When to use: Moving code from
src/ to packages/ for better modularity.
Checklist
-
Create Package Structure
packages/[package-name]/ ├── package.json ├── tsconfig.json ├── vitest.config.ts ├── README.md ├── src/ │ ├── index.ts │ └── [implementation files] └── __tests__/ -
Define Package Dependencies
- Use
for internal deps"@orientbot/core": "workspace:*" - Keep dependencies minimal
- Avoid circular dependencies
- Use
-
Update tsconfig.json Path Mappings
{ "compilerOptions": { "paths": { "@orientbot/[package]": ["./packages/[package]/src/index.ts"] } } } -
Incremental Migration
- Start with types-only export if the migration is complex
- Add deprecation notices to old files
- Update consumers one at a time
- Run tests after each consumer update
-
Verify Package Builds
pnpm --filter @orientbot/[package] build pnpm --filter @orientbot/[package] test
Example: @orientbot/database-services
// packages/database-services/src/index.ts export { MessageDatabase, StoredMessage, StoredGroup } from './messageDatabase.js'; export { SlackDatabase } from './slackDatabase.js'; export { SchedulerDatabase } from './schedulerDatabase.js'; export { WebhookDatabase } from './webhookDatabase.js'; export * from './types/index.js';
Pattern 3: Database Access Unification
When to use: Multiple database access patterns coexist (raw SQL, BaseDatabase, Drizzle).
Checklist
-
Audit Current Usage
grep -r "BaseDatabase" src/ grep -r "getDatabase" src/ grep -r "executeRawSql" src/ -
Create Comparison Tests
- Write tests that verify old and new implementations produce same results
- Include edge cases and error scenarios
-
Create Drizzle Helpers
// In @orientbot/database export async function executeRaw<T>(sql: string, params?: unknown[]): Promise<T[]>; export async function executeRawOne<T>(sql: string, params?: unknown[]): Promise<T | null>; -
Migrate Services One at a Time
- Start with services that already use Drizzle partially
- Update imports to use @orientbot/database
- Run comparison tests after each migration
-
Deprecate Old Patterns
- Add
JSDoc to old implementations@deprecated - Set timeline for removal
- Add
Pattern 4: Tool Definition Consolidation
When to use: Same tools defined in multiple places.
Checklist
-
Create Tool Definitions Directory
src/tools/definitions/ ├── index.ts ├── jira.ts └── whatsapp.ts -
Extract Common Definitions
// src/tools/definitions/jira.ts export const JIRA_TOOL_DEFINITIONS: Anthropic.Tool[] = [ { name: 'get_all_issues', ... }, // ... ]; -
Create Platform-Specific Getters
export function getSlackJiraTools(): Anthropic.Tool[] { return [...JIRA_TOOL_DEFINITIONS]; } export function getWhatsAppJiraTools(): Anthropic.Tool[] { return [...JIRA_TOOL_DEFINITIONS, ...EXTENDED_TOOLS]; } -
Update Consumers
- Import from definitions instead of defining inline
- Test that all tools are still available
Common Pitfalls
1. Breaking Import Paths
- Always update tsconfig.json path mappings
- Use
extensions for ESM compatibility.js - Build dependent packages before consumers
2. Missing Type Exports
- Export types explicitly:
export type { TypeName } - Re-export from index.ts for convenience
3. Circular Dependencies
- Keep packages loosely coupled
- Use dependency injection patterns
- Types can often break circular deps
4. Test Coverage Gaps
- Write tests BEFORE refactoring
- Use comparison tests for migrations
- Run tests after each incremental change
Pattern 5: Import Path Migration
When to use: Migrating imports from old paths to new package exports, or consolidating scattered imports.
Pre-Migration Audit
Before changing any imports, audit the current state:
# Find all imports of a specific module grep -r "from '.*oldModule'" packages/ src/ --include="*.ts" # Count imports by pattern grep -r "from '@orientbot/old" packages/ --include="*.ts" | wc -l # Find files importing from dist (should be zero) grep -r "from '.*dist/" packages/ src/ --include="*.ts"
Migration Checklist
-
Build the target package first
pnpm --filter @orientbot/new-package build ls packages/new-package/dist/ # Verify dist exists -
Add workspace dependency to consumer's package.json
{ "dependencies": { "@orientbot/new-package": "workspace:*" } } -
Update imports one file at a time
// Before import { Service } from '../../old/path/service.js'; // After import { Service } from '@orientbot/new-package'; -
Verify after each file
pnpm --filter @orientbot/consumer-package build pnpm --filter @orientbot/consumer-package test -
Check for runtime issues
- Build passes but runtime fails = missing export or ESM/CJS issue
- See
andesm-cjs-interop
skills for solutionspackage-exports
Import Path Decision Tree
| Scenario | Import From |
|---|---|
| Same package, same directory | |
| Same package, different directory | |
| Different package (production) | |
| Different package (dev, no build) | Path alias resolves to source |
| Legacy src/ code | |
Avoiding Dist Imports
Never import directly from another package's dist:
// ❌ Wrong - brittle, breaks on rebuild import { X } from '../../../packages/core/dist/index.js'; import { X } from '@orientbot/core/dist/something.js'; // ✅ Correct - uses package exports import { X } from '@orientbot/core';
Add this test to prevent regressions:
// tests/no-dist-imports.test.ts import { describe, it, expect } from 'vitest'; import { execSync } from 'child_process'; describe('Import hygiene', () => { it('should not import from dist directories', () => { const result = execSync("grep -r \"from '.*dist/\" packages/ src/ --include='*.ts' || true", { encoding: 'utf-8', }); expect(result.trim()).toBe(''); }); });
Bulk Migration Script
For large migrations, use a script:
#!/bin/bash # migrate-imports.sh OLD_IMPORT="from '../../old/service" NEW_IMPORT="from '@orientbot/new-package" find packages/ src/ -name "*.ts" -exec \ sed -i '' "s|${OLD_IMPORT}|${NEW_IMPORT}|g" {} \; echo "Updated files:" git diff --name-only
Post-Migration Verification
# Full rebuild pnpm turbo build --force # Run all tests pnpm test # Type check pnpm turbo typecheck # Check for leftover old imports grep -r "old/import/path" packages/ src/ --include="*.ts"
Package Dependency Graph
@orientbot/core (foundation) ├── @orientbot/database (Drizzle ORM) ├── @orientbot/database-services (MessageDB, SlackDB, etc.) ├── @orientbot/integrations (JIRA, Google) ├── @orientbot/mcp-tools (tool implementations) ├── @orientbot/bot-whatsapp ├── @orientbot/bot-slack ├── @orientbot/api-gateway └── @orientbot/dashboard
Migration Status Tracking
Use this template to track migration progress:
| Component | Status | Package | Notes | | ------------------ | -------- | ---------------------------- | ------------------------------- | | jiraService | partial | @orientbot/integrations | Types exported, service pending | | messageDatabase | complete | @orientbot/database-services | | | toolCallingService | complete | src/services/ | Shared between agents |