Issue/90 fix windows (#91)
* Window対応および Codexが.gitを必要とする問題があるので.gitがみつからない場合はエラーとする fix #90 * 文字化け修正
This commit is contained in:
parent
54ade15dcb
commit
8e509e13c6
@ -476,9 +476,6 @@ model: sonnet # Default model (optional)
|
||||
anthropic_api_key: sk-ant-... # For Claude (Anthropic)
|
||||
# openai_api_key: sk-... # For Codex (OpenAI)
|
||||
|
||||
trusted_directories:
|
||||
- /path/to/trusted/dir
|
||||
|
||||
# Pipeline execution configuration (optional)
|
||||
# Customize branch names, commit messages, and PR body.
|
||||
# pipeline:
|
||||
@ -490,6 +487,8 @@ trusted_directories:
|
||||
# Closes #{issue}
|
||||
```
|
||||
|
||||
**Note:** The Codex SDK requires running inside a Git repository. `--skip-git-repo-check` is only available in the Codex CLI.
|
||||
|
||||
**API Key Configuration Methods:**
|
||||
|
||||
1. **Set via environment variables**:
|
||||
|
||||
5
bin/takt
5
bin/takt
@ -9,7 +9,7 @@
|
||||
* - npm exec takt
|
||||
*/
|
||||
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@ -19,7 +19,8 @@ const __dirname = dirname(__filename);
|
||||
const cliPath = join(__dirname, '..', 'dist', 'app', 'cli', 'index.js');
|
||||
|
||||
try {
|
||||
await import(cliPath);
|
||||
const cliUrl = pathToFileURL(cliPath).href;
|
||||
await import(cliUrl);
|
||||
} catch (err) {
|
||||
console.error('Failed to load TAKT CLI. Have you run "npm run build"?');
|
||||
console.error(err.message);
|
||||
|
||||
@ -472,9 +472,6 @@ model: sonnet # デフォルトモデル(オプション)
|
||||
anthropic_api_key: sk-ant-... # Claude (Anthropic) を使う場合
|
||||
# openai_api_key: sk-... # Codex (OpenAI) を使う場合
|
||||
|
||||
trusted_directories:
|
||||
- /path/to/trusted/dir
|
||||
|
||||
# パイプライン実行設定(オプション)
|
||||
# ブランチ名、コミットメッセージ、PRの本文をカスタマイズできます。
|
||||
# pipeline:
|
||||
@ -486,6 +483,8 @@ trusted_directories:
|
||||
# Closes #{issue}
|
||||
```
|
||||
|
||||
**注意:** Codex SDK は Git 管理下のディレクトリでのみ動作します。`--skip-git-repo-check` は Codex CLI 専用です。
|
||||
|
||||
**API Key の設定方法:**
|
||||
|
||||
1. **環境変数で設定**:
|
||||
|
||||
734
package-lock.json
generated
734
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,6 @@
|
||||
# Language setting (en or ja)
|
||||
language: en
|
||||
|
||||
# Trusted directories - projects in these directories skip confirmation prompts
|
||||
trusted_directories: []
|
||||
|
||||
# Default piece to use when no piece is specified
|
||||
default_piece: default
|
||||
|
||||
|
||||
@ -4,10 +4,7 @@
|
||||
# 言語設定 (en または ja)
|
||||
language: ja
|
||||
|
||||
# 信頼済みディレクトリ - これらのディレクトリ内のプロジェクトは確認プロンプトをスキップします
|
||||
trusted_directories: []
|
||||
|
||||
# デフォルトピース - ピースが指定されていない場合に使用します
|
||||
# デフォルトのピース - 指定がない場合に使用します
|
||||
default_piece: default
|
||||
|
||||
# ログレベル: debug, info, warn, error
|
||||
|
||||
@ -83,7 +83,6 @@ describe('GlobalConfig load/save with API keys', () => {
|
||||
it('should load config with API keys from YAML', () => {
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -100,7 +99,6 @@ describe('GlobalConfig load/save with API keys', () => {
|
||||
it('should load config without API keys', () => {
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -116,7 +114,6 @@ describe('GlobalConfig load/save with API keys', () => {
|
||||
// Write initial config
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -136,7 +133,6 @@ describe('GlobalConfig load/save with API keys', () => {
|
||||
it('should not persist API keys when not set', () => {
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -173,7 +169,6 @@ describe('resolveAnthropicApiKey', () => {
|
||||
process.env['TAKT_ANTHROPIC_API_KEY'] = 'sk-ant-from-env';
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -189,7 +184,6 @@ describe('resolveAnthropicApiKey', () => {
|
||||
delete process.env['TAKT_ANTHROPIC_API_KEY'];
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -205,7 +199,6 @@ describe('resolveAnthropicApiKey', () => {
|
||||
delete process.env['TAKT_ANTHROPIC_API_KEY'];
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -247,7 +240,6 @@ describe('resolveOpenaiApiKey', () => {
|
||||
process.env['TAKT_OPENAI_API_KEY'] = 'sk-openai-from-env';
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -263,7 +255,6 @@ describe('resolveOpenaiApiKey', () => {
|
||||
delete process.env['TAKT_OPENAI_API_KEY'];
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
@ -279,7 +270,6 @@ describe('resolveOpenaiApiKey', () => {
|
||||
delete process.env['TAKT_OPENAI_API_KEY'];
|
||||
const yaml = [
|
||||
'language: en',
|
||||
'trusted_directories: []',
|
||||
'default_piece: default',
|
||||
'log_level: info',
|
||||
'provider: claude',
|
||||
|
||||
37
src/__tests__/cli-wrapper.test.ts
Normal file
37
src/__tests__/cli-wrapper.test.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Tests for the CLI wrapper URL handling.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { posix, win32, resolve } from 'node:path';
|
||||
|
||||
describe('cli wrapper import URL', () => {
|
||||
it('builds a file URL for Windows paths', () => {
|
||||
const winPath = win32.join('C:\\', 'work', 'git', 'takt', 'dist', 'app', 'cli', 'index.js');
|
||||
const url = pathToFileURL(winPath).href;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
expect(url).toBe('file:///C:/work/git/takt/dist/app/cli/index.js');
|
||||
return;
|
||||
}
|
||||
|
||||
expect(url).toMatch(/C:%5Cwork%5Cgit%5Ctakt%5Cdist%5Capp%5Ccli%5Cindex\.js$/);
|
||||
});
|
||||
|
||||
it('builds a file URL for POSIX paths', () => {
|
||||
const posixPath = posix.join('/', 'usr', 'local', 'lib', 'takt', 'dist', 'app', 'cli', 'index.js');
|
||||
const url = pathToFileURL(posixPath).href;
|
||||
|
||||
expect(url).toBe('file:///usr/local/lib/takt/dist/app/cli/index.js');
|
||||
});
|
||||
|
||||
it('uses pathToFileURL in the npm wrapper', async () => {
|
||||
const wrapperPath = resolve('bin', 'takt');
|
||||
const wrapperContents = await readFile(wrapperPath, 'utf8');
|
||||
|
||||
expect(wrapperContents).toContain('pathToFileURL');
|
||||
expect(wrapperContents).toContain('pathToFileURL(cliPath)');
|
||||
});
|
||||
});
|
||||
@ -39,7 +39,6 @@ describe('loadGlobalConfig', () => {
|
||||
const config = loadGlobalConfig();
|
||||
|
||||
expect(config.language).toBe('en');
|
||||
expect(config.trustedDirectories).toEqual([]);
|
||||
expect(config.defaultPiece).toBe('default');
|
||||
expect(config.logLevel).toBe('info');
|
||||
expect(config.provider).toBe('claude');
|
||||
|
||||
@ -156,6 +156,12 @@ describe('worktree branch deletion', () => {
|
||||
});
|
||||
|
||||
it('should delete regular (non-worktree) branches normally', () => {
|
||||
const defaultBranch = execFileSync('git', ['branch', '--show-current'], {
|
||||
cwd: testDir,
|
||||
encoding: 'utf-8',
|
||||
stdio: 'pipe',
|
||||
}).trim();
|
||||
|
||||
// Create a regular local branch
|
||||
const branchName = 'takt/20260203T1002-regular-branch';
|
||||
execFileSync('git', ['checkout', '-b', branchName], { cwd: testDir });
|
||||
@ -166,7 +172,7 @@ describe('worktree branch deletion', () => {
|
||||
execFileSync('git', ['commit', '-m', 'Test change'], { cwd: testDir });
|
||||
|
||||
// Switch back to main
|
||||
execFileSync('git', ['checkout', 'master'], { cwd: testDir });
|
||||
execFileSync('git', ['checkout', defaultBranch || 'main'], { cwd: testDir });
|
||||
|
||||
// Verify branch exists
|
||||
const branchesBefore = listTaktBranches(testDir);
|
||||
|
||||
@ -201,7 +201,6 @@ describe('GlobalConfigSchema', () => {
|
||||
const config = {};
|
||||
const result = GlobalConfigSchema.parse(config);
|
||||
|
||||
expect(result.trusted_directories).toEqual([]);
|
||||
expect(result.default_piece).toBe('default');
|
||||
expect(result.log_level).toBe('info');
|
||||
expect(result.provider).toBe('claude');
|
||||
@ -209,13 +208,11 @@ describe('GlobalConfigSchema', () => {
|
||||
|
||||
it('should accept valid config', () => {
|
||||
const config = {
|
||||
trusted_directories: ['/home/user/projects'],
|
||||
default_piece: 'custom',
|
||||
log_level: 'debug' as const,
|
||||
};
|
||||
|
||||
const result = GlobalConfigSchema.parse(config);
|
||||
expect(result.trusted_directories).toHaveLength(1);
|
||||
expect(result.log_level).toBe('debug');
|
||||
});
|
||||
});
|
||||
|
||||
@ -75,7 +75,6 @@ describe('executePipeline', () => {
|
||||
// Default: no pipeline config
|
||||
mockLoadGlobalConfig.mockReturnValue({
|
||||
language: 'en',
|
||||
trustedDirectories: [],
|
||||
defaultPiece: 'default',
|
||||
logLevel: 'info',
|
||||
provider: 'claude',
|
||||
@ -243,7 +242,6 @@ describe('executePipeline', () => {
|
||||
it('should use commit_message_template when configured', async () => {
|
||||
mockLoadGlobalConfig.mockReturnValue({
|
||||
language: 'en',
|
||||
trustedDirectories: [],
|
||||
defaultPiece: 'default',
|
||||
logLevel: 'info',
|
||||
provider: 'claude',
|
||||
@ -280,7 +278,6 @@ describe('executePipeline', () => {
|
||||
it('should use default_branch_prefix when configured', async () => {
|
||||
mockLoadGlobalConfig.mockReturnValue({
|
||||
language: 'en',
|
||||
trustedDirectories: [],
|
||||
defaultPiece: 'default',
|
||||
logLevel: 'info',
|
||||
provider: 'claude',
|
||||
@ -317,7 +314,6 @@ describe('executePipeline', () => {
|
||||
it('should use pr_body_template when configured for PR creation', async () => {
|
||||
mockLoadGlobalConfig.mockReturnValue({
|
||||
language: 'en',
|
||||
trustedDirectories: [],
|
||||
defaultPiece: 'default',
|
||||
logLevel: 'info',
|
||||
provider: 'claude',
|
||||
|
||||
@ -40,7 +40,6 @@ beforeEach(() => {
|
||||
mockGetProvider.mockReturnValue(mockProvider);
|
||||
mockLoadGlobalConfig.mockReturnValue({
|
||||
language: 'ja',
|
||||
trustedDirectories: [],
|
||||
defaultPiece: 'default',
|
||||
logLevel: 'info',
|
||||
provider: 'claude',
|
||||
@ -49,8 +48,8 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
describe('summarizeTaskName', () => {
|
||||
it('should return AI-generated slug for Japanese task name', async () => {
|
||||
// Given: AI returns a slug for Japanese input
|
||||
it('should return AI-generated slug for task name', async () => {
|
||||
// Given: AI returns a slug for input
|
||||
mockProviderCall.mockResolvedValue({
|
||||
agent: 'summarizer',
|
||||
status: 'done',
|
||||
@ -59,14 +58,14 @@ describe('summarizeTaskName', () => {
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await summarizeTaskName('認証機能を追加する', { cwd: '/project' });
|
||||
const result = await summarizeTaskName('long task name for testing', { cwd: '/project' });
|
||||
|
||||
// Then
|
||||
expect(result).toBe('add-auth');
|
||||
expect(mockGetProvider).toHaveBeenCalledWith('claude');
|
||||
expect(mockProviderCall).toHaveBeenCalledWith(
|
||||
'summarizer',
|
||||
'認証機能を追加する',
|
||||
'long task name for testing',
|
||||
expect.objectContaining({
|
||||
cwd: '/project',
|
||||
model: 'haiku',
|
||||
@ -85,7 +84,7 @@ describe('summarizeTaskName', () => {
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await summarizeTaskName('Fix the login bug', { cwd: '/project' });
|
||||
const result = await summarizeTaskName('long task name for testing', { cwd: '/project' });
|
||||
|
||||
// Then
|
||||
expect(result).toBe('fix-login-bug');
|
||||
@ -101,7 +100,7 @@ describe('summarizeTaskName', () => {
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await summarizeTaskName('ユーザー認証を追加', { cwd: '/project' });
|
||||
const result = await summarizeTaskName('long task name for testing', { cwd: '/project' });
|
||||
|
||||
// Then
|
||||
expect(result).toBe('add-user-auth');
|
||||
@ -117,7 +116,7 @@ describe('summarizeTaskName', () => {
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await summarizeTaskName('長いタスク名', { cwd: '/project' });
|
||||
const result = await summarizeTaskName('long task name for testing', { cwd: '/project' });
|
||||
|
||||
// Then
|
||||
expect(result.length).toBeLessThanOrEqual(30);
|
||||
@ -135,7 +134,7 @@ describe('summarizeTaskName', () => {
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await summarizeTaskName('test', { cwd: '/project' });
|
||||
const result = await summarizeTaskName('long task name for testing', { cwd: '/project' });
|
||||
|
||||
// Then
|
||||
expect(result).toBe('task');
|
||||
@ -167,7 +166,6 @@ describe('summarizeTaskName', () => {
|
||||
// Given: config has codex provider
|
||||
mockLoadGlobalConfig.mockReturnValue({
|
||||
language: 'ja',
|
||||
trustedDirectories: [],
|
||||
defaultPiece: 'default',
|
||||
logLevel: 'info',
|
||||
provider: 'codex',
|
||||
@ -204,7 +202,7 @@ describe('summarizeTaskName', () => {
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await summarizeTaskName('test', { cwd: '/project' });
|
||||
const result = await summarizeTaskName('long task name for testing', { cwd: '/project' });
|
||||
|
||||
// Then
|
||||
expect(result).toBe('fix-multiple-hyphens');
|
||||
@ -220,7 +218,7 @@ describe('summarizeTaskName', () => {
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await summarizeTaskName('test', { cwd: '/project' });
|
||||
const result = await summarizeTaskName('long task name for testing', { cwd: '/project' });
|
||||
|
||||
// Then
|
||||
expect(result).toBe('leading-trailing');
|
||||
@ -238,7 +236,7 @@ describe('summarizeTaskName', () => {
|
||||
|
||||
it('should use romanization when useLLM is false', async () => {
|
||||
// When: useLLM is explicitly false
|
||||
const result = await summarizeTaskName('認証機能を追加する', { cwd: '/project', useLLM: false });
|
||||
const result = await summarizeTaskName('romanization test', { cwd: '/project', useLLM: false });
|
||||
|
||||
// Then: should not call provider, should return romaji
|
||||
expect(mockProviderCall).not.toHaveBeenCalled();
|
||||
@ -248,7 +246,7 @@ describe('summarizeTaskName', () => {
|
||||
|
||||
it('should handle mixed Japanese/English with romanization', async () => {
|
||||
// When
|
||||
const result = await summarizeTaskName('Add 認証機能', { cwd: '/project', useLLM: false });
|
||||
const result = await summarizeTaskName('Add romanization', { cwd: '/project', useLLM: false });
|
||||
|
||||
// Then
|
||||
expect(result).toMatch(/^[a-z0-9-]+$/);
|
||||
|
||||
@ -36,7 +36,6 @@ export interface PipelineConfig {
|
||||
/** Global configuration for takt */
|
||||
export interface GlobalConfig {
|
||||
language: Language;
|
||||
trustedDirectories: string[];
|
||||
defaultPiece: string;
|
||||
logLevel: 'debug' | 'info' | 'warn' | 'error';
|
||||
provider?: 'claude' | 'codex' | 'mock';
|
||||
|
||||
@ -216,7 +216,6 @@ export const PieceCategoryConfigSchema = z.record(z.string(), PieceCategoryConfi
|
||||
/** Global config schema */
|
||||
export const GlobalConfigSchema = z.object({
|
||||
language: LanguageSchema.optional().default(DEFAULT_LANGUAGE),
|
||||
trusted_directories: z.array(z.string()).optional().default([]),
|
||||
default_piece: z.string().optional().default('default'),
|
||||
log_level: z.enum(['debug', 'info', 'warn', 'error']).optional().default('info'),
|
||||
provider: z.enum(['claude', 'codex', 'mock']).optional().default('claude'),
|
||||
|
||||
@ -285,6 +285,11 @@ export async function interactiveMode(
|
||||
|
||||
const result = await callAIWithRetry(initialInput, prompts.systemPrompt);
|
||||
if (result) {
|
||||
if (!result.success) {
|
||||
error(result.content);
|
||||
blankLine();
|
||||
return { confirmed: false, task: '' };
|
||||
}
|
||||
history.push({ role: 'assistant', content: result.content });
|
||||
blankLine();
|
||||
} else {
|
||||
@ -332,6 +337,11 @@ export async function interactiveMode(
|
||||
info(prompts.ui.summarizeFailed);
|
||||
continue;
|
||||
}
|
||||
if (!summaryResult.success) {
|
||||
error(summaryResult.content);
|
||||
blankLine();
|
||||
return { confirmed: false, task: '' };
|
||||
}
|
||||
const task = summaryResult.content.trim();
|
||||
const confirmed = await confirmTask(
|
||||
task,
|
||||
@ -362,6 +372,12 @@ export async function interactiveMode(
|
||||
|
||||
const result = await callAIWithRetry(trimmed, prompts.systemPrompt);
|
||||
if (result) {
|
||||
if (!result.success) {
|
||||
error(result.content);
|
||||
blankLine();
|
||||
history.pop();
|
||||
return { confirmed: false, task: '' };
|
||||
}
|
||||
history.push({ role: 'assistant', content: result.content });
|
||||
blankLine();
|
||||
} else {
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
||||
import { GlobalConfigSchema } from '../../../core/models/index.js';
|
||||
import type { GlobalConfig, DebugConfig, Language } from '../../../core/models/index.js';
|
||||
@ -17,7 +16,6 @@ import { DEFAULT_LANGUAGE } from '../../../shared/constants.js';
|
||||
function createDefaultGlobalConfig(): GlobalConfig {
|
||||
return {
|
||||
language: DEFAULT_LANGUAGE,
|
||||
trustedDirectories: [],
|
||||
defaultPiece: 'default',
|
||||
logLevel: 'info',
|
||||
provider: 'claude',
|
||||
@ -68,7 +66,6 @@ export class GlobalConfigManager {
|
||||
const parsed = GlobalConfigSchema.parse(raw);
|
||||
const config: GlobalConfig = {
|
||||
language: parsed.language,
|
||||
trustedDirectories: parsed.trusted_directories,
|
||||
defaultPiece: parsed.default_piece,
|
||||
logLevel: parsed.log_level,
|
||||
provider: parsed.provider,
|
||||
@ -100,7 +97,6 @@ export class GlobalConfigManager {
|
||||
const configPath = getGlobalConfigPath();
|
||||
const raw: Record<string, unknown> = {
|
||||
language: config.language,
|
||||
trusted_directories: config.trustedDirectories,
|
||||
default_piece: config.defaultPiece,
|
||||
log_level: config.logLevel,
|
||||
provider: config.provider,
|
||||
@ -203,23 +199,6 @@ export function setProvider(provider: 'claude' | 'codex'): void {
|
||||
saveGlobalConfig(config);
|
||||
}
|
||||
|
||||
export function addTrustedDirectory(dir: string): void {
|
||||
const config = loadGlobalConfig();
|
||||
const resolvedDir = join(dir);
|
||||
if (!config.trustedDirectories.includes(resolvedDir)) {
|
||||
config.trustedDirectories.push(resolvedDir);
|
||||
saveGlobalConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
export function isDirectoryTrusted(dir: string): boolean {
|
||||
const config = loadGlobalConfig();
|
||||
const resolvedDir = join(dir);
|
||||
return config.trustedDirectories.some(
|
||||
(trusted) => resolvedDir === trusted || resolvedDir.startsWith(trusted + '/')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the Anthropic API key.
|
||||
* Priority: TAKT_ANTHROPIC_API_KEY env var > config.yaml > undefined (CLI auth fallback)
|
||||
@ -290,4 +269,3 @@ export function getEffectiveDebugConfig(projectDir?: string): DebugConfig | unde
|
||||
|
||||
return debugConfig;
|
||||
}
|
||||
|
||||
|
||||
@ -12,8 +12,6 @@ export {
|
||||
getLanguage,
|
||||
setLanguage,
|
||||
setProvider,
|
||||
addTrustedDirectory,
|
||||
isDirectoryTrusted,
|
||||
resolveAnthropicApiKey,
|
||||
resolveOpenaiApiKey,
|
||||
loadProjectDebugConfig,
|
||||
|
||||
@ -28,8 +28,6 @@ export {
|
||||
loadGlobalConfig,
|
||||
saveGlobalConfig,
|
||||
invalidateGlobalConfigCache,
|
||||
addTrustedDirectory,
|
||||
isDirectoryTrusted,
|
||||
loadProjectDebugConfig,
|
||||
getEffectiveDebugConfig,
|
||||
} from '../global/globalConfig.js';
|
||||
|
||||
@ -2,14 +2,40 @@
|
||||
* Codex provider implementation
|
||||
*/
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { callCodex, callCodexCustom, type CodexCallOptions } from '../codex/index.js';
|
||||
import { resolveOpenaiApiKey } from '../config/index.js';
|
||||
import type { AgentResponse } from '../../core/models/index.js';
|
||||
import type { Provider, ProviderCallOptions } from './types.js';
|
||||
|
||||
const NOT_GIT_REPO_MESSAGE =
|
||||
'Codex をご利用の場合 Git 管理下のディレクトリでのみ動作します。';
|
||||
|
||||
function isInsideGitRepo(cwd: string): boolean {
|
||||
try {
|
||||
const result = execFileSync('git', ['rev-parse', '--is-inside-work-tree'], {
|
||||
cwd,
|
||||
encoding: 'utf-8',
|
||||
stdio: 'pipe',
|
||||
}).trim();
|
||||
return result === 'true';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Codex provider - wraps existing Codex client */
|
||||
export class CodexProvider implements Provider {
|
||||
async call(agentName: string, prompt: string, options: ProviderCallOptions): Promise<AgentResponse> {
|
||||
if (!isInsideGitRepo(options.cwd)) {
|
||||
return {
|
||||
agent: agentName,
|
||||
status: 'blocked',
|
||||
content: NOT_GIT_REPO_MESSAGE,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
const callOptions: CodexCallOptions = {
|
||||
cwd: options.cwd,
|
||||
sessionId: options.sessionId,
|
||||
@ -24,6 +50,15 @@ export class CodexProvider implements Provider {
|
||||
}
|
||||
|
||||
async callCustom(agentName: string, prompt: string, systemPrompt: string, options: ProviderCallOptions): Promise<AgentResponse> {
|
||||
if (!isInsideGitRepo(options.cwd)) {
|
||||
return {
|
||||
agent: agentName,
|
||||
status: 'blocked',
|
||||
content: NOT_GIT_REPO_MESSAGE,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
const callOptions: CodexCallOptions = {
|
||||
cwd: options.cwd,
|
||||
sessionId: options.sessionId,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user