refactor: piece設定解決とconfig優先順位の参照経路を統一
This commit is contained in:
parent
6b425d64fc
commit
67ae3e8ae5
@ -76,6 +76,7 @@ vi.mock('../infra/task/index.js', () => ({
|
|||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
getPieceDescription: vi.fn(() => ({ name: 'default', description: 'test piece', pieceStructure: '', movementPreviews: [] })),
|
getPieceDescription: vi.fn(() => ({ name: 'default', description: 'test piece', pieceStructure: '', movementPreviews: [] })),
|
||||||
|
resolveConfigValue: vi.fn((_: string, key: string) => (key === 'piece' ? 'default' : false)),
|
||||||
resolveConfigValues: vi.fn(() => ({ language: 'en', interactivePreviewMovements: 3, provider: 'claude' })),
|
resolveConfigValues: vi.fn(() => ({ language: 'en', interactivePreviewMovements: 3, provider: 'claude' })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const configMock = vi.hoisted(() => ({
|
|||||||
loadAllPiecesWithSources: vi.fn(),
|
loadAllPiecesWithSources: vi.fn(),
|
||||||
getPieceCategories: vi.fn(),
|
getPieceCategories: vi.fn(),
|
||||||
buildCategorizedPieces: vi.fn(),
|
buildCategorizedPieces: vi.fn(),
|
||||||
getCurrentPiece: vi.fn(),
|
resolveConfigValue: vi.fn(),
|
||||||
findPieceCategories: vi.fn(() => []),
|
findPieceCategories: vi.fn(() => []),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -258,13 +258,13 @@ describe('selectPiece', () => {
|
|||||||
configMock.loadAllPiecesWithSources.mockReset();
|
configMock.loadAllPiecesWithSources.mockReset();
|
||||||
configMock.getPieceCategories.mockReset();
|
configMock.getPieceCategories.mockReset();
|
||||||
configMock.buildCategorizedPieces.mockReset();
|
configMock.buildCategorizedPieces.mockReset();
|
||||||
configMock.getCurrentPiece.mockReset();
|
configMock.resolveConfigValue.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return default piece when no pieces found and fallbackToDefault is true', async () => {
|
it('should return default piece when no pieces found and fallbackToDefault is true', async () => {
|
||||||
configMock.getPieceCategories.mockReturnValue(null);
|
configMock.getPieceCategories.mockReturnValue(null);
|
||||||
configMock.listPieces.mockReturnValue([]);
|
configMock.listPieces.mockReturnValue([]);
|
||||||
configMock.getCurrentPiece.mockReturnValue('default');
|
configMock.resolveConfigValue.mockReturnValue('default');
|
||||||
|
|
||||||
const result = await selectPiece('/cwd');
|
const result = await selectPiece('/cwd');
|
||||||
|
|
||||||
@ -274,7 +274,7 @@ describe('selectPiece', () => {
|
|||||||
it('should return null when no pieces found and fallbackToDefault is false', async () => {
|
it('should return null when no pieces found and fallbackToDefault is false', async () => {
|
||||||
configMock.getPieceCategories.mockReturnValue(null);
|
configMock.getPieceCategories.mockReturnValue(null);
|
||||||
configMock.listPieces.mockReturnValue([]);
|
configMock.listPieces.mockReturnValue([]);
|
||||||
configMock.getCurrentPiece.mockReturnValue('default');
|
configMock.resolveConfigValue.mockReturnValue('default');
|
||||||
|
|
||||||
const result = await selectPiece('/cwd', { fallbackToDefault: false });
|
const result = await selectPiece('/cwd', { fallbackToDefault: false });
|
||||||
|
|
||||||
@ -287,7 +287,7 @@ describe('selectPiece', () => {
|
|||||||
configMock.listPieceEntries.mockReturnValue([
|
configMock.listPieceEntries.mockReturnValue([
|
||||||
{ name: 'only-piece', path: '/tmp/only-piece.yaml', source: 'user' },
|
{ name: 'only-piece', path: '/tmp/only-piece.yaml', source: 'user' },
|
||||||
]);
|
]);
|
||||||
configMock.getCurrentPiece.mockReturnValue('only-piece');
|
configMock.resolveConfigValue.mockReturnValue('only-piece');
|
||||||
selectOptionMock.mockResolvedValueOnce('only-piece');
|
selectOptionMock.mockResolvedValueOnce('only-piece');
|
||||||
|
|
||||||
const result = await selectPiece('/cwd');
|
const result = await selectPiece('/cwd');
|
||||||
@ -307,7 +307,7 @@ describe('selectPiece', () => {
|
|||||||
configMock.getPieceCategories.mockReturnValue({ categories: ['Dev'] });
|
configMock.getPieceCategories.mockReturnValue({ categories: ['Dev'] });
|
||||||
configMock.loadAllPiecesWithSources.mockReturnValue(pieceMap);
|
configMock.loadAllPiecesWithSources.mockReturnValue(pieceMap);
|
||||||
configMock.buildCategorizedPieces.mockReturnValue(categorized);
|
configMock.buildCategorizedPieces.mockReturnValue(categorized);
|
||||||
configMock.getCurrentPiece.mockReturnValue('my-piece');
|
configMock.resolveConfigValue.mockReturnValue('my-piece');
|
||||||
|
|
||||||
selectOptionMock.mockResolvedValueOnce('__current__');
|
selectOptionMock.mockResolvedValueOnce('__current__');
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ describe('selectPiece', () => {
|
|||||||
configMock.getPieceCategories.mockReturnValue(null);
|
configMock.getPieceCategories.mockReturnValue(null);
|
||||||
configMock.listPieces.mockReturnValue(['piece-a', 'piece-b']);
|
configMock.listPieces.mockReturnValue(['piece-a', 'piece-b']);
|
||||||
configMock.listPieceEntries.mockReturnValue(entries);
|
configMock.listPieceEntries.mockReturnValue(entries);
|
||||||
configMock.getCurrentPiece.mockReturnValue('piece-a');
|
configMock.resolveConfigValue.mockReturnValue('piece-a');
|
||||||
|
|
||||||
selectOptionMock
|
selectOptionMock
|
||||||
.mockResolvedValueOnce('custom')
|
.mockResolvedValueOnce('custom')
|
||||||
|
|||||||
@ -90,10 +90,14 @@ vi.mock('../infra/config/index.js', () => ({
|
|||||||
updatePersonaSession: vi.fn(),
|
updatePersonaSession: vi.fn(),
|
||||||
loadWorktreeSessions: vi.fn().mockReturnValue({}),
|
loadWorktreeSessions: vi.fn().mockReturnValue({}),
|
||||||
updateWorktreeSession: vi.fn(),
|
updateWorktreeSession: vi.fn(),
|
||||||
loadGlobalConfig: vi.fn().mockReturnValue({ provider: 'claude' }),
|
resolvePieceConfigValues: vi.fn().mockReturnValue({
|
||||||
loadConfig: vi.fn().mockReturnValue({
|
notificationSound: true,
|
||||||
global: { provider: 'claude' },
|
notificationSoundEvents: {},
|
||||||
project: {},
|
provider: 'claude',
|
||||||
|
runtime: undefined,
|
||||||
|
preventSleep: false,
|
||||||
|
model: undefined,
|
||||||
|
observability: undefined,
|
||||||
}),
|
}),
|
||||||
saveSessionState: vi.fn(),
|
saveSessionState: vi.fn(),
|
||||||
ensureDir: vi.fn(),
|
ensureDir: vi.fn(),
|
||||||
|
|||||||
@ -59,7 +59,15 @@ vi.mock('../infra/config/index.js', () => ({
|
|||||||
updatePersonaSession: vi.fn(),
|
updatePersonaSession: vi.fn(),
|
||||||
loadWorktreeSessions: mockLoadWorktreeSessions,
|
loadWorktreeSessions: mockLoadWorktreeSessions,
|
||||||
updateWorktreeSession: vi.fn(),
|
updateWorktreeSession: vi.fn(),
|
||||||
loadGlobalConfig: vi.fn().mockReturnValue({ provider: 'claude' }),
|
resolvePieceConfigValues: vi.fn().mockReturnValue({
|
||||||
|
notificationSound: true,
|
||||||
|
notificationSoundEvents: {},
|
||||||
|
provider: 'claude',
|
||||||
|
runtime: undefined,
|
||||||
|
preventSleep: false,
|
||||||
|
model: undefined,
|
||||||
|
observability: undefined,
|
||||||
|
}),
|
||||||
saveSessionState: vi.fn(),
|
saveSessionState: vi.fn(),
|
||||||
ensureDir: vi.fn(),
|
ensureDir: vi.fn(),
|
||||||
writeFileAtomic: vi.fn(),
|
writeFileAtomic: vi.fn(),
|
||||||
|
|||||||
@ -9,6 +9,7 @@ const {
|
|||||||
mockCompleteTask,
|
mockCompleteTask,
|
||||||
mockFailTask,
|
mockFailTask,
|
||||||
mockExecuteTask,
|
mockExecuteTask,
|
||||||
|
mockResolvePieceConfigValue,
|
||||||
} = vi.hoisted(() => ({
|
} = vi.hoisted(() => ({
|
||||||
mockAddTask: vi.fn(() => ({
|
mockAddTask: vi.fn(() => ({
|
||||||
name: 'test-task',
|
name: 'test-task',
|
||||||
@ -21,6 +22,7 @@ const {
|
|||||||
mockCompleteTask: vi.fn(),
|
mockCompleteTask: vi.fn(),
|
||||||
mockFailTask: vi.fn(),
|
mockFailTask: vi.fn(),
|
||||||
mockExecuteTask: vi.fn(),
|
mockExecuteTask: vi.fn(),
|
||||||
|
mockResolvePieceConfigValue: vi.fn((_: string, key: string) => (key === 'autoPr' ? undefined : 'default')),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../shared/prompt/index.js', () => ({
|
vi.mock('../shared/prompt/index.js', () => ({
|
||||||
@ -28,11 +30,10 @@ vi.mock('../shared/prompt/index.js', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
getCurrentPiece: vi.fn(),
|
resolvePieceConfigValue: (...args: unknown[]) => mockResolvePieceConfigValue(...args),
|
||||||
listPieces: vi.fn(() => ['default']),
|
listPieces: vi.fn(() => ['default']),
|
||||||
listPieceEntries: vi.fn(() => []),
|
listPieceEntries: vi.fn(() => []),
|
||||||
isPiecePath: vi.fn(() => false),
|
isPiecePath: vi.fn(() => false),
|
||||||
loadConfig: vi.fn(() => ({ global: {}, project: {} })),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/task/index.js', () => ({
|
vi.mock('../infra/task/index.js', () => ({
|
||||||
@ -102,7 +103,7 @@ beforeEach(() => {
|
|||||||
|
|
||||||
describe('resolveAutoPr default in selectAndExecuteTask', () => {
|
describe('resolveAutoPr default in selectAndExecuteTask', () => {
|
||||||
it('should call auto-PR confirm with default true when no CLI option or config', async () => {
|
it('should call auto-PR confirm with default true when no CLI option or config', async () => {
|
||||||
// Given: worktree is enabled via override, no autoPr option, no global config autoPr
|
// Given: worktree is enabled via override, no autoPr option, no config autoPr
|
||||||
mockConfirm.mockResolvedValue(true);
|
mockConfirm.mockResolvedValue(true);
|
||||||
mockSummarizeTaskName.mockResolvedValue('test-task');
|
mockSummarizeTaskName.mockResolvedValue('test-task');
|
||||||
mockCreateSharedClone.mockReturnValue({
|
mockCreateSharedClone.mockReturnValue({
|
||||||
@ -121,10 +122,7 @@ describe('resolveAutoPr default in selectAndExecuteTask', () => {
|
|||||||
createWorktree: true,
|
createWorktree: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Then: the 'Create pull request?' confirm is called with default true
|
const autoPrCall = mockConfirm.mock.calls.find((call) => call[0] === 'Create pull request?');
|
||||||
const autoPrCall = mockConfirm.mock.calls.find(
|
|
||||||
(call) => call[0] === 'Create pull request?',
|
|
||||||
);
|
|
||||||
expect(autoPrCall).toBeDefined();
|
expect(autoPrCall).toBeDefined();
|
||||||
expect(autoPrCall![1]).toBe(true);
|
expect(autoPrCall![1]).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
loadPiece: vi.fn(() => null),
|
loadPiece: vi.fn(() => null),
|
||||||
getCurrentPiece: vi.fn(() => 'default'),
|
resolveConfigValue: vi.fn(() => 'default'),
|
||||||
setCurrentPiece: vi.fn(),
|
setCurrentPiece: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -20,11 +20,11 @@ vi.mock('../shared/ui/index.js', () => ({
|
|||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { getCurrentPiece, loadPiece, setCurrentPiece } from '../infra/config/index.js';
|
import { resolveConfigValue, loadPiece, setCurrentPiece } from '../infra/config/index.js';
|
||||||
import { selectPiece } from '../features/pieceSelection/index.js';
|
import { selectPiece } from '../features/pieceSelection/index.js';
|
||||||
import { switchPiece } from '../features/config/switchPiece.js';
|
import { switchPiece } from '../features/config/switchPiece.js';
|
||||||
|
|
||||||
const mockGetCurrentPiece = vi.mocked(getCurrentPiece);
|
const mockResolveConfigValue = vi.mocked(resolveConfigValue);
|
||||||
const mockLoadPiece = vi.mocked(loadPiece);
|
const mockLoadPiece = vi.mocked(loadPiece);
|
||||||
const mockSetCurrentPiece = vi.mocked(setCurrentPiece);
|
const mockSetCurrentPiece = vi.mocked(setCurrentPiece);
|
||||||
const mockSelectPiece = vi.mocked(selectPiece);
|
const mockSelectPiece = vi.mocked(selectPiece);
|
||||||
@ -32,6 +32,7 @@ const mockSelectPiece = vi.mocked(selectPiece);
|
|||||||
describe('switchPiece', () => {
|
describe('switchPiece', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
mockResolveConfigValue.mockReturnValue('default');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call selectPiece with fallbackToDefault: false', async () => {
|
it('should call selectPiece with fallbackToDefault: false', async () => {
|
||||||
|
|||||||
@ -5,12 +5,12 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import type { TaskInfo } from '../infra/task/index.js';
|
import type { TaskInfo } from '../infra/task/index.js';
|
||||||
|
|
||||||
const { mockResolveTaskExecution, mockExecutePiece, mockLoadPieceByIdentifier, mockResolveConfigValues, mockBuildTaskResult, mockPersistTaskResult, mockPersistTaskError, mockPostExecutionFlow } =
|
const { mockResolveTaskExecution, mockExecutePiece, mockLoadPieceByIdentifier, mockResolvePieceConfigValues, mockBuildTaskResult, mockPersistTaskResult, mockPersistTaskError, mockPostExecutionFlow } =
|
||||||
vi.hoisted(() => ({
|
vi.hoisted(() => ({
|
||||||
mockResolveTaskExecution: vi.fn(),
|
mockResolveTaskExecution: vi.fn(),
|
||||||
mockExecutePiece: vi.fn(),
|
mockExecutePiece: vi.fn(),
|
||||||
mockLoadPieceByIdentifier: vi.fn(),
|
mockLoadPieceByIdentifier: vi.fn(),
|
||||||
mockResolveConfigValues: vi.fn(),
|
mockResolvePieceConfigValues: vi.fn(),
|
||||||
mockBuildTaskResult: vi.fn(),
|
mockBuildTaskResult: vi.fn(),
|
||||||
mockPersistTaskResult: vi.fn(),
|
mockPersistTaskResult: vi.fn(),
|
||||||
mockPersistTaskError: vi.fn(),
|
mockPersistTaskError: vi.fn(),
|
||||||
@ -38,7 +38,7 @@ vi.mock('../features/tasks/execute/postExecution.js', () => ({
|
|||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
loadPieceByIdentifier: (...args: unknown[]) => mockLoadPieceByIdentifier(...args),
|
loadPieceByIdentifier: (...args: unknown[]) => mockLoadPieceByIdentifier(...args),
|
||||||
isPiecePath: () => false,
|
isPiecePath: () => false,
|
||||||
resolveConfigValues: (...args: unknown[]) => mockResolveConfigValues(...args),
|
resolvePieceConfigValues: (...args: unknown[]) => mockResolvePieceConfigValues(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../shared/ui/index.js', () => ({
|
vi.mock('../shared/ui/index.js', () => ({
|
||||||
@ -83,7 +83,7 @@ describe('executeAndCompleteTask', () => {
|
|||||||
name: 'default',
|
name: 'default',
|
||||||
movements: [],
|
movements: [],
|
||||||
});
|
});
|
||||||
mockResolveConfigValues.mockReturnValue({
|
mockResolvePieceConfigValues.mockReturnValue({
|
||||||
language: 'en',
|
language: 'en',
|
||||||
provider: 'claude',
|
provider: 'claude',
|
||||||
model: undefined,
|
model: undefined,
|
||||||
|
|||||||
@ -48,7 +48,7 @@ vi.mock('../infra/task/index.js', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
resolveConfigValues: vi.fn(() => ({ interactivePreviewMovements: 3, language: 'en' })),
|
resolvePieceConfigValues: vi.fn(() => ({ interactivePreviewMovements: 3, language: 'en' })),
|
||||||
getPieceDescription: vi.fn(() => ({
|
getPieceDescription: vi.fn(() => ({
|
||||||
name: 'default',
|
name: 'default',
|
||||||
description: 'desc',
|
description: 'desc',
|
||||||
|
|||||||
@ -4,7 +4,7 @@ const {
|
|||||||
mockExistsSync,
|
mockExistsSync,
|
||||||
mockSelectPiece,
|
mockSelectPiece,
|
||||||
mockSelectOption,
|
mockSelectOption,
|
||||||
mockResolveConfigValue,
|
mockResolvePieceConfigValue,
|
||||||
mockLoadPieceByIdentifier,
|
mockLoadPieceByIdentifier,
|
||||||
mockGetPieceDescription,
|
mockGetPieceDescription,
|
||||||
mockRunRetryMode,
|
mockRunRetryMode,
|
||||||
@ -16,7 +16,7 @@ const {
|
|||||||
mockExistsSync: vi.fn(() => true),
|
mockExistsSync: vi.fn(() => true),
|
||||||
mockSelectPiece: vi.fn(),
|
mockSelectPiece: vi.fn(),
|
||||||
mockSelectOption: vi.fn(),
|
mockSelectOption: vi.fn(),
|
||||||
mockResolveConfigValue: vi.fn(),
|
mockResolvePieceConfigValue: vi.fn(),
|
||||||
mockLoadPieceByIdentifier: vi.fn(),
|
mockLoadPieceByIdentifier: vi.fn(),
|
||||||
mockGetPieceDescription: vi.fn(() => ({
|
mockGetPieceDescription: vi.fn(() => ({
|
||||||
name: 'default',
|
name: 'default',
|
||||||
@ -60,7 +60,7 @@ vi.mock('../shared/utils/index.js', async (importOriginal) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
resolveConfigValue: (...args: unknown[]) => mockResolveConfigValue(...args),
|
resolvePieceConfigValue: (...args: unknown[]) => mockResolvePieceConfigValue(...args),
|
||||||
loadPieceByIdentifier: (...args: unknown[]) => mockLoadPieceByIdentifier(...args),
|
loadPieceByIdentifier: (...args: unknown[]) => mockLoadPieceByIdentifier(...args),
|
||||||
getPieceDescription: (...args: unknown[]) => mockGetPieceDescription(...args),
|
getPieceDescription: (...args: unknown[]) => mockGetPieceDescription(...args),
|
||||||
}));
|
}));
|
||||||
@ -126,7 +126,7 @@ beforeEach(() => {
|
|||||||
mockExistsSync.mockReturnValue(true);
|
mockExistsSync.mockReturnValue(true);
|
||||||
|
|
||||||
mockSelectPiece.mockResolvedValue('default');
|
mockSelectPiece.mockResolvedValue('default');
|
||||||
mockResolveConfigValue.mockReturnValue(3);
|
mockResolvePieceConfigValue.mockReturnValue(3);
|
||||||
mockLoadPieceByIdentifier.mockReturnValue(defaultPieceConfig);
|
mockLoadPieceByIdentifier.mockReturnValue(defaultPieceConfig);
|
||||||
mockSelectOption.mockResolvedValue('plan');
|
mockSelectOption.mockResolvedValue('plan');
|
||||||
mockRunRetryMode.mockResolvedValue({ action: 'execute', task: '追加指示A' });
|
mockRunRetryMode.mockResolvedValue({ action: 'execute', task: '追加指示A' });
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const {
|
|||||||
mockSuccess,
|
mockSuccess,
|
||||||
mockWarn,
|
mockWarn,
|
||||||
mockError,
|
mockError,
|
||||||
mockGetCurrentPiece,
|
mockResolveConfigValue,
|
||||||
} = vi.hoisted(() => ({
|
} = vi.hoisted(() => ({
|
||||||
mockRecoverInterruptedRunningTasks: vi.fn(),
|
mockRecoverInterruptedRunningTasks: vi.fn(),
|
||||||
mockGetTasksDir: vi.fn(),
|
mockGetTasksDir: vi.fn(),
|
||||||
@ -28,7 +28,7 @@ const {
|
|||||||
mockSuccess: vi.fn(),
|
mockSuccess: vi.fn(),
|
||||||
mockWarn: vi.fn(),
|
mockWarn: vi.fn(),
|
||||||
mockError: vi.fn(),
|
mockError: vi.fn(),
|
||||||
mockGetCurrentPiece: vi.fn(),
|
mockResolveConfigValue: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/task/index.js', () => ({
|
vi.mock('../infra/task/index.js', () => ({
|
||||||
@ -61,7 +61,7 @@ vi.mock('../shared/i18n/index.js', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../infra/config/index.js', () => ({
|
vi.mock('../infra/config/index.js', () => ({
|
||||||
getCurrentPiece: mockGetCurrentPiece,
|
resolveConfigValue: mockResolveConfigValue,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { watchTasks } from '../features/tasks/watch/index.js';
|
import { watchTasks } from '../features/tasks/watch/index.js';
|
||||||
@ -69,7 +69,7 @@ import { watchTasks } from '../features/tasks/watch/index.js';
|
|||||||
describe('watchTasks', () => {
|
describe('watchTasks', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
mockGetCurrentPiece.mockReturnValue('default');
|
mockResolveConfigValue.mockReturnValue('default');
|
||||||
mockRecoverInterruptedRunningTasks.mockReturnValue(0);
|
mockRecoverInterruptedRunningTasks.mockReturnValue(0);
|
||||||
mockGetTasksDir.mockReturnValue('/project/.takt/tasks.yaml');
|
mockGetTasksDir.mockReturnValue('/project/.takt/tasks.yaml');
|
||||||
mockExecuteAndCompleteTask.mockResolvedValue(true);
|
mockExecuteAndCompleteTask.mockResolvedValue(true);
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
* Registers all named subcommands (run, watch, add, list, switch, clear, eject, prompt, catalog).
|
* Registers all named subcommands (run, watch, add, list, switch, clear, eject, prompt, catalog).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { clearPersonaSessions, getCurrentPiece } from '../../infra/config/index.js';
|
import { clearPersonaSessions, resolveConfigValue } from '../../infra/config/index.js';
|
||||||
import { success } from '../../shared/ui/index.js';
|
import { success } from '../../shared/ui/index.js';
|
||||||
import { runAllTasks, addTask, watchTasks, listTasks } from '../../features/tasks/index.js';
|
import { runAllTasks, addTask, watchTasks, listTasks } from '../../features/tasks/index.js';
|
||||||
import { switchPiece, ejectBuiltin, ejectFacet, parseFacetType, VALID_FACET_TYPES, resetCategoriesToDefault, deploySkill } from '../../features/config/index.js';
|
import { switchPiece, ejectBuiltin, ejectFacet, parseFacetType, VALID_FACET_TYPES, resetCategoriesToDefault, deploySkill } from '../../features/config/index.js';
|
||||||
@ -17,7 +17,7 @@ program
|
|||||||
.command('run')
|
.command('run')
|
||||||
.description('Run all pending tasks from .takt/tasks.yaml')
|
.description('Run all pending tasks from .takt/tasks.yaml')
|
||||||
.action(async () => {
|
.action(async () => {
|
||||||
const piece = getCurrentPiece(resolvedCwd);
|
const piece = resolveConfigValue(resolvedCwd, 'piece');
|
||||||
await runAllTasks(resolvedCwd, piece, resolveAgentOverrides(program));
|
await runAllTasks(resolvedCwd, piece, resolveAgentOverrides(program));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,7 @@ import {
|
|||||||
dispatchConversationAction,
|
dispatchConversationAction,
|
||||||
type InteractiveModeResult,
|
type InteractiveModeResult,
|
||||||
} from '../../features/interactive/index.js';
|
} from '../../features/interactive/index.js';
|
||||||
import { getPieceDescription, resolveConfigValues } from '../../infra/config/index.js';
|
import { getPieceDescription, resolveConfigValue, resolveConfigValues } from '../../infra/config/index.js';
|
||||||
import { DEFAULT_PIECE_NAME } from '../../shared/constants.js';
|
|
||||||
import { program, resolvedCwd, pipelineMode } from './program.js';
|
import { program, resolvedCwd, pipelineMode } from './program.js';
|
||||||
import { resolveAgentOverrides, parseCreateWorktreeOption, isDirectTask } from './helpers.js';
|
import { resolveAgentOverrides, parseCreateWorktreeOption, isDirectTask } from './helpers.js';
|
||||||
import { loadTaskHistory } from './taskHistory.js';
|
import { loadTaskHistory } from './taskHistory.js';
|
||||||
@ -85,8 +84,12 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
const opts = program.opts();
|
const opts = program.opts();
|
||||||
const agentOverrides = resolveAgentOverrides(program);
|
const agentOverrides = resolveAgentOverrides(program);
|
||||||
const createWorktreeOverride = parseCreateWorktreeOption(opts.createWorktree as string | undefined);
|
const createWorktreeOverride = parseCreateWorktreeOption(opts.createWorktree as string | undefined);
|
||||||
|
const resolvedPipelinePiece = (opts.piece as string | undefined) ?? resolveConfigValue(resolvedCwd, 'piece');
|
||||||
|
const resolvedPipelineAutoPr = opts.autoPr === true
|
||||||
|
? true
|
||||||
|
: (resolveConfigValue(resolvedCwd, 'autoPr') ?? false);
|
||||||
const selectOptions: SelectAndExecuteOptions = {
|
const selectOptions: SelectAndExecuteOptions = {
|
||||||
autoPr: opts.autoPr === true,
|
autoPr: opts.autoPr === true ? true : undefined,
|
||||||
repo: opts.repo as string | undefined,
|
repo: opts.repo as string | undefined,
|
||||||
piece: opts.piece as string | undefined,
|
piece: opts.piece as string | undefined,
|
||||||
createWorktree: createWorktreeOverride,
|
createWorktree: createWorktreeOverride,
|
||||||
@ -97,9 +100,9 @@ export async function executeDefaultAction(task?: string): Promise<void> {
|
|||||||
const exitCode = await executePipeline({
|
const exitCode = await executePipeline({
|
||||||
issueNumber: opts.issue as number | undefined,
|
issueNumber: opts.issue as number | undefined,
|
||||||
task: opts.task as string | undefined,
|
task: opts.task as string | undefined,
|
||||||
piece: (opts.piece as string | undefined) ?? DEFAULT_PIECE_NAME,
|
piece: resolvedPipelinePiece,
|
||||||
branch: opts.branch as string | undefined,
|
branch: opts.branch as string | undefined,
|
||||||
autoPr: opts.autoPr === true,
|
autoPr: resolvedPipelineAutoPr,
|
||||||
repo: opts.repo as string | undefined,
|
repo: opts.repo as string | undefined,
|
||||||
skipGit: opts.skipGit === true,
|
skipGit: opts.skipGit === true,
|
||||||
cwd: resolvedCwd,
|
cwd: resolvedCwd,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import chalk from 'chalk';
|
|||||||
import type { PieceSource } from '../../infra/config/loaders/pieceResolver.js';
|
import type { PieceSource } from '../../infra/config/loaders/pieceResolver.js';
|
||||||
import { getLanguageResourcesDir } from '../../infra/resources/index.js';
|
import { getLanguageResourcesDir } from '../../infra/resources/index.js';
|
||||||
import { getGlobalConfigDir, getProjectConfigDir } from '../../infra/config/paths.js';
|
import { getGlobalConfigDir, getProjectConfigDir } from '../../infra/config/paths.js';
|
||||||
import { resolveConfigValues } from '../../infra/config/index.js';
|
import { resolvePieceConfigValues } from '../../infra/config/index.js';
|
||||||
import { section, error as logError, info } from '../../shared/ui/index.js';
|
import { section, error as logError, info } from '../../shared/ui/index.js';
|
||||||
|
|
||||||
const FACET_TYPES = [
|
const FACET_TYPES = [
|
||||||
@ -62,7 +62,7 @@ function getFacetDirs(
|
|||||||
facetType: FacetType,
|
facetType: FacetType,
|
||||||
cwd: string,
|
cwd: string,
|
||||||
): { dir: string; source: PieceSource }[] {
|
): { dir: string; source: PieceSource }[] {
|
||||||
const config = resolveConfigValues(cwd, ['enableBuiltinPieces', 'language']);
|
const config = resolvePieceConfigValues(cwd, ['enableBuiltinPieces', 'language']);
|
||||||
const dirs: { dir: string; source: PieceSource }[] = [];
|
const dirs: { dir: string; source: PieceSource }[] = [];
|
||||||
|
|
||||||
if (config.enableBuiltinPieces !== false) {
|
if (config.enableBuiltinPieces !== false) {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
loadPiece,
|
loadPiece,
|
||||||
getCurrentPiece,
|
resolveConfigValue,
|
||||||
setCurrentPiece,
|
setCurrentPiece,
|
||||||
} from '../../infra/config/index.js';
|
} from '../../infra/config/index.js';
|
||||||
import { info, success, error } from '../../shared/ui/index.js';
|
import { info, success, error } from '../../shared/ui/index.js';
|
||||||
@ -16,7 +16,7 @@ import { selectPiece } from '../pieceSelection/index.js';
|
|||||||
*/
|
*/
|
||||||
export async function switchPiece(cwd: string, pieceName?: string): Promise<boolean> {
|
export async function switchPiece(cwd: string, pieceName?: string): Promise<boolean> {
|
||||||
if (!pieceName) {
|
if (!pieceName) {
|
||||||
const current = getCurrentPiece(cwd);
|
const current = resolveConfigValue(cwd, 'piece');
|
||||||
info(`Current piece: ${current}`);
|
info(`Current piece: ${current}`);
|
||||||
|
|
||||||
const selected = await selectPiece(cwd, { fallbackToDefault: false });
|
const selected = await selectPiece(cwd, { fallbackToDefault: false });
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import {
|
|||||||
loadAllPiecesWithSources,
|
loadAllPiecesWithSources,
|
||||||
getPieceCategories,
|
getPieceCategories,
|
||||||
buildCategorizedPieces,
|
buildCategorizedPieces,
|
||||||
getCurrentPiece,
|
resolveConfigValue,
|
||||||
type PieceDirEntry,
|
type PieceDirEntry,
|
||||||
type PieceCategoryNode,
|
type PieceCategoryNode,
|
||||||
type CategorizedPieces,
|
type CategorizedPieces,
|
||||||
@ -522,7 +522,7 @@ export async function selectPiece(
|
|||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
const fallbackToDefault = options?.fallbackToDefault !== false;
|
const fallbackToDefault = options?.fallbackToDefault !== false;
|
||||||
const categoryConfig = getPieceCategories(cwd);
|
const categoryConfig = getPieceCategories(cwd);
|
||||||
const currentPiece = getCurrentPiece(cwd);
|
const currentPiece = resolveConfigValue(cwd, 'piece');
|
||||||
|
|
||||||
if (categoryConfig) {
|
if (categoryConfig) {
|
||||||
const allPieces = loadAllPiecesWithSources(cwd);
|
const allPieces = loadAllPiecesWithSources(cwd);
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
* Useful for debugging and understanding what prompts agents will receive.
|
* Useful for debugging and understanding what prompts agents will receive.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { loadPieceByIdentifier, getCurrentPiece, resolveConfigValue } from '../../infra/config/index.js';
|
import { loadPieceByIdentifier, resolvePieceConfigValue } from '../../infra/config/index.js';
|
||||||
import { InstructionBuilder } from '../../core/piece/instruction/InstructionBuilder.js';
|
import { InstructionBuilder } from '../../core/piece/instruction/InstructionBuilder.js';
|
||||||
import { ReportInstructionBuilder } from '../../core/piece/instruction/ReportInstructionBuilder.js';
|
import { ReportInstructionBuilder } from '../../core/piece/instruction/ReportInstructionBuilder.js';
|
||||||
import { StatusJudgmentBuilder } from '../../core/piece/instruction/StatusJudgmentBuilder.js';
|
import { StatusJudgmentBuilder } from '../../core/piece/instruction/StatusJudgmentBuilder.js';
|
||||||
@ -21,7 +21,7 @@ import { header, info, error, blankLine } from '../../shared/ui/index.js';
|
|||||||
* the Phase 1, Phase 2, and Phase 3 prompts with sample variable values.
|
* the Phase 1, Phase 2, and Phase 3 prompts with sample variable values.
|
||||||
*/
|
*/
|
||||||
export async function previewPrompts(cwd: string, pieceIdentifier?: string): Promise<void> {
|
export async function previewPrompts(cwd: string, pieceIdentifier?: string): Promise<void> {
|
||||||
const identifier = pieceIdentifier ?? getCurrentPiece(cwd);
|
const identifier = pieceIdentifier ?? resolvePieceConfigValue(cwd, 'piece');
|
||||||
const config = loadPieceByIdentifier(identifier, cwd);
|
const config = loadPieceByIdentifier(identifier, cwd);
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
@ -29,7 +29,7 @@ export async function previewPrompts(cwd: string, pieceIdentifier?: string): Pro
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const language = resolveConfigValue(cwd, 'language') as Language;
|
const language = resolvePieceConfigValue(cwd, 'language') as Language;
|
||||||
|
|
||||||
header(`Prompt Preview: ${config.name}`);
|
header(`Prompt Preview: ${config.name}`);
|
||||||
info(`Movements: ${config.movements.length}`);
|
info(`Movements: ${config.movements.length}`);
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import {
|
|||||||
updatePersonaSession,
|
updatePersonaSession,
|
||||||
loadWorktreeSessions,
|
loadWorktreeSessions,
|
||||||
updateWorktreeSession,
|
updateWorktreeSession,
|
||||||
resolveConfigValues,
|
resolvePieceConfigValues,
|
||||||
saveSessionState,
|
saveSessionState,
|
||||||
type SessionState,
|
type SessionState,
|
||||||
} from '../../../infra/config/index.js';
|
} from '../../../infra/config/index.js';
|
||||||
@ -317,7 +317,7 @@ export async function executePiece(
|
|||||||
|
|
||||||
// Load saved agent sessions only on retry; normal runs start with empty sessions
|
// Load saved agent sessions only on retry; normal runs start with empty sessions
|
||||||
const isWorktree = cwd !== projectCwd;
|
const isWorktree = cwd !== projectCwd;
|
||||||
const globalConfig = resolveConfigValues(
|
const globalConfig = resolvePieceConfigValues(
|
||||||
projectCwd,
|
projectCwd,
|
||||||
['notificationSound', 'notificationSoundEvents', 'provider', 'runtime', 'preventSleep', 'model', 'observability'],
|
['notificationSound', 'notificationSoundEvents', 'provider', 'runtime', 'preventSleep', 'model', 'observability'],
|
||||||
);
|
);
|
||||||
@ -326,7 +326,7 @@ export async function executePiece(
|
|||||||
const shouldNotifyIterationLimit = shouldNotify && notificationSoundEvents?.iterationLimit !== false;
|
const shouldNotifyIterationLimit = shouldNotify && notificationSoundEvents?.iterationLimit !== false;
|
||||||
const shouldNotifyPieceComplete = shouldNotify && notificationSoundEvents?.pieceComplete !== false;
|
const shouldNotifyPieceComplete = shouldNotify && notificationSoundEvents?.pieceComplete !== false;
|
||||||
const shouldNotifyPieceAbort = shouldNotify && notificationSoundEvents?.pieceAbort !== false;
|
const shouldNotifyPieceAbort = shouldNotify && notificationSoundEvents?.pieceAbort !== false;
|
||||||
const currentProvider = globalConfig.provider ?? 'claude';
|
const currentProvider = globalConfig.provider;
|
||||||
const effectivePieceConfig: PieceConfig = {
|
const effectivePieceConfig: PieceConfig = {
|
||||||
...pieceConfig,
|
...pieceConfig,
|
||||||
runtime: resolveRuntimeConfig(globalConfig.runtime, pieceConfig.runtime),
|
runtime: resolveRuntimeConfig(globalConfig.runtime, pieceConfig.runtime),
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
* instructBranch (instruct mode from takt list).
|
* instructBranch (instruct mode from takt list).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolveConfigValue } from '../../../infra/config/index.js';
|
import { resolvePieceConfigValue } from '../../../infra/config/index.js';
|
||||||
import { confirm } from '../../../shared/prompt/index.js';
|
import { confirm } from '../../../shared/prompt/index.js';
|
||||||
import { autoCommitAndPush } from '../../../infra/task/index.js';
|
import { autoCommitAndPush } from '../../../infra/task/index.js';
|
||||||
import { info, error, success } from '../../../shared/ui/index.js';
|
import { info, error, success } from '../../../shared/ui/index.js';
|
||||||
@ -23,11 +23,10 @@ export async function resolveAutoPr(optionAutoPr: boolean | undefined, cwd: stri
|
|||||||
return optionAutoPr;
|
return optionAutoPr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoPr = resolveConfigValue(cwd, 'autoPr');
|
const autoPr = resolvePieceConfigValue(cwd, 'autoPr');
|
||||||
if (typeof autoPr === 'boolean') {
|
if (typeof autoPr === 'boolean') {
|
||||||
return autoPr;
|
return autoPr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return confirm('Create pull request?', true);
|
return confirm('Create pull request?', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { resolveConfigValue } from '../../../infra/config/index.js';
|
import { resolvePieceConfigValue } from '../../../infra/config/index.js';
|
||||||
import { type TaskInfo, createSharedClone, summarizeTaskName, getCurrentBranch } from '../../../infra/task/index.js';
|
import { type TaskInfo, createSharedClone, summarizeTaskName, getCurrentBranch } from '../../../infra/task/index.js';
|
||||||
import { withProgress } from '../../../shared/ui/index.js';
|
import { withProgress } from '../../../shared/ui/index.js';
|
||||||
import { getTaskSlugFromTaskDir } from '../../../shared/utils/taskPaths.js';
|
import { getTaskSlugFromTaskDir } from '../../../shared/utils/taskPaths.js';
|
||||||
@ -141,7 +141,7 @@ export async function resolveTaskExecution(
|
|||||||
if (data.auto_pr !== undefined) {
|
if (data.auto_pr !== undefined) {
|
||||||
autoPr = data.auto_pr;
|
autoPr = data.auto_pr;
|
||||||
} else {
|
} else {
|
||||||
autoPr = resolveConfigValue(defaultCwd, 'autoPr') ?? false;
|
autoPr = resolvePieceConfigValue(defaultCwd, 'autoPr') ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* Session management helpers for agent execution
|
* Session management helpers for agent execution
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { loadPersonaSessions, updatePersonaSession, resolveConfigValue } from '../../../infra/config/index.js';
|
import { loadPersonaSessions, updatePersonaSession, resolvePieceConfigValue } from '../../../infra/config/index.js';
|
||||||
import type { AgentResponse } from '../../../core/models/index.js';
|
import type { AgentResponse } from '../../../core/models/index.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,7 +15,7 @@ export async function withPersonaSession(
|
|||||||
fn: (sessionId?: string) => Promise<AgentResponse>,
|
fn: (sessionId?: string) => Promise<AgentResponse>,
|
||||||
provider?: string
|
provider?: string
|
||||||
): Promise<AgentResponse> {
|
): Promise<AgentResponse> {
|
||||||
const resolvedProvider = provider ?? resolveConfigValue(cwd, 'provider') ?? 'claude';
|
const resolvedProvider = provider ?? resolvePieceConfigValue(cwd, 'provider');
|
||||||
const sessions = loadPersonaSessions(cwd, resolvedProvider);
|
const sessions = loadPersonaSessions(cwd, resolvedProvider);
|
||||||
const sessionId = sessions[personaName];
|
const sessionId = sessions[personaName];
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* Task execution logic
|
* Task execution logic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { loadPieceByIdentifier, isPiecePath, resolveConfigValues } from '../../../infra/config/index.js';
|
import { loadPieceByIdentifier, isPiecePath, resolvePieceConfigValues } from '../../../infra/config/index.js';
|
||||||
import { TaskRunner, type TaskInfo } from '../../../infra/task/index.js';
|
import { TaskRunner, type TaskInfo } from '../../../infra/task/index.js';
|
||||||
import {
|
import {
|
||||||
header,
|
header,
|
||||||
@ -86,7 +86,7 @@ async function executeTaskWithResult(options: ExecuteTaskOptions): Promise<Piece
|
|||||||
movements: pieceConfig.movements.map((s: { name: string }) => s.name),
|
movements: pieceConfig.movements.map((s: { name: string }) => s.name),
|
||||||
});
|
});
|
||||||
|
|
||||||
const config = resolveConfigValues(projectCwd, [
|
const config = resolvePieceConfigValues(projectCwd, [
|
||||||
'language',
|
'language',
|
||||||
'provider',
|
'provider',
|
||||||
'model',
|
'model',
|
||||||
@ -238,7 +238,7 @@ export async function runAllTasks(
|
|||||||
options?: TaskExecutionOptions,
|
options?: TaskExecutionOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const taskRunner = new TaskRunner(cwd);
|
const taskRunner = new TaskRunner(cwd);
|
||||||
const globalConfig = resolveConfigValues(
|
const globalConfig = resolvePieceConfigValues(
|
||||||
cwd,
|
cwd,
|
||||||
['notificationSound', 'notificationSoundEvents', 'concurrency', 'taskPollIntervalMs'],
|
['notificationSound', 'notificationSoundEvents', 'concurrency', 'taskPollIntervalMs'],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import {
|
|||||||
import { type RunSessionContext, formatRunSessionForPrompt } from '../../interactive/runSessionReader.js';
|
import { type RunSessionContext, formatRunSessionForPrompt } from '../../interactive/runSessionReader.js';
|
||||||
import { loadTemplate } from '../../../shared/prompts/index.js';
|
import { loadTemplate } from '../../../shared/prompts/index.js';
|
||||||
import { getLabelObject } from '../../../shared/i18n/index.js';
|
import { getLabelObject } from '../../../shared/i18n/index.js';
|
||||||
import { resolveConfigValues } from '../../../infra/config/index.js';
|
import { resolvePieceConfigValues } from '../../../infra/config/index.js';
|
||||||
|
|
||||||
export type InstructModeAction = 'execute' | 'save_task' | 'cancel';
|
export type InstructModeAction = 'execute' | 'save_task' | 'cancel';
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ export async function runInstructMode(
|
|||||||
pieceContext?: PieceContext,
|
pieceContext?: PieceContext,
|
||||||
runSessionContext?: RunSessionContext,
|
runSessionContext?: RunSessionContext,
|
||||||
): Promise<InstructModeResult> {
|
): Promise<InstructModeResult> {
|
||||||
const globalConfig = resolveConfigValues(cwd, ['language', 'provider']);
|
const globalConfig = resolvePieceConfigValues(cwd, ['language', 'provider']);
|
||||||
const lang = resolveLanguage(globalConfig.language);
|
const lang = resolveLanguage(globalConfig.language);
|
||||||
|
|
||||||
if (!globalConfig.provider) {
|
if (!globalConfig.provider) {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
TaskRunner,
|
TaskRunner,
|
||||||
detectDefaultBranch,
|
detectDefaultBranch,
|
||||||
} from '../../../infra/task/index.js';
|
} from '../../../infra/task/index.js';
|
||||||
import { resolveConfigValues, getPieceDescription } from '../../../infra/config/index.js';
|
import { resolvePieceConfigValues, getPieceDescription } from '../../../infra/config/index.js';
|
||||||
import { info, error as logError } from '../../../shared/ui/index.js';
|
import { info, error as logError } from '../../../shared/ui/index.js';
|
||||||
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
|
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
|
||||||
import { runInstructMode } from './instructMode.js';
|
import { runInstructMode } from './instructMode.js';
|
||||||
@ -93,7 +93,7 @@ export async function instructBranch(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalConfig = resolveConfigValues(projectDir, ['interactivePreviewMovements', 'language']);
|
const globalConfig = resolvePieceConfigValues(projectDir, ['interactivePreviewMovements', 'language']);
|
||||||
const pieceDesc = getPieceDescription(selectedPiece, projectDir, globalConfig.interactivePreviewMovements);
|
const pieceDesc = getPieceDescription(selectedPiece, projectDir, globalConfig.interactivePreviewMovements);
|
||||||
const pieceContext: PieceContext = {
|
const pieceContext: PieceContext = {
|
||||||
name: pieceDesc.name,
|
name: pieceDesc.name,
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import type { TaskListItem } from '../../../infra/task/index.js';
|
import type { TaskListItem } from '../../../infra/task/index.js';
|
||||||
import { TaskRunner } from '../../../infra/task/index.js';
|
import { TaskRunner } from '../../../infra/task/index.js';
|
||||||
import { loadPieceByIdentifier, resolveConfigValue, getPieceDescription } from '../../../infra/config/index.js';
|
import { loadPieceByIdentifier, resolvePieceConfigValue, getPieceDescription } from '../../../infra/config/index.js';
|
||||||
import { selectPiece } from '../../pieceSelection/index.js';
|
import { selectPiece } from '../../pieceSelection/index.js';
|
||||||
import { selectOption } from '../../../shared/prompt/index.js';
|
import { selectOption } from '../../../shared/prompt/index.js';
|
||||||
import { info, header, blankLine, status } from '../../../shared/ui/index.js';
|
import { info, header, blankLine, status } from '../../../shared/ui/index.js';
|
||||||
@ -133,7 +133,7 @@ export async function retryFailedTask(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewCount = resolveConfigValue(projectDir, 'interactivePreviewMovements');
|
const previewCount = resolvePieceConfigValue(projectDir, 'interactivePreviewMovements');
|
||||||
const pieceConfig = loadPieceByIdentifier(selectedPiece, projectDir);
|
const pieceConfig = loadPieceByIdentifier(selectedPiece, projectDir);
|
||||||
|
|
||||||
if (!pieceConfig) {
|
if (!pieceConfig) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TaskRunner, type TaskInfo, TaskWatcher } from '../../../infra/task/index.js';
|
import { TaskRunner, type TaskInfo, TaskWatcher } from '../../../infra/task/index.js';
|
||||||
import { getCurrentPiece } from '../../../infra/config/index.js';
|
import { resolveConfigValue } from '../../../infra/config/index.js';
|
||||||
import {
|
import {
|
||||||
header,
|
header,
|
||||||
info,
|
info,
|
||||||
@ -15,7 +15,6 @@ import {
|
|||||||
blankLine,
|
blankLine,
|
||||||
} from '../../../shared/ui/index.js';
|
} from '../../../shared/ui/index.js';
|
||||||
import { executeAndCompleteTask } from '../execute/taskExecution.js';
|
import { executeAndCompleteTask } from '../execute/taskExecution.js';
|
||||||
import { DEFAULT_PIECE_NAME } from '../../../shared/constants.js';
|
|
||||||
import { EXIT_SIGINT } from '../../../shared/exitCodes.js';
|
import { EXIT_SIGINT } from '../../../shared/exitCodes.js';
|
||||||
import { ShutdownManager } from '../execute/shutdownManager.js';
|
import { ShutdownManager } from '../execute/shutdownManager.js';
|
||||||
import type { TaskExecutionOptions } from '../execute/types.js';
|
import type { TaskExecutionOptions } from '../execute/types.js';
|
||||||
@ -25,7 +24,7 @@ import type { TaskExecutionOptions } from '../execute/types.js';
|
|||||||
* Runs until Ctrl+C.
|
* Runs until Ctrl+C.
|
||||||
*/
|
*/
|
||||||
export async function watchTasks(cwd: string, options?: TaskExecutionOptions): Promise<void> {
|
export async function watchTasks(cwd: string, options?: TaskExecutionOptions): Promise<void> {
|
||||||
const pieceName = getCurrentPiece(cwd) || DEFAULT_PIECE_NAME;
|
const pieceName = resolveConfigValue(cwd, 'piece');
|
||||||
const taskRunner = new TaskRunner(cwd);
|
const taskRunner = new TaskRunner(cwd);
|
||||||
const watcher = new TaskWatcher(cwd);
|
const watcher = new TaskWatcher(cwd);
|
||||||
const recovered = taskRunner.recoverInterruptedRunningTasks();
|
const recovered = taskRunner.recoverInterruptedRunningTasks();
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
||||||
import { dirname, join } from 'node:path';
|
import { dirname, join } from 'node:path';
|
||||||
import { getGlobalConfigDir } from '../paths.js';
|
import { getGlobalConfigDir } from '../paths.js';
|
||||||
import { resolveConfigValue } from '../resolveConfigValue.js';
|
import { resolvePieceConfigValue } from '../resolvePieceConfigValue.js';
|
||||||
|
|
||||||
const INITIAL_USER_CATEGORIES_CONTENT = 'piece_categories: {}\n';
|
const INITIAL_USER_CATEGORIES_CONTENT = 'piece_categories: {}\n';
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ function getDefaultPieceCategoriesPath(): string {
|
|||||||
|
|
||||||
/** Get the path to the user's piece categories file. */
|
/** Get the path to the user's piece categories file. */
|
||||||
export function getPieceCategoriesPath(cwd: string): string {
|
export function getPieceCategoriesPath(cwd: string): string {
|
||||||
const pieceCategoriesFile = resolveConfigValue(cwd, 'pieceCategoriesFile');
|
const pieceCategoriesFile = resolvePieceConfigValue(cwd, 'pieceCategoriesFile');
|
||||||
if (pieceCategoriesFile) {
|
if (pieceCategoriesFile) {
|
||||||
return pieceCategoriesFile;
|
return pieceCategoriesFile;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,3 +7,4 @@ export * from './loaders/index.js';
|
|||||||
export * from './global/index.js';
|
export * from './global/index.js';
|
||||||
export * from './project/index.js';
|
export * from './project/index.js';
|
||||||
export * from './resolveConfigValue.js';
|
export * from './resolveConfigValue.js';
|
||||||
|
export * from './resolvePieceConfigValue.js';
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { envVarNameFromPath } from './env/config-env-overrides.js';
|
|||||||
|
|
||||||
export interface LoadedConfig extends GlobalConfig {
|
export interface LoadedConfig extends GlobalConfig {
|
||||||
piece: string;
|
piece: string;
|
||||||
|
provider: NonNullable<GlobalConfig['provider']>;
|
||||||
verbose: boolean;
|
verbose: boolean;
|
||||||
providerOptions?: MovementProviderOptions;
|
providerOptions?: MovementProviderOptions;
|
||||||
providerProfiles?: ProviderPermissionProfiles;
|
providerProfiles?: ProviderPermissionProfiles;
|
||||||
@ -15,12 +16,13 @@ export interface LoadedConfig extends GlobalConfig {
|
|||||||
export function loadConfig(projectDir: string): LoadedConfig {
|
export function loadConfig(projectDir: string): LoadedConfig {
|
||||||
const global = loadGlobalConfig();
|
const global = loadGlobalConfig();
|
||||||
const project = loadProjectConfig(projectDir);
|
const project = loadProjectConfig(projectDir);
|
||||||
const provider = project.provider ?? global.provider;
|
const provider = (project.provider ?? global.provider ?? 'claude') as NonNullable<GlobalConfig['provider']>;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...global,
|
...global,
|
||||||
piece: project.piece ?? 'default',
|
piece: project.piece ?? 'default',
|
||||||
provider,
|
provider,
|
||||||
|
autoPr: project.auto_pr ?? global.autoPr,
|
||||||
model: resolveModel(global, provider),
|
model: resolveModel(global, provider),
|
||||||
verbose: resolveVerbose(project.verbose, global.verbose),
|
verbose: resolveVerbose(project.verbose, global.verbose),
|
||||||
providerOptions: mergeProviderOptions(global.providerOptions, project.providerOptions),
|
providerOptions: mergeProviderOptions(global.providerOptions, project.providerOptions),
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { z } from 'zod/v4';
|
|||||||
import { getPieceCategoriesPath } from '../global/pieceCategories.js';
|
import { getPieceCategoriesPath } from '../global/pieceCategories.js';
|
||||||
import { getLanguageResourcesDir } from '../../resources/index.js';
|
import { getLanguageResourcesDir } from '../../resources/index.js';
|
||||||
import { listBuiltinPieceNames } from './pieceResolver.js';
|
import { listBuiltinPieceNames } from './pieceResolver.js';
|
||||||
import { resolveConfigValues } from '../resolveConfigValue.js';
|
import { resolvePieceConfigValues } from '../resolvePieceConfigValue.js';
|
||||||
import type { PieceWithSource } from './pieceResolver.js';
|
import type { PieceWithSource } from './pieceResolver.js';
|
||||||
|
|
||||||
const CategoryConfigSchema = z.object({
|
const CategoryConfigSchema = z.object({
|
||||||
@ -233,7 +233,7 @@ function resolveOthersCategoryName(defaultConfig: ParsedCategoryConfig, userConf
|
|||||||
* Returns null if file doesn't exist or has no piece_categories.
|
* Returns null if file doesn't exist or has no piece_categories.
|
||||||
*/
|
*/
|
||||||
export function loadDefaultCategories(cwd: string): CategoryConfig | null {
|
export function loadDefaultCategories(cwd: string): CategoryConfig | null {
|
||||||
const { language: lang } = resolveConfigValues(cwd, ['language']);
|
const { language: lang } = resolvePieceConfigValues(cwd, ['language']);
|
||||||
const filePath = join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
|
const filePath = join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
|
||||||
const parsed = loadCategoryConfigFromPath(filePath, filePath);
|
const parsed = loadCategoryConfigFromPath(filePath, filePath);
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ export function loadDefaultCategories(cwd: string): CategoryConfig | null {
|
|||||||
|
|
||||||
/** Get the path to the builtin default categories file. */
|
/** Get the path to the builtin default categories file. */
|
||||||
export function getDefaultCategoriesPath(cwd: string): string {
|
export function getDefaultCategoriesPath(cwd: string): string {
|
||||||
const { language: lang } = resolveConfigValues(cwd, ['language']);
|
const { language: lang } = resolvePieceConfigValues(cwd, ['language']);
|
||||||
return join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
|
return join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,7 +378,7 @@ export function buildCategorizedPieces(
|
|||||||
config: CategoryConfig,
|
config: CategoryConfig,
|
||||||
cwd: string,
|
cwd: string,
|
||||||
): CategorizedPieces {
|
): CategorizedPieces {
|
||||||
const globalConfig = resolveConfigValues(cwd, ['enableBuiltinPieces', 'disabledBuiltins']);
|
const globalConfig = resolvePieceConfigValues(cwd, ['enableBuiltinPieces', 'disabledBuiltins']);
|
||||||
const ignoreMissing = new Set<string>();
|
const ignoreMissing = new Set<string>();
|
||||||
if (globalConfig.enableBuiltinPieces === false) {
|
if (globalConfig.enableBuiltinPieces === false) {
|
||||||
for (const name of listBuiltinPieceNames(cwd, { includeDisabled: true })) {
|
for (const name of listBuiltinPieceNames(cwd, { includeDisabled: true })) {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { parse as parseYaml } from 'yaml';
|
|||||||
import type { z } from 'zod';
|
import type { z } from 'zod';
|
||||||
import { PieceConfigRawSchema, PieceMovementRawSchema } from '../../../core/models/index.js';
|
import { PieceConfigRawSchema, PieceMovementRawSchema } from '../../../core/models/index.js';
|
||||||
import type { PieceConfig, PieceMovement, PieceRule, OutputContractEntry, OutputContractItem, LoopMonitorConfig, LoopMonitorJudge, ArpeggioMovementConfig, ArpeggioMergeMovementConfig, TeamLeaderConfig } from '../../../core/models/index.js';
|
import type { PieceConfig, PieceMovement, PieceRule, OutputContractEntry, OutputContractItem, LoopMonitorConfig, LoopMonitorJudge, ArpeggioMovementConfig, ArpeggioMergeMovementConfig, TeamLeaderConfig } from '../../../core/models/index.js';
|
||||||
import { resolveConfigValue } from '../resolveConfigValue.js';
|
import { resolvePieceConfigValue } from '../resolvePieceConfigValue.js';
|
||||||
import {
|
import {
|
||||||
type PieceSections,
|
type PieceSections,
|
||||||
type FacetResolutionContext,
|
type FacetResolutionContext,
|
||||||
@ -439,7 +439,7 @@ export function loadPieceFromFile(filePath: string, projectDir: string): PieceCo
|
|||||||
const pieceDir = dirname(filePath);
|
const pieceDir = dirname(filePath);
|
||||||
|
|
||||||
const context: FacetResolutionContext = {
|
const context: FacetResolutionContext = {
|
||||||
lang: resolveConfigValue(projectDir, 'language'),
|
lang: resolvePieceConfigValue(projectDir, 'language'),
|
||||||
projectDir,
|
projectDir,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { join, resolve, isAbsolute } from 'node:path';
|
|||||||
import { homedir } from 'node:os';
|
import { homedir } from 'node:os';
|
||||||
import type { PieceConfig, PieceMovement, InteractiveMode } from '../../../core/models/index.js';
|
import type { PieceConfig, PieceMovement, InteractiveMode } from '../../../core/models/index.js';
|
||||||
import { getGlobalPiecesDir, getBuiltinPiecesDir, getProjectConfigDir } from '../paths.js';
|
import { getGlobalPiecesDir, getBuiltinPiecesDir, getProjectConfigDir } from '../paths.js';
|
||||||
import { resolveConfigValues } from '../resolveConfigValue.js';
|
import { resolvePieceConfigValues } from '../resolvePieceConfigValue.js';
|
||||||
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
|
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
|
||||||
import { loadPieceFromFile } from './pieceParser.js';
|
import { loadPieceFromFile } from './pieceParser.js';
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ export interface PieceWithSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function listBuiltinPieceNames(cwd: string, options?: { includeDisabled?: boolean }): string[] {
|
export function listBuiltinPieceNames(cwd: string, options?: { includeDisabled?: boolean }): string[] {
|
||||||
const config = resolveConfigValues(cwd, ['language', 'disabledBuiltins']);
|
const config = resolvePieceConfigValues(cwd, ['language', 'disabledBuiltins']);
|
||||||
const lang = config.language;
|
const lang = config.language;
|
||||||
const dir = getBuiltinPiecesDir(lang);
|
const dir = getBuiltinPiecesDir(lang);
|
||||||
const disabled = options?.includeDisabled ? undefined : (config.disabledBuiltins ?? []);
|
const disabled = options?.includeDisabled ? undefined : (config.disabledBuiltins ?? []);
|
||||||
@ -37,7 +37,7 @@ export function listBuiltinPieceNames(cwd: string, options?: { includeDisabled?:
|
|||||||
|
|
||||||
/** Get builtin piece by name */
|
/** Get builtin piece by name */
|
||||||
export function getBuiltinPiece(name: string, projectCwd: string): PieceConfig | null {
|
export function getBuiltinPiece(name: string, projectCwd: string): PieceConfig | null {
|
||||||
const config = resolveConfigValues(projectCwd, ['enableBuiltinPieces', 'language', 'disabledBuiltins']);
|
const config = resolvePieceConfigValues(projectCwd, ['enableBuiltinPieces', 'language', 'disabledBuiltins']);
|
||||||
if (config.enableBuiltinPieces === false) return null;
|
if (config.enableBuiltinPieces === false) return null;
|
||||||
const lang = config.language;
|
const lang = config.language;
|
||||||
const disabled = config.disabledBuiltins ?? [];
|
const disabled = config.disabledBuiltins ?? [];
|
||||||
@ -373,7 +373,7 @@ function* iteratePieceDir(
|
|||||||
|
|
||||||
/** Get the 3-layer directory list (builtin → user → project-local) */
|
/** Get the 3-layer directory list (builtin → user → project-local) */
|
||||||
function getPieceDirs(cwd: string): { dir: string; source: PieceSource; disabled?: string[] }[] {
|
function getPieceDirs(cwd: string): { dir: string; source: PieceSource; disabled?: string[] }[] {
|
||||||
const config = resolveConfigValues(cwd, ['enableBuiltinPieces', 'language', 'disabledBuiltins']);
|
const config = resolvePieceConfigValues(cwd, ['enableBuiltinPieces', 'language', 'disabledBuiltins']);
|
||||||
const disabled = config.disabledBuiltins ?? [];
|
const disabled = config.disabledBuiltins ?? [];
|
||||||
const lang = config.language;
|
const lang = config.language;
|
||||||
const dirs: { dir: string; source: PieceSource; disabled?: string[] }[] = [];
|
const dirs: { dir: string; source: PieceSource; disabled?: string[] }[] = [];
|
||||||
|
|||||||
17
src/infra/config/resolvePieceConfigValue.ts
Normal file
17
src/infra/config/resolvePieceConfigValue.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { ConfigParameterKey } from './resolveConfigValue.js';
|
||||||
|
import { resolveConfigValue, resolveConfigValues } from './resolveConfigValue.js';
|
||||||
|
import type { LoadedConfig } from './loadConfig.js';
|
||||||
|
|
||||||
|
export function resolvePieceConfigValue<K extends ConfigParameterKey>(
|
||||||
|
projectDir: string,
|
||||||
|
key: K,
|
||||||
|
): LoadedConfig[K] {
|
||||||
|
return resolveConfigValue(projectDir, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolvePieceConfigValues<K extends ConfigParameterKey>(
|
||||||
|
projectDir: string,
|
||||||
|
keys: readonly K[],
|
||||||
|
): Pick<LoadedConfig, K> {
|
||||||
|
return resolveConfigValues(projectDir, keys);
|
||||||
|
}
|
||||||
@ -10,7 +10,11 @@ export interface ProjectLocalConfig {
|
|||||||
/** Current piece name */
|
/** Current piece name */
|
||||||
piece?: string;
|
piece?: string;
|
||||||
/** Provider selection for agent runtime */
|
/** Provider selection for agent runtime */
|
||||||
provider?: 'claude' | 'codex' | 'opencode';
|
provider?: 'claude' | 'codex' | 'opencode' | 'mock';
|
||||||
|
/** Auto-create PR after worktree execution */
|
||||||
|
auto_pr?: boolean;
|
||||||
|
/** Auto-create PR after worktree execution (camelCase alias) */
|
||||||
|
autoPr?: boolean;
|
||||||
/** Verbose output mode */
|
/** Verbose output mode */
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
/** Provider-specific options (overrides global, overridden by piece/movement) */
|
/** Provider-specific options (overrides global, overridden by piece/movement) */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user