From f04a950c9ea35b05345a7e9e59e75a90f6924fa4 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:00:47 +0900 Subject: [PATCH] more refactor --- src/__tests__/github-pr.test.ts | 2 +- src/__tests__/watcher.test.ts | 2 +- src/cli.ts | 7 +- src/commands/execution/index.ts | 19 ++++-- src/commands/execution/pipelineExecution.ts | 30 ++------ src/commands/execution/selectAndExecute.ts | 17 +---- src/commands/execution/taskExecution.ts | 22 +----- src/commands/execution/types.ts | 76 +++++++++++++++++++++ src/commands/execution/workflowExecution.ts | 25 ++----- src/commands/management/listTasks.ts | 2 +- src/commands/management/taskActions.ts | 3 +- src/commands/management/watchTasks.ts | 2 +- src/config/project/projectConfig.ts | 26 +------ src/config/project/sessionStore.ts | 10 +-- src/config/types.ts | 36 ++++++++++ src/github/issue.ts | 16 +---- src/github/pr.ts | 26 ++----- src/github/types.ts | 37 ++++++++++ src/mock/client.ts | 14 +--- src/mock/scenario.ts | 11 +-- src/mock/types.ts | 26 +++++++ src/providers/mock.ts | 3 +- src/task/branchList.ts | 19 ++---- src/task/clone.ts | 53 ++++++++------ src/task/index.ts | 23 ++++--- src/task/runner.ts | 22 +----- src/task/summarize.ts | 12 +--- src/task/types.ts | 67 ++++++++++++++++++ src/task/watcher.ts | 3 +- 29 files changed, 358 insertions(+), 253 deletions(-) create mode 100644 src/commands/execution/types.ts create mode 100644 src/config/types.ts create mode 100644 src/github/types.ts create mode 100644 src/mock/types.ts create mode 100644 src/task/types.ts diff --git a/src/__tests__/github-pr.test.ts b/src/__tests__/github-pr.test.ts index 19e1c0c..bc6c245 100644 --- a/src/__tests__/github-pr.test.ts +++ b/src/__tests__/github-pr.test.ts @@ -7,7 +7,7 @@ import { describe, it, expect } from 'vitest'; import { buildPrBody } from '../github/pr.js'; -import type { GitHubIssue } from '../github/issue.js'; +import type { GitHubIssue } from '../github/types.js'; describe('buildPrBody', () => { it('should build body with issue and report', () => { diff --git a/src/__tests__/watcher.test.ts b/src/__tests__/watcher.test.ts index 0dabc8a..228a6b6 100644 --- a/src/__tests__/watcher.test.ts +++ b/src/__tests__/watcher.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { mkdirSync, writeFileSync, existsSync, rmSync } from 'node:fs'; import { join } from 'node:path'; import { TaskWatcher } from '../task/watcher.js'; -import type { TaskInfo } from '../task/runner.js'; +import type { TaskInfo } from '../task/types.js'; describe('TaskWatcher', () => { const testDir = `/tmp/takt-watcher-test-${Date.now()}`; diff --git a/src/cli.ts b/src/cli.ts index f327252..0eae095 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -45,11 +45,8 @@ import { DEFAULT_WORKFLOW_NAME } from './constants.js'; import { checkForUpdates } from './utils/updateNotifier.js'; import { getErrorMessage } from './utils/error.js'; import { resolveIssueTask, isIssueReference } from './github/issue.js'; -import { - selectAndExecuteTask, - type SelectAndExecuteOptions, -} from './commands/execution/selectAndExecute.js'; -import type { TaskExecutionOptions } from './commands/execution/taskExecution.js'; +import { selectAndExecuteTask } from './commands/execution/selectAndExecute.js'; +import type { TaskExecutionOptions, SelectAndExecuteOptions } from './commands/execution/types.js'; import type { ProviderType } from './providers/index.js'; const require = createRequire(import.meta.url); diff --git a/src/commands/execution/index.ts b/src/commands/execution/index.ts index 8ac9081..1baecc4 100644 --- a/src/commands/execution/index.ts +++ b/src/commands/execution/index.ts @@ -2,13 +2,22 @@ * Task/workflow execution commands. */ -export { executeWorkflow, type WorkflowExecutionResult, type WorkflowExecutionOptions } from './workflowExecution.js'; -export { executeTask, runAllTasks, executeAndCompleteTask, resolveTaskExecution, type TaskExecutionOptions } from './taskExecution.js'; +// Types +export type { + WorkflowExecutionResult, + WorkflowExecutionOptions, + TaskExecutionOptions, + ExecuteTaskOptions, + PipelineExecutionOptions, + WorktreeConfirmationResult, + SelectAndExecuteOptions, +} from './types.js'; + +export { executeWorkflow } from './workflowExecution.js'; +export { executeTask, runAllTasks, executeAndCompleteTask, resolveTaskExecution } from './taskExecution.js'; export { selectAndExecuteTask, confirmAndCreateWorktree, - type SelectAndExecuteOptions, - type WorktreeConfirmationResult, } from './selectAndExecute.js'; -export { executePipeline, type PipelineExecutionOptions } from './pipelineExecution.js'; +export { executePipeline } from './pipelineExecution.js'; export { withAgentSession } from './session.js'; diff --git a/src/commands/execution/pipelineExecution.ts b/src/commands/execution/pipelineExecution.ts index 5977d81..9268a1a 100644 --- a/src/commands/execution/pipelineExecution.ts +++ b/src/commands/execution/pipelineExecution.ts @@ -10,10 +10,12 @@ */ import { execFileSync } from 'node:child_process'; -import { fetchIssue, formatIssueAsTask, checkGhCli, type GitHubIssue } from '../../github/issue.js'; +import { fetchIssue, formatIssueAsTask, checkGhCli } from '../../github/issue.js'; +import type { GitHubIssue } from '../../github/types.js'; import { createPullRequest, pushBranch, buildPrBody } from '../../github/pr.js'; import { stageAndCommit } from '../../task/git.js'; -import { executeTask, type TaskExecutionOptions } from './taskExecution.js'; +import { executeTask } from './taskExecution.js'; +import type { TaskExecutionOptions, PipelineExecutionOptions } from './types.js'; import { loadGlobalConfig } from '../../config/global/globalConfig.js'; import { info, error, success, status, blankLine } from '../../utils/ui.js'; import { createLogger } from '../../utils/debug.js'; @@ -25,31 +27,11 @@ import { EXIT_GIT_OPERATION_FAILED, EXIT_PR_CREATION_FAILED, } from '../../exitCodes.js'; -import type { ProviderType } from '../../providers/index.js'; + +export type { PipelineExecutionOptions }; const log = createLogger('pipeline'); -export interface PipelineExecutionOptions { - /** GitHub issue number */ - issueNumber?: number; - /** Task content (alternative to issue) */ - task?: string; - /** Workflow name or path to workflow file */ - workflow: string; - /** Branch name (auto-generated if omitted) */ - branch?: string; - /** Whether to create a PR after successful execution */ - autoPr: boolean; - /** Repository in owner/repo format */ - repo?: string; - /** Skip branch creation, commit, and push (workflow-only execution) */ - skipGit?: boolean; - /** Working directory */ - cwd: string; - provider?: ProviderType; - model?: string; -} - /** * Expand template variables in a string. * Supported: {title}, {issue}, {issue_body}, {report} diff --git a/src/commands/execution/selectAndExecute.ts b/src/commands/execution/selectAndExecute.ts index 6128bb4..ea7f9ed 100644 --- a/src/commands/execution/selectAndExecute.ts +++ b/src/commands/execution/selectAndExecute.ts @@ -17,23 +17,12 @@ import { info, error, success } from '../../utils/ui.js'; import { createLogger } from '../../utils/debug.js'; import { createPullRequest, buildPrBody } from '../../github/pr.js'; import { executeTask } from './taskExecution.js'; -import type { TaskExecutionOptions } from './taskExecution.js'; +import type { TaskExecutionOptions, WorktreeConfirmationResult, SelectAndExecuteOptions } from './types.js'; + +export type { WorktreeConfirmationResult, SelectAndExecuteOptions }; const log = createLogger('selectAndExecute'); -export interface WorktreeConfirmationResult { - execCwd: string; - isWorktree: boolean; - branch?: string; -} - -export interface SelectAndExecuteOptions { - autoPr?: boolean; - repo?: string; - workflow?: string; - createWorktree?: boolean | undefined; -} - /** * Select a workflow interactively. * Returns the selected workflow name, or null if cancelled. diff --git a/src/commands/execution/taskExecution.ts b/src/commands/execution/taskExecution.ts index 0aff497..0593c0b 100644 --- a/src/commands/execution/taskExecution.ts +++ b/src/commands/execution/taskExecution.ts @@ -19,28 +19,12 @@ import { createLogger } from '../../utils/debug.js'; import { getErrorMessage } from '../../utils/error.js'; import { executeWorkflow } from './workflowExecution.js'; import { DEFAULT_WORKFLOW_NAME } from '../../constants.js'; -import type { ProviderType } from '../../providers/index.js'; +import type { TaskExecutionOptions, ExecuteTaskOptions } from './types.js'; + +export type { TaskExecutionOptions, ExecuteTaskOptions }; const log = createLogger('task'); -export interface TaskExecutionOptions { - provider?: ProviderType; - model?: string; -} - -export interface ExecuteTaskOptions { - /** Task content */ - task: string; - /** Working directory (may be a clone path) */ - cwd: string; - /** Workflow name or path (auto-detected by isWorkflowPath) */ - workflowIdentifier: string; - /** Project root (where .takt/ lives) */ - projectCwd: string; - /** Agent provider/model overrides */ - agentOverrides?: TaskExecutionOptions; -} - /** * Execute a single task with workflow. */ diff --git a/src/commands/execution/types.ts b/src/commands/execution/types.ts new file mode 100644 index 0000000..f600063 --- /dev/null +++ b/src/commands/execution/types.ts @@ -0,0 +1,76 @@ +/** + * Execution module type definitions + */ + +import type { Language } from '../../models/types.js'; +import type { ProviderType } from '../../providers/index.js'; + +/** Result of workflow execution */ +export interface WorkflowExecutionResult { + success: boolean; + reason?: string; +} + +/** Options for workflow execution */ +export interface WorkflowExecutionOptions { + /** Header prefix for display */ + headerPrefix?: string; + /** Project root directory (where .takt/ lives). */ + projectCwd: string; + /** Language for instruction metadata */ + language?: Language; + provider?: ProviderType; + model?: string; +} + +export interface TaskExecutionOptions { + provider?: ProviderType; + model?: string; +} + +export interface ExecuteTaskOptions { + /** Task content */ + task: string; + /** Working directory (may be a clone path) */ + cwd: string; + /** Workflow name or path (auto-detected by isWorkflowPath) */ + workflowIdentifier: string; + /** Project root (where .takt/ lives) */ + projectCwd: string; + /** Agent provider/model overrides */ + agentOverrides?: TaskExecutionOptions; +} + +export interface PipelineExecutionOptions { + /** GitHub issue number */ + issueNumber?: number; + /** Task content (alternative to issue) */ + task?: string; + /** Workflow name or path to workflow file */ + workflow: string; + /** Branch name (auto-generated if omitted) */ + branch?: string; + /** Whether to create a PR after successful execution */ + autoPr: boolean; + /** Repository in owner/repo format */ + repo?: string; + /** Skip branch creation, commit, and push (workflow-only execution) */ + skipGit?: boolean; + /** Working directory */ + cwd: string; + provider?: ProviderType; + model?: string; +} + +export interface WorktreeConfirmationResult { + execCwd: string; + isWorktree: boolean; + branch?: string; +} + +export interface SelectAndExecuteOptions { + autoPr?: boolean; + repo?: string; + workflow?: string; + createWorktree?: boolean | undefined; +} diff --git a/src/commands/execution/workflowExecution.ts b/src/commands/execution/workflowExecution.ts index d42fb4d..ed47739 100644 --- a/src/commands/execution/workflowExecution.ts +++ b/src/commands/execution/workflowExecution.ts @@ -4,9 +4,12 @@ import { readFileSync } from 'node:fs'; import { WorkflowEngine } from '../../workflow/engine/WorkflowEngine.js'; -import type { WorkflowConfig, Language } from '../../models/types.js'; +import type { WorkflowConfig } from '../../models/types.js'; import type { IterationLimitRequest } from '../../workflow/types.js'; -import type { ProviderType } from '../../providers/index.js'; +import type { WorkflowExecutionResult, WorkflowExecutionOptions } from './types.js'; + +export type { WorkflowExecutionResult, WorkflowExecutionOptions }; + import { loadAgentSessions, updateAgentSession, @@ -62,24 +65,6 @@ function formatElapsedTime(startTime: string, endTime: string): string { return `${minutes}m ${seconds}s`; } -/** Result of workflow execution */ -export interface WorkflowExecutionResult { - success: boolean; - reason?: string; -} - -/** Options for workflow execution */ -export interface WorkflowExecutionOptions { - /** Header prefix for display */ - headerPrefix?: string; - /** Project root directory (where .takt/ lives). */ - projectCwd: string; - /** Language for instruction metadata */ - language?: Language; - provider?: ProviderType; - model?: string; -} - /** * Execute a workflow and handle all events */ diff --git a/src/commands/management/listTasks.ts b/src/commands/management/listTasks.ts index 55df2c9..5a8a436 100644 --- a/src/commands/management/listTasks.ts +++ b/src/commands/management/listTasks.ts @@ -13,7 +13,7 @@ import { import { selectOption, confirm } from '../../prompt/index.js'; import { info } from '../../utils/ui.js'; import { createLogger } from '../../utils/debug.js'; -import type { TaskExecutionOptions } from '../execution/taskExecution.js'; +import type { TaskExecutionOptions } from '../execution/types.js'; import { type ListAction, showFullDiff, diff --git a/src/commands/management/taskActions.ts b/src/commands/management/taskActions.ts index 9f0a478..cb164b5 100644 --- a/src/commands/management/taskActions.ts +++ b/src/commands/management/taskActions.ts @@ -22,7 +22,8 @@ import { selectOption, promptInput } from '../../prompt/index.js'; import { info, success, error as logError, warn, header, blankLine } from '../../utils/ui.js'; import { createLogger } from '../../utils/debug.js'; import { getErrorMessage } from '../../utils/error.js'; -import { executeTask, type TaskExecutionOptions } from '../execution/taskExecution.js'; +import { executeTask } from '../execution/taskExecution.js'; +import type { TaskExecutionOptions } from '../execution/types.js'; import { listWorkflows } from '../../config/loaders/workflowLoader.js'; import { getCurrentWorkflow } from '../../config/paths.js'; import { DEFAULT_WORKFLOW_NAME } from '../../constants.js'; diff --git a/src/commands/management/watchTasks.ts b/src/commands/management/watchTasks.ts index 7399767..3d63a5c 100644 --- a/src/commands/management/watchTasks.ts +++ b/src/commands/management/watchTasks.ts @@ -17,7 +17,7 @@ import { } from '../../utils/ui.js'; import { executeAndCompleteTask } from '../execution/taskExecution.js'; import { DEFAULT_WORKFLOW_NAME } from '../../constants.js'; -import type { TaskExecutionOptions } from '../execution/taskExecution.js'; +import type { TaskExecutionOptions } from '../execution/types.js'; /** * Watch for tasks and execute them as they appear. diff --git a/src/config/project/projectConfig.ts b/src/config/project/projectConfig.ts index a70d50a..ef88c56 100644 --- a/src/config/project/projectConfig.ts +++ b/src/config/project/projectConfig.ts @@ -8,31 +8,9 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'; import { join, resolve } from 'node:path'; import { parse, stringify } from 'yaml'; import { copyProjectResourcesToDir } from '../../resources/index.js'; +import type { PermissionMode, ProjectPermissionMode, ProjectLocalConfig } from '../types.js'; -/** Permission mode for the project - * - default: Uses Agent SDK's acceptEdits mode (auto-accepts file edits, minimal prompts) - * - sacrifice-my-pc: Auto-approves all permission requests (bypassPermissions) - * - * Note: 'confirm' mode is planned but not yet implemented - */ -export type PermissionMode = 'default' | 'sacrifice-my-pc'; - -/** @deprecated Use PermissionMode instead */ -export type ProjectPermissionMode = PermissionMode; - -/** Project configuration stored in .takt/config.yaml */ -export interface ProjectLocalConfig { - /** Current workflow name */ - workflow?: string; - /** Provider selection for agent runtime */ - provider?: 'claude' | 'codex'; - /** Permission mode setting */ - permissionMode?: PermissionMode; - /** Verbose output mode */ - verbose?: boolean; - /** Custom settings */ - [key: string]: unknown; -} +export type { PermissionMode, ProjectPermissionMode, ProjectLocalConfig }; /** Default project configuration */ const DEFAULT_PROJECT_CONFIG: ProjectLocalConfig = { diff --git a/src/config/project/sessionStore.ts b/src/config/project/sessionStore.ts index 52e8d9b..5503ea1 100644 --- a/src/config/project/sessionStore.ts +++ b/src/config/project/sessionStore.ts @@ -84,13 +84,9 @@ export function addToInputHistory(projectDir: string, input: string): void { // ============ Agent Sessions ============ -/** Agent session data for persistence */ -export interface AgentSessionData { - agentSessions: Record; - updatedAt: string; - /** Provider that created these sessions (claude, codex, etc.) */ - provider?: string; -} +import type { AgentSessionData } from '../types.js'; + +export type { AgentSessionData }; /** Get path for storing agent sessions */ export function getAgentSessionsPath(projectDir: string): string { diff --git a/src/config/types.ts b/src/config/types.ts new file mode 100644 index 0000000..e8dd2e9 --- /dev/null +++ b/src/config/types.ts @@ -0,0 +1,36 @@ +/** + * Config module type definitions + */ + +/** Permission mode for the project + * - default: Uses Agent SDK's acceptEdits mode (auto-accepts file edits, minimal prompts) + * - sacrifice-my-pc: Auto-approves all permission requests (bypassPermissions) + * + * Note: 'confirm' mode is planned but not yet implemented + */ +export type PermissionMode = 'default' | 'sacrifice-my-pc'; + +/** @deprecated Use PermissionMode instead */ +export type ProjectPermissionMode = PermissionMode; + +/** Project configuration stored in .takt/config.yaml */ +export interface ProjectLocalConfig { + /** Current workflow name */ + workflow?: string; + /** Provider selection for agent runtime */ + provider?: 'claude' | 'codex'; + /** Permission mode setting */ + permissionMode?: PermissionMode; + /** Verbose output mode */ + verbose?: boolean; + /** Custom settings */ + [key: string]: unknown; +} + +/** Agent session data for persistence */ +export interface AgentSessionData { + agentSessions: Record; + updatedAt: string; + /** Provider that created these sessions (claude, codex, etc.) */ + provider?: string; +} diff --git a/src/github/issue.ts b/src/github/issue.ts index 3aae03f..cdee3c7 100644 --- a/src/github/issue.ts +++ b/src/github/issue.ts @@ -7,25 +7,15 @@ import { execFileSync } from 'node:child_process'; import { createLogger } from '../utils/debug.js'; +import type { GitHubIssue, GhCliStatus } from './types.js'; + +export type { GitHubIssue, GhCliStatus }; const log = createLogger('github'); /** Regex to match `#N` patterns (issue numbers) */ const ISSUE_NUMBER_REGEX = /^#(\d+)$/; -export interface GitHubIssue { - number: number; - title: string; - body: string; - labels: string[]; - comments: Array<{ author: string; body: string }>; -} - -export interface GhCliStatus { - available: boolean; - error?: string; -} - /** * Check if `gh` CLI is available and authenticated. */ diff --git a/src/github/pr.ts b/src/github/pr.ts index 5ed988d..8b668bf 100644 --- a/src/github/pr.ts +++ b/src/github/pr.ts @@ -7,31 +7,13 @@ import { execFileSync } from 'node:child_process'; import { createLogger } from '../utils/debug.js'; import { getErrorMessage } from '../utils/error.js'; -import { checkGhCli, type GitHubIssue } from './issue.js'; +import { checkGhCli } from './issue.js'; +import type { GitHubIssue, CreatePrOptions, CreatePrResult } from './types.js'; + +export type { CreatePrOptions, CreatePrResult }; const log = createLogger('github-pr'); -export interface CreatePrOptions { - /** Branch to create PR from */ - branch: string; - /** PR title */ - title: string; - /** PR body (markdown) */ - body: string; - /** Base branch (default: repo default branch) */ - base?: string; - /** Repository in owner/repo format (optional, uses current repo if omitted) */ - repo?: string; -} - -export interface CreatePrResult { - success: boolean; - /** PR URL on success */ - url?: string; - /** Error message on failure */ - error?: string; -} - /** * Push a branch to origin. * Throws on failure. diff --git a/src/github/types.ts b/src/github/types.ts new file mode 100644 index 0000000..e318d40 --- /dev/null +++ b/src/github/types.ts @@ -0,0 +1,37 @@ +/** + * GitHub module type definitions + */ + +export interface GitHubIssue { + number: number; + title: string; + body: string; + labels: string[]; + comments: Array<{ author: string; body: string }>; +} + +export interface GhCliStatus { + available: boolean; + error?: string; +} + +export interface CreatePrOptions { + /** Branch to create PR from */ + branch: string; + /** PR title */ + title: string; + /** PR body (markdown) */ + body: string; + /** Base branch (default: repo default branch) */ + base?: string; + /** Repository in owner/repo format (optional, uses current repo if omitted) */ + repo?: string; +} + +export interface CreatePrResult { + success: boolean; + /** PR URL on success */ + url?: string; + /** Error message on failure */ + error?: string; +} diff --git a/src/mock/client.ts b/src/mock/client.ts index 4da6fd6..1215e79 100644 --- a/src/mock/client.ts +++ b/src/mock/client.ts @@ -6,20 +6,12 @@ */ import { randomUUID } from 'node:crypto'; -import type { StreamCallback, StreamEvent } from '../claude/process.js'; +import type { StreamEvent } from '../claude/process.js'; import type { AgentResponse } from '../models/types.js'; import { getScenarioQueue } from './scenario.js'; +import type { MockCallOptions } from './types.js'; -/** Options for mock calls */ -export interface MockCallOptions { - cwd: string; - sessionId?: string; - onStream?: StreamCallback; - /** Fixed response content (optional, defaults to generic mock response) */ - mockResponse?: string; - /** Fixed status to return (optional, defaults to 'done') */ - mockStatus?: 'done' | 'blocked' | 'approved' | 'rejected' | 'improve'; -} +export type { MockCallOptions }; /** * Generate a mock session ID diff --git a/src/mock/scenario.ts b/src/mock/scenario.ts index 1775785..0cd65d7 100644 --- a/src/mock/scenario.ts +++ b/src/mock/scenario.ts @@ -7,16 +7,9 @@ */ import { readFileSync, existsSync } from 'node:fs'; +import type { ScenarioEntry } from './types.js'; -/** A single entry in a mock scenario */ -export interface ScenarioEntry { - /** Agent name to match (optional — if omitted, consumed by call order) */ - agent?: string; - /** Response status */ - status: 'done' | 'blocked' | 'approved' | 'rejected' | 'improve'; - /** Response content body */ - content: string; -} +export type { ScenarioEntry }; /** * Queue that dispenses scenario entries. diff --git a/src/mock/types.ts b/src/mock/types.ts new file mode 100644 index 0000000..ab76664 --- /dev/null +++ b/src/mock/types.ts @@ -0,0 +1,26 @@ +/** + * Mock module type definitions + */ + +import type { StreamCallback } from '../claude/process.js'; + +/** Options for mock calls */ +export interface MockCallOptions { + cwd: string; + sessionId?: string; + onStream?: StreamCallback; + /** Fixed response content (optional, defaults to generic mock response) */ + mockResponse?: string; + /** Fixed status to return (optional, defaults to 'done') */ + mockStatus?: 'done' | 'blocked' | 'approved' | 'rejected' | 'improve'; +} + +/** A single entry in a mock scenario */ +export interface ScenarioEntry { + /** Agent name to match (optional — if omitted, consumed by call order) */ + agent?: string; + /** Response status */ + status: 'done' | 'blocked' | 'approved' | 'rejected' | 'improve'; + /** Response content body */ + content: string; +} diff --git a/src/providers/mock.ts b/src/providers/mock.ts index f99d699..d47b1c0 100644 --- a/src/providers/mock.ts +++ b/src/providers/mock.ts @@ -2,7 +2,8 @@ * Mock provider implementation */ -import { callMock, callMockCustom, type MockCallOptions } from '../mock/client.js'; +import { callMock, callMockCustom } from '../mock/client.js'; +import type { MockCallOptions } from '../mock/types.js'; import type { AgentResponse } from '../models/types.js'; import type { Provider, ProviderCallOptions } from './types.js'; diff --git a/src/task/branchList.ts b/src/task/branchList.ts index babbeab..0d4b03f 100644 --- a/src/task/branchList.ts +++ b/src/task/branchList.ts @@ -9,23 +9,12 @@ import { execFileSync } from 'node:child_process'; import { createLogger } from '../utils/debug.js'; +import type { BranchInfo, BranchListItem } from './types.js'; + +export type { BranchInfo, BranchListItem }; + const log = createLogger('branchList'); -/** Branch info from `git branch --list` */ -export interface BranchInfo { - branch: string; - commit: string; -} - -/** Branch with list metadata */ -export interface BranchListItem { - info: BranchInfo; - filesChanged: number; - taskSlug: string; - /** Original task instruction extracted from first commit message */ - originalInstruction: string; -} - const TAKT_BRANCH_PREFIX = 'takt/'; /** diff --git a/src/task/clone.ts b/src/task/clone.ts index fa4da70..3820c90 100644 --- a/src/task/clone.ts +++ b/src/task/clone.ts @@ -13,27 +13,12 @@ import { execFileSync } from 'node:child_process'; import { createLogger } from '../utils/debug.js'; import { slugify } from '../utils/slug.js'; import { loadGlobalConfig } from '../config/global/globalConfig.js'; +import type { WorktreeOptions, WorktreeResult } from './types.js'; + +export type { WorktreeOptions, WorktreeResult }; const log = createLogger('clone'); -export interface WorktreeOptions { - /** worktree setting: true = auto path, string = custom path */ - worktree: boolean | string; - /** Branch name (optional, auto-generated if omitted) */ - branch?: string; - /** Task slug for auto-generated paths/branches */ - taskSlug: string; - /** GitHub Issue number (optional, for formatting branch/path) */ - issueNumber?: number; -} - -export interface WorktreeResult { - /** Absolute path to the clone */ - path: string; - /** Branch name used */ - branch: string; -} - const CLONE_META_DIR = 'clone-meta'; /** @@ -112,11 +97,41 @@ export class CloneManager { } } + /** + * Resolve the main repository path (handles git worktree case). + * If projectDir is a worktree, returns the main repo path. + * Otherwise, returns projectDir as-is. + */ + private static resolveMainRepo(projectDir: string): string { + const gitPath = path.join(projectDir, '.git'); + + try { + const stats = fs.statSync(gitPath); + if (stats.isFile()) { + const content = fs.readFileSync(gitPath, 'utf-8'); + const match = content.match(/^gitdir:\s*(.+)$/m); + if (match && match[1]) { + const worktreePath = match[1].trim(); + const gitDir = path.resolve(worktreePath, '..', '..'); + const mainRepoPath = path.dirname(gitDir); + log.info('Detected worktree, using main repo', { worktree: projectDir, mainRepo: mainRepoPath }); + return mainRepoPath; + } + } + } catch (err) { + log.debug('Failed to resolve main repo, using projectDir as-is', { error: String(err) }); + } + + return projectDir; + } + /** Clone a repository and remove origin to isolate from the main repo */ private static cloneAndIsolate(projectDir: string, clonePath: string): void { + const referenceRepo = CloneManager.resolveMainRepo(projectDir); + fs.mkdirSync(path.dirname(clonePath), { recursive: true }); - execFileSync('git', ['clone', '--reference', projectDir, '--dissociate', projectDir, clonePath], { + execFileSync('git', ['clone', '--reference', referenceRepo, '--dissociate', projectDir, clonePath], { cwd: projectDir, stdio: 'pipe', }); diff --git a/src/task/index.ts b/src/task/index.ts index 5188dac..28909bf 100644 --- a/src/task/index.ts +++ b/src/task/index.ts @@ -2,17 +2,24 @@ * Task execution module */ +// Types +export type { + TaskInfo, + TaskResult, + WorktreeOptions, + WorktreeResult, + BranchInfo, + BranchListItem, + SummarizeOptions, +} from './types.js'; + // Classes export { CloneManager } from './clone.js'; export { AutoCommitter } from './autoCommit.js'; export { TaskSummarizer } from './summarize.js'; export { BranchManager } from './branchList.js'; -export { - TaskRunner, - type TaskInfo, - type TaskResult, -} from './runner.js'; +export { TaskRunner } from './runner.js'; export { showTaskList } from './display.js'; @@ -25,8 +32,6 @@ export { saveCloneMeta, removeCloneMeta, cleanupOrphanedClone, - type WorktreeOptions, - type WorktreeResult, } from './clone.js'; export { detectDefaultBranch, @@ -36,9 +41,7 @@ export { extractTaskSlug, getOriginalInstruction, buildListItems, - type BranchInfo, - type BranchListItem, } from './branchList.js'; export { autoCommitAndPush, type AutoCommitResult } from './autoCommit.js'; -export { summarizeTaskName, type SummarizeOptions } from './summarize.js'; +export { summarizeTaskName } from './summarize.js'; export { TaskWatcher, type TaskWatcherOptions } from './watcher.js'; diff --git a/src/task/runner.ts b/src/task/runner.ts index 5bd22be..8102854 100644 --- a/src/task/runner.ts +++ b/src/task/runner.ts @@ -16,27 +16,9 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import { parseTaskFiles, parseTaskFile, type ParsedTask } from './parser.js'; -import type { TaskFileData } from './schema.js'; +import type { TaskInfo, TaskResult } from './types.js'; -/** タスク情報 */ -export interface TaskInfo { - filePath: string; - name: string; - content: string; - createdAt: string; - /** Structured data from YAML files (null for .md files) */ - data: TaskFileData | null; -} - -/** タスク実行結果 */ -export interface TaskResult { - task: TaskInfo; - success: boolean; - response: string; - executionLog: string[]; - startedAt: string; - completedAt: string; -} +export type { TaskInfo, TaskResult }; /** * タスク実行管理クラス diff --git a/src/task/summarize.ts b/src/task/summarize.ts index b1bc918..a1d456d 100644 --- a/src/task/summarize.ts +++ b/src/task/summarize.ts @@ -8,6 +8,9 @@ import * as wanakana from 'wanakana'; import { loadGlobalConfig } from '../config/global/globalConfig.js'; import { getProvider, type ProviderType } from '../providers/index.js'; import { createLogger } from '../utils/debug.js'; +import type { SummarizeOptions } from './types.js'; + +export type { SummarizeOptions }; const log = createLogger('summarize'); @@ -25,15 +28,6 @@ Fix the login bug → fix-login-bug worktreeを作るときブランチ名をAIで生成 → ai-branch-naming レビュー画面に元の指示を表示する → show-original-instruction`; -export interface SummarizeOptions { - /** Working directory for Claude execution */ - cwd: string; - /** Model to use (optional, defaults to config or haiku) */ - model?: string; - /** Use LLM for summarization (default: true). If false, uses romanization. */ - useLLM?: boolean; -} - /** * Sanitize a string for use as git branch name and directory name. * Allows only: a-z, 0-9, hyphen. diff --git a/src/task/types.ts b/src/task/types.ts new file mode 100644 index 0000000..b4f6cdd --- /dev/null +++ b/src/task/types.ts @@ -0,0 +1,67 @@ +/** + * Task module type definitions + */ + +import type { TaskFileData } from './schema.js'; + +/** タスク情報 */ +export interface TaskInfo { + filePath: string; + name: string; + content: string; + createdAt: string; + /** Structured data from YAML files (null for .md files) */ + data: TaskFileData | null; +} + +/** タスク実行結果 */ +export interface TaskResult { + task: TaskInfo; + success: boolean; + response: string; + executionLog: string[]; + startedAt: string; + completedAt: string; +} + +export interface WorktreeOptions { + /** worktree setting: true = auto path, string = custom path */ + worktree: boolean | string; + /** Branch name (optional, auto-generated if omitted) */ + branch?: string; + /** Task slug for auto-generated paths/branches */ + taskSlug: string; + /** GitHub Issue number (optional, for formatting branch/path) */ + issueNumber?: number; +} + +export interface WorktreeResult { + /** Absolute path to the clone */ + path: string; + /** Branch name used */ + branch: string; +} + +/** Branch info from `git branch --list` */ +export interface BranchInfo { + branch: string; + commit: string; +} + +/** Branch with list metadata */ +export interface BranchListItem { + info: BranchInfo; + filesChanged: number; + taskSlug: string; + /** Original task instruction extracted from first commit message */ + originalInstruction: string; +} + +export interface SummarizeOptions { + /** Working directory for Claude execution */ + cwd: string; + /** Model to use (optional, defaults to config or haiku) */ + model?: string; + /** Use LLM for summarization (default: true). If false, uses romanization. */ + useLLM?: boolean; +} diff --git a/src/task/watcher.ts b/src/task/watcher.ts index 6edfe78..18a886e 100644 --- a/src/task/watcher.ts +++ b/src/task/watcher.ts @@ -6,7 +6,8 @@ */ import { createLogger } from '../utils/debug.js'; -import { TaskRunner, type TaskInfo } from './runner.js'; +import { TaskRunner } from './runner.js'; +import type { TaskInfo } from './types.js'; const log = createLogger('watcher');