Tasks API 
Tasks APIは、Traeエディタ内でタスクの作成、管理、実行を行うための包括的なシステムを提供します。ビルド、テスト、リント、カスタムスクリプトなど、様々なタスクを効率的に処理できます。
概要 
Tasks APIを使用すると、以下のことが可能になります:
- タスク管理: タスクの登録、実行、終了
- タスクプロバイダー: npm、スクリプト、カスタムタスクプロバイダー
- タスクテンプレート: 一般的なタスクの事前定義済みテンプレート
- タスク実行: シェル、プロセス、カスタムタスクの実行
- 変数解決: ワークスペースとユーザー変数の解決
- タスク履歴: 実行履歴の追跡と管理
- UIインテグレーション: タスクパネル、ステータスバー、コマンドパレット
基本的な使用方法 
タスクマネージャー 
typescript
import { TraeAPI } from '@trae/api';
class TaskManager {
  private tasks: Map<string, Task> = new Map();
  private taskProviders: TaskProvider[] = [];
  private runningTasks: Map<string, TaskExecution> = new Map();
  private taskHistory: TaskHistoryEntry[] = [];
  private taskTemplates: Map<string, TaskTemplate> = new Map();
  constructor() {
    this.initializeBuiltinTasks();
    this.initializeTaskProviders();
    this.initializeTaskTemplates();
  }
  private initializeBuiltinTasks(): void {
    // ビルトインタスクの初期化
    const builtinTasks: Task[] = [
      {
        id: 'build',
        label: 'Build',
        type: 'shell',
        command: 'npm',
        args: ['run', 'build'],
        group: 'build',
        detail: 'プロジェクトをビルドします'
      },
      {
        id: 'test',
        label: 'Test',
        type: 'shell',
        command: 'npm',
        args: ['test'],
        group: 'test',
        detail: 'テストを実行します'
      },
      {
        id: 'lint',
        label: 'Lint',
        type: 'shell',
        command: 'npm',
        args: ['run', 'lint'],
        group: 'build',
        detail: 'コードをリントします'
      },
      {
        id: 'dev',
        label: 'Dev Server',
        type: 'shell',
        command: 'npm',
        args: ['run', 'dev'],
        group: 'build',
        detail: '開発サーバーを起動します',
        isBackground: true
      }
    ];
    builtinTasks.forEach(task => this.tasks.set(task.id, task));
  }
  // タスクの登録
  registerTask(task: Task): void {
    this.tasks.set(task.id, task);
    this.notifyTaskChange('added', task);
  }
  // タスクの登録解除
  unregisterTask(taskId: string): boolean {
    const task = this.tasks.get(taskId);
    if (task) {
      this.tasks.delete(taskId);
      this.notifyTaskChange('removed', task);
      return true;
    }
    return false;
  }
  // タスクの取得
  getTask(taskId: string): Task | undefined {
    return this.tasks.get(taskId);
  }
  // 全タスクの取得
  getTasks(): Task[] {
    return Array.from(this.tasks.values());
  }
  // グループ別タスクの取得
  getTasksByGroup(group: string): Task[] {
    return this.getTasks().filter(task => task.group === group);
  }
  // タスクの実行
  async executeTask(taskId: string, options?: TaskExecutionOptions): Promise<TaskExecution> {
    const task = this.getTask(taskId);
    if (!task) {
      throw new Error(`Task not found: ${taskId}`);
    }
    // 変数の解決
    const resolvedTask = await this.resolveTaskVariables(task);
    
    // タスクの実行
    const execution = await this.createTaskExecution(resolvedTask, options);
    this.runningTasks.set(execution.id, execution);
    // イベントの発火
    this.notifyTaskStart(execution);
    try {
      await execution.start();
      
      // 履歴に追加
      this.addToHistory({
        taskId: task.id,
        startTime: execution.startTime,
        endTime: Date.now(),
        duration: Date.now() - execution.startTime,
        success: execution.exitCode === 0,
        exitCode: execution.exitCode
      });
      this.notifyTaskEnd(execution);
    } catch (error) {
      this.addToHistory({
        taskId: task.id,
        startTime: execution.startTime,
        endTime: Date.now(),
        duration: Date.now() - execution.startTime,
        success: false,
        exitCode: -1,
        error: error.message
      });
      this.notifyTaskEnd(execution);
      throw error;
    } finally {
      this.runningTasks.delete(execution.id);
    }
    return execution;
  }
  // タスクの実行(タイプ別)
  private async createTaskExecution(task: Task, options?: TaskExecutionOptions): Promise<TaskExecution> {
    switch (task.type) {
      case 'shell':
        return new ShellTaskExecution(task, options);
      case 'process':
        return new ProcessTaskExecution(task, options);
      case 'custom':
        return new CustomTaskExecution(task, options);
      default:
        throw new Error(`Unsupported task type: ${task.type}`);
    }
  }
  // 変数の解決
  private async resolveTaskVariables(task: Task): Promise<Task> {
    const workspaceRoot = TraeAPI.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
    const userHome = process.env.HOME || process.env.USERPROFILE || '';
    
    const variables = {
      '${workspaceFolder}': workspaceRoot,
      '${workspaceRoot}': workspaceRoot,
      '${userHome}': userHome,
      '${env:NODE_ENV}': process.env.NODE_ENV || 'development'
    };
    const resolveString = (str: string): string => {
      let resolved = str;
      for (const [variable, value] of Object.entries(variables)) {
        resolved = resolved.replace(new RegExp(variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), value);
      }
      return resolved;
    };
    return {
      ...task,
      command: resolveString(task.command),
      args: task.args?.map(resolveString),
      cwd: task.cwd ? resolveString(task.cwd) : undefined,
      env: task.env ? Object.fromEntries(
        Object.entries(task.env).map(([key, value]) => [key, resolveString(value)])
      ) : undefined
    };
  }
  // タスクの終了
  async terminateTask(executionId: string): Promise<boolean> {
    const execution = this.runningTasks.get(executionId);
    if (execution) {
      try {
        await execution.terminate();
        this.runningTasks.delete(executionId);
        return true;
      } catch (error) {
        console.error('Failed to terminate task:', error);
        return false;
      }
    }
    return false;
  }
  // 実行中タスクの取得
  getRunningTasks(): TaskExecution[] {
    return Array.from(this.runningTasks.values());
  }
  // タスク履歴の取得
  getTaskHistory(limit?: number): TaskHistoryEntry[] {
    const history = [...this.taskHistory].reverse();
    return limit ? history.slice(0, limit) : history;
  }
  // 履歴への追加
  private addToHistory(entry: TaskHistoryEntry): void {
    this.taskHistory.push(entry);
    
    // 履歴の制限(最大100件)
    if (this.taskHistory.length > 100) {
      this.taskHistory = this.taskHistory.slice(-100);
    }
  }
  // タスクプロバイダーの登録
  registerTaskProvider(provider: TaskProvider): void {
    this.taskProviders.push(provider);
  }
  // プロバイダータスクの取得
  async getProvidedTasks(): Promise<Task[]> {
    const allTasks: Task[] = [];
    
    for (const provider of this.taskProviders) {
      try {
        const tasks = await provider.provideTasks();
        allTasks.push(...tasks);
      } catch (error) {
        console.error(`Task provider error:`, error);
      }
    }
    
    return allTasks;
  }
  // イベント通知
  private notifyTaskChange(type: 'added' | 'removed', task: Task): void {
    TraeAPI.events.emit('onDidChangeTask', { type, task });
  }
  private notifyTaskStart(execution: TaskExecution): void {
    TraeAPI.events.emit('onDidStartTask', execution);
  }
  private notifyTaskEnd(execution: TaskExecution): void {
    TraeAPI.events.emit('onDidEndTask', execution);
  }
  // リソースの解放
  dispose(): void {
    // 実行中のタスクを終了
    for (const execution of this.runningTasks.values()) {
      execution.terminate().catch(console.error);
    }
    this.runningTasks.clear();
    
    // プロバイダーの解放
    for (const provider of this.taskProviders) {
      if (provider.dispose) {
        provider.dispose();
      }
    }
    this.taskProviders.length = 0;
  }
}タスクプロバイダー 
NPMタスクプロバイダー 
typescript
class NpmTaskProvider implements TaskProvider {
  async provideTasks(): Promise<Task[]> {
    const packageJsonPath = path.join(TraeAPI.workspace.workspaceFolders?.[0]?.uri.fsPath || '', 'package.json');
    
    if (!fs.existsSync(packageJsonPath)) {
      return [];
    }
    try {
      const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
      const scripts = packageJson.scripts || {};
      
      return Object.entries(scripts).map(([name, script]) => ({
        id: `npm:${name}`,
        label: `npm: ${name}`,
        type: 'shell' as const,
        command: 'npm',
        args: ['run', name],
        group: this.getTaskGroup(name),
        detail: `npm run ${name}`,
        source: 'npm'
      }));
    } catch (error) {
      console.error('Failed to parse package.json:', error);
      return [];
    }
  }
  private getTaskGroup(scriptName: string): string {
    if (scriptName.includes('build')) return 'build';
    if (scriptName.includes('test')) return 'test';
    if (scriptName.includes('lint')) return 'build';
    if (scriptName.includes('dev') || scriptName.includes('start')) return 'build';
    return 'misc';
  }
}スクリプトタスクプロバイダー 
typescript
class ScriptTaskProvider implements TaskProvider {
  async provideTasks(): Promise<Task[]> {
    const workspaceRoot = TraeAPI.workspace.workspaceFolders?.[0]?.uri.fsPath;
    if (!workspaceRoot) return [];
    const tasks: Task[] = [];
    
    // .scriptsディレクトリのスクリプトを検索
    const scriptsDir = path.join(workspaceRoot, '.scripts');
    if (fs.existsSync(scriptsDir)) {
      const scriptFiles = fs.readdirSync(scriptsDir)
        .filter(file => file.endsWith('.sh') || file.endsWith('.bat') || file.endsWith('.ps1'));
      
      for (const file of scriptFiles) {
        const name = path.basename(file, path.extname(file));
        const scriptPath = path.join(scriptsDir, file);
        
        tasks.push({
          id: `script:${name}`,
          label: `Script: ${name}`,
          type: 'shell',
          command: this.getShellCommand(file),
          args: [scriptPath],
          group: 'misc',
          detail: `Run script: ${file}`,
          source: 'scripts'
        });
      }
    }
    
    return tasks;
  }
  private getShellCommand(filename: string): string {
    if (filename.endsWith('.sh')) return 'bash';
    if (filename.endsWith('.bat')) return 'cmd';
    if (filename.endsWith('.ps1')) return 'powershell';
    return 'node';
  }
}タスクテンプレート 
typescript
class TaskTemplateManager {
  private templates: Map<string, TaskTemplate> = new Map();
  constructor() {
    this.initializeBuiltinTemplates();
  }
  private initializeBuiltinTemplates(): void {
    const builtinTemplates: TaskTemplate[] = [
      {
        id: 'npm-build',
        name: 'NPM Build',
        description: 'NPMビルドタスクを作成します',
        template: {
          type: 'shell',
          command: 'npm',
          args: ['run', 'build'],
          group: 'build'
        }
      },
      {
        id: 'npm-test',
        name: 'NPM Test',
        description: 'NPMテストタスクを作成します',
        template: {
          type: 'shell',
          command: 'npm',
          args: ['test'],
          group: 'test'
        }
      },
      {
        id: 'docker-build',
        name: 'Docker Build',
        description: 'Dockerビルドタスクを作成します',
        template: {
          type: 'shell',
          command: 'docker',
          args: ['build', '-t', '${input:imageName}', '.'],
          group: 'build'
        }
      },
      {
        id: 'python-run',
        name: 'Python Run',
        description: 'Pythonスクリプト実行タスクを作成します',
        template: {
          type: 'shell',
          command: 'python',
          args: ['${input:scriptPath}'],
          group: 'misc'
        }
      }
    ];
    builtinTemplates.forEach(template => this.templates.set(template.id, template));
  }
  getTemplate(id: string): TaskTemplate | undefined {
    return this.templates.get(id);
  }
  getTemplates(): TaskTemplate[] {
    return Array.from(this.templates.values());
  }
  registerTemplate(template: TaskTemplate): void {
    this.templates.set(template.id, template);
  }
  createTaskFromTemplate(templateId: string, options: TaskCreationOptions): Task {
    const template = this.getTemplate(templateId);
    if (!template) {
      throw new Error(`Template not found: ${templateId}`);
    }
    const taskId = options.id || `${templateId}-${Date.now()}`;
    
    return {
      id: taskId,
      label: options.label || template.name,
      ...template.template,
      detail: options.detail || template.description
    };
  }
}イベント処理 
typescript
// タスク開始イベント
TraeAPI.events.on('onDidStartTask', (execution: TaskExecution) => {
  console.log(`Task started: ${execution.task.label}`);
  
  // ステータスバーの更新
  TraeAPI.window.setStatusBarMessage(
    `$(sync~spin) Running: ${execution.task.label}`,
    execution.promise
  );
});
// タスク終了イベント
TraeAPI.events.on('onDidEndTask', (execution: TaskExecution) => {
  console.log(`Task ended: ${execution.task.label}, Exit code: ${execution.exitCode}`);
  
  if (execution.exitCode === 0) {
    TraeAPI.window.showInformationMessage(`Task '${execution.task.label}' completed successfully`);
  } else {
    TraeAPI.window.showErrorMessage(`Task '${execution.task.label}' failed with exit code ${execution.exitCode}`);
  }
});
// タスク変更イベント
TraeAPI.events.on('onDidChangeTask', (event: TaskChangeEvent) => {
  console.log(`Task ${event.type}: ${event.task.label}`);
});タスク実行 
TaskExecutionクラス 
typescript
abstract class TaskExecution {
  public readonly id: string;
  public readonly task: Task;
  public readonly startTime: number;
  public exitCode: number = 0;
  public promise: Promise<void>;
  
  private output: string[] = [];
  private outputChannel: TraeAPI.OutputChannel;
  constructor(task: Task, options?: TaskExecutionOptions) {
    this.id = `${task.id}-${Date.now()}`;
    this.task = task;
    this.startTime = Date.now();
    this.outputChannel = TraeAPI.window.createOutputChannel(`Task: ${task.label}`);
    
    if (options?.showOutput) {
      this.outputChannel.show();
    }
  }
  abstract start(): Promise<void>;
  abstract terminate(): Promise<void>;
  protected addOutput(data: string): void {
    this.output.push(data);
    this.outputChannel.append(data);
  }
  getOutput(): string[] {
    return [...this.output];
  }
  dispose(): void {
    this.outputChannel.dispose();
  }
}
class ShellTaskExecution extends TaskExecution {
  private process?: ChildProcess;
  async start(): Promise<void> {
    return new Promise((resolve, reject) => {
      const command = this.task.command;
      const args = this.task.args || [];
      const options = {
        cwd: this.task.cwd || TraeAPI.workspace.workspaceFolders?.[0]?.uri.fsPath,
        env: { ...process.env, ...this.task.env },
        shell: true
      };
      this.addOutput(`> ${command} ${args.join(' ')}\n`);
      
      this.process = spawn(command, args, options);
      this.process.stdout?.on('data', (data) => {
        this.addOutput(data.toString());
      });
      this.process.stderr?.on('data', (data) => {
        this.addOutput(data.toString());
      });
      this.process.on('close', (code) => {
        this.exitCode = code || 0;
        this.addOutput(`\nProcess exited with code ${this.exitCode}\n`);
        
        if (this.exitCode === 0) {
          resolve();
        } else {
          reject(new Error(`Process exited with code ${this.exitCode}`));
        }
      });
      this.process.on('error', (error) => {
        this.addOutput(`\nError: ${error.message}\n`);
        reject(error);
      });
    });
  }
  async terminate(): Promise<void> {
    if (this.process && !this.process.killed) {
      this.process.kill('SIGTERM');
      
      // 強制終了のタイムアウト
      setTimeout(() => {
        if (this.process && !this.process.killed) {
          this.process.kill('SIGKILL');
        }
      }, 5000);
    }
  }
}
class ProcessTaskExecution extends TaskExecution {
  private process?: ChildProcess;
  async start(): Promise<void> {
    return new Promise((resolve, reject) => {
      const options = {
        cwd: this.task.cwd || TraeAPI.workspace.workspaceFolders?.[0]?.uri.fsPath,
        env: { ...process.env, ...this.task.env }
      };
      this.addOutput(`> ${this.task.command} ${(this.task.args || []).join(' ')}\n`);
      
      this.process = spawn(this.task.command, this.task.args || [], options);
      this.process.stdout?.on('data', (data) => {
        this.addOutput(data.toString());
      });
      this.process.stderr?.on('data', (data) => {
        this.addOutput(data.toString());
      });
      this.process.on('close', (code) => {
        this.exitCode = code || 0;
        this.addOutput(`\nProcess exited with code ${this.exitCode}\n`);
        
        if (this.exitCode === 0) {
          resolve();
        } else {
          reject(new Error(`Process exited with code ${this.exitCode}`));
        }
      });
      this.process.on('error', (error) => {
        this.addOutput(`\nError: ${error.message}\n`);
        reject(error);
      });
    });
  }
  async terminate(): Promise<void> {
    if (this.process && !this.process.killed) {
      this.process.kill();
    }
  }
}
class CustomTaskExecution extends TaskExecution {
  async start(): Promise<void> {
    // カスタムタスクの実装
    if (this.task.customHandler) {
      try {
        await this.task.customHandler(this);
      } catch (error) {
        this.exitCode = 1;
        throw error;
      }
    } else {
      throw new Error('Custom task handler not provided');
    }
  }
  async terminate(): Promise<void> {
    // カスタムタスクの終了処理
    if (this.task.customTerminator) {
      await this.task.customTerminator(this);
    }
  }
}UIプロバイダー 
typescript
class TaskUIProvider {
  private taskManager: TaskManager;
  private statusBarItem: TraeAPI.StatusBarItem;
  private taskPanel?: TraeAPI.WebviewPanel;
  constructor(private taskManager: TaskManager) {
    this.setupCommands();
    this.setupStatusBar();
    this.setupEventListeners();
  }
  private setupCommands(): void {
    // タスクコマンドの登録
    TraeAPI.commands.registerCommand('tasks.showPanel', () => {
      this.showTaskPanel();
    });
    
    TraeAPI.commands.registerCommand('tasks.runTask', async () => {
      await this.showTaskPicker();
    });
    
    TraeAPI.commands.registerCommand('tasks.runBuildTask', async () => {
      await this.runTaskByGroup('build');
    });
    
    TraeAPI.commands.registerCommand('tasks.runTestTask', async () => {
      await this.runTaskByGroup('test');
    });
    
    TraeAPI.commands.registerCommand('tasks.terminateTask', async () => {
      await this.showRunningTaskPicker();
    });
    
    TraeAPI.commands.registerCommand('tasks.restartTask', async () => {
      await this.showRestartTaskPicker();
    });
  }
  private setupStatusBar(): void {
    this.statusBarItem = TraeAPI.window.createStatusBarItem(
      TraeAPI.StatusBarAlignment.Left,
      10
    );
    
    this.statusBarItem.text = '$(play) Tasks';
    this.statusBarItem.tooltip = 'Run Task';
    this.statusBarItem.command = 'tasks.runTask';
    this.statusBarItem.show();
  }
  private async showTaskPicker(): Promise<void> {
    const allTasks = this.taskManager.getTasks();
    const providedTasks = await this.taskManager.getProvidedTasks();
    const tasks = [...allTasks, ...providedTasks];
    
    if (tasks.length === 0) {
      TraeAPI.window.showInformationMessage('利用可能なタスクがありません');
      return;
    }
    
    const items = tasks.map(task => ({
      label: task.label,
      description: task.detail || task.command,
      detail: `グループ: ${task.group || 'なし'}`,
      task
    }));
    
    const selected = await TraeAPI.window.showQuickPick(items, {
      placeHolder: '実行するタスクを選択してください',
      matchOnDescription: true,
      matchOnDetail: true
    });
    
    if (selected) {
      try {
        await this.taskManager.executeTask(selected.task.id);
      } catch (error) {
        TraeAPI.window.showErrorMessage(`タスクの実行に失敗しました: ${error}`);
      }
    }
  }
  private async runTaskByGroup(group: string): Promise<void> {
    const tasks = this.taskManager.getTasksByGroup(group);
    const providedTasks = await this.taskManager.getProvidedTasks();
    const groupTasks = [...tasks, ...providedTasks.filter(t => t.group === group)];
    
    if (groupTasks.length === 0) {
      TraeAPI.window.showInformationMessage(`${group}タスクが利用できません`);
      return;
    }
    
    if (groupTasks.length === 1) {
      try {
        await this.taskManager.executeTask(groupTasks[0].id);
      } catch (error) {
        TraeAPI.window.showErrorMessage(`タスクの実行に失敗しました: ${error}`);
      }
      return;
    }
    
    const items = groupTasks.map(task => ({
      label: task.label,
      description: task.detail || task.command,
      task
    }));
    
    const selected = await TraeAPI.window.showQuickPick(items, {
      placeHolder: `実行する${group}タスクを選択してください`,
      matchOnDescription: true
    });
    
    if (selected) {
      try {
        await this.taskManager.executeTask(selected.task.id);
      } catch (error) {
        TraeAPI.window.showErrorMessage(`タスクの実行に失敗しました: ${error}`);
      }
    }
  }
  private async showRunningTaskPicker(): Promise<void> {
    const runningTasks = this.taskManager.getRunningTasks();
    
    if (runningTasks.length === 0) {
      TraeAPI.window.showInformationMessage('現在実行中のタスクはありません');
      return;
    }
    
    const items = runningTasks.map(runningTask => ({
      label: runningTask.task.label,
      description: `実行時間: ${Math.floor((Date.now() - runningTask.startTime) / 1000)}秒`,
      detail: runningTask.task.command,
      runningTask
    }));
    
    const selected = await TraeAPI.window.showQuickPick(items, {
      placeHolder: '終了するタスクを選択してください',
      matchOnDescription: true
    });
    
    if (selected) {
      const confirmed = await TraeAPI.window.showWarningMessage(
        `'${selected.runningTask.task.label}'を終了してもよろしいですか?`,
        '終了',
        'キャンセル'
      );
      
      if (confirmed === '終了') {
        const success = await this.taskManager.terminateTask(selected.runningTask.id);
        if (success) {
          TraeAPI.window.showInformationMessage('タスクを終了しました');
        } else {
          TraeAPI.window.showErrorMessage('タスクの終了に失敗しました');
        }
      }
    }
  }
  private async showRestartTaskPicker(): Promise<void> {
    const history = this.taskManager.getTaskHistory(10);
    
    if (history.length === 0) {
      TraeAPI.window.showInformationMessage('再実行可能な最近のタスクがありません');
      return;
    }
    
    const items = history.map(entry => {
      const task = this.taskManager.getTask(entry.taskId);
      if (!task) return null;
      
      return {
        label: task.label,
        description: `最終実行: ${new Date(entry.endTime).toLocaleTimeString()}`,
        detail: `実行時間: ${entry.duration}ms, ${entry.success ? '成功' : '失敗'}`,
        task
      };
    }).filter(Boolean) as any[];
    
    if (items.length === 0) {
      TraeAPI.window.showInformationMessage('再実行可能なタスクがありません');
      return;
    }
    
    const selected = await TraeAPI.window.showQuickPick(items, {
      placeHolder: '再実行するタスクを選択してください',
      matchOnDescription: true
    });
    
    if (selected) {
      try {
        await this.taskManager.executeTask(selected.task.id);
      } catch (error) {
        TraeAPI.window.showErrorMessage(`タスクの再実行に失敗しました: ${error}`);
      }
    }
  }
  private async showTaskPanel(): Promise<void> {
    if (this.taskPanel) {
      this.taskPanel.reveal();
      return;
    }
    this.taskPanel = TraeAPI.window.createWebviewPanel(
      'tasks',
      'タスク',
      TraeAPI.ViewColumn.One,
      {
        enableScripts: true,
        retainContextWhenHidden: true
      }
    );
    this.taskPanel.webview.html = this.getTaskHTML();
    this.setupWebviewMessageHandling();
    this.taskPanel.onDidDispose(() => {
      this.taskPanel = null;
    });
    // 初期データの送信
    this.updateTaskPanel();
  }
  private getTaskHTML(): string {
    return `
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>タスク</title>
        <style>
          body {
            margin: 0;
            padding: 20px;
            background: #1e1e1e;
            color: #d4d4d4;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            font-size: 13px;
          }
          
          .tasks-container {
            max-width: 1000px;
            margin: 0 auto;
          }
          
          .section {
            margin-bottom: 30px;
          }
          
          .section-title {
            font-size: 16px;
            font-weight: 600;
            margin-bottom: 15px;
            color: #ffffff;
            border-bottom: 1px solid #3e3e42;
            padding-bottom: 8px;
          }
          
          .task-list {
            display: grid;
            gap: 10px;
          }
          
          .task-item {
            background: #2d2d30;
            border: 1px solid #3e3e42;
            border-radius: 6px;
            padding: 15px;
            cursor: pointer;
            transition: all 0.2s ease;
          }
          
          .task-item:hover {
            background: #37373d;
            border-color: #007acc;
          }
          
          .task-item.running {
            border-color: #f9c23c;
            background: #3d3a2a;
          }
          
          .task-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 8px;
          }
          
          .task-name {
            font-weight: 500;
            color: #ffffff;
          }
          
          .task-status {
            padding: 2px 8px;
            border-radius: 12px;
            font-size: 11px;
            font-weight: 500;
          }
          
          .task-status.running {
            background: #f9c23c;
            color: #000000;
          }
          
          .task-status.completed {
            background: #4caf50;
            color: #ffffff;
          }
          
          .task-status.failed {
            background: #f44336;
            color: #ffffff;
          }
          
          .task-command {
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 12px;
            color: #cccccc;
            margin-bottom: 8px;
          }
          
          .task-details {
            display: flex;
            gap: 15px;
            font-size: 11px;
            color: #999999;
          }
          
          .task-actions {
            display: flex;
            gap: 8px;
            margin-top: 10px;
          }
          
          .task-button {
            padding: 4px 12px;
            background: #0e639c;
            border: none;
            color: #ffffff;
            border-radius: 3px;
            cursor: pointer;
            font-size: 11px;
          }
          
          .task-button:hover {
            background: #1177bb;
          }
          
          .task-button.danger {
            background: #d32f2f;
          }
          
          .task-button.danger:hover {
            background: #f44336;
          }
          
          .no-tasks {
            text-align: center;
            color: #cccccc;
            padding: 40px;
            background: #2d2d30;
            border-radius: 6px;
          }
          
          .history-item {
            background: #2d2d30;
            border: 1px solid #3e3e42;
            border-radius: 6px;
            padding: 12px;
            margin-bottom: 8px;
          }
          
          .history-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 5px;
          }
          
          .history-name {
            font-weight: 500;
          }
          
          .history-time {
            font-size: 11px;
            color: #999999;
          }
          
          .history-details {
            display: flex;
            gap: 15px;
            font-size: 11px;
            color: #cccccc;
          }
          
          .templates-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
            gap: 15px;
          }
          
          .template-item {
            background: #2d2d30;
            border: 1px solid #3e3e42;
            border-radius: 6px;
            padding: 15px;
            cursor: pointer;
            transition: all 0.2s ease;
          }
          
          .template-item:hover {
            background: #37373d;
            border-color: #007acc;
          }
          
          .template-name {
            font-weight: 500;
            color: #ffffff;
            margin-bottom: 8px;
          }
          
          .template-description {
            font-size: 12px;
            color: #cccccc;
            margin-bottom: 10px;
          }
          
          .template-command {
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 11px;
            color: #999999;
          }
        </style>
      </head>
      <body>
        <div class="tasks-container">
          <div class="section">
            <div class="section-title">実行中のタスク</div>
            <div id="runningTasks" class="task-list">
              <div class="no-tasks">現在実行中のタスクはありません</div>
            </div>
          </div>
          
          <div class="section">
            <div class="section-title">利用可能なタスク</div>
            <div id="availableTasks" class="task-list">
              <div class="no-tasks">タスクを読み込み中...</div>
            </div>
          </div>
          
          <div class="section">
            <div class="section-title">タスクテンプレート</div>
            <div id="taskTemplates" class="templates-grid">
              <div class="no-tasks">テンプレートを読み込み中...</div>
            </div>
          </div>
          
          <div class="section">
            <div class="section-title">最近のタスク</div>
            <div id="taskHistory">
              <div class="no-tasks">最近のタスクはありません</div>
            </div>
          </div>
        </div>
        
        <script>
          const vscode = acquireVsCodeApi();
          
          function runTask(taskId) {
            vscode.postMessage({
              type: 'runTask',
              taskId
            });
          }
          
          function terminateTask(executionId) {
            vscode.postMessage({
              type: 'terminateTask',
              executionId
            });
          }
          
          function createFromTemplate(templateId) {
            vscode.postMessage({
              type: 'createFromTemplate',
              templateId
            });
          }
          
          function updateRunningTasks(tasks) {
            const container = document.getElementById('runningTasks');
            
            if (tasks.length === 0) {
              container.innerHTML = '<div class="no-tasks">現在実行中のタスクはありません</div>';
              return;
            }
            
            container.innerHTML = tasks.map(task => \`
              <div class="task-item running">
                <div class="task-header">
                  <div class="task-name">\${task.task.label}</div>
                  <div class="task-status running">実行中</div>
                </div>
                <div class="task-command">\${task.task.command} \${(task.task.args || []).join(' ')}</div>
                <div class="task-details">
                  <span>開始時刻: \${new Date(task.startTime).toLocaleTimeString()}</span>
                  <span>実行時間: \${Math.floor((Date.now() - task.startTime) / 1000)}秒</span>
                </div>
                <div class="task-actions">
                  <button class="task-button danger" onclick="terminateTask('\${task.id}')">終了</button>
                </div>
              </div>
            \`).join('');
          }
          
          function updateAvailableTasks(tasks) {
            const container = document.getElementById('availableTasks');
            
            if (tasks.length === 0) {
              container.innerHTML = '<div class="no-tasks">利用可能なタスクがありません</div>';
              return;
            }
            
            container.innerHTML = tasks.map(task => \`
              <div class="task-item" onclick="runTask('\${task.id}')">
                <div class="task-header">
                  <div class="task-name">\${task.label}</div>
                </div>
                <div class="task-command">\${task.command} \${(task.args || []).join(' ')}</div>
                <div class="task-details">
                  <span>グループ: \${task.group || 'なし'}</span>
                  <span>タイプ: \${task.type}</span>
                </div>
              </div>
            \`).join('');
          }
          
          function updateTaskTemplates(templates) {
            const container = document.getElementById('taskTemplates');
            
            if (templates.length === 0) {
              container.innerHTML = '<div class="no-tasks">利用可能なテンプレートがありません</div>';
              return;
            }
            
            container.innerHTML = templates.map(template => \`
              <div class="template-item" onclick="createFromTemplate('\${template.id}')">
                <div class="template-name">\${template.name}</div>
                <div class="template-description">\${template.description}</div>
                <div class="template-command">\${template.template.command} \${(template.template.args || []).join(' ')}</div>
              </div>
            \`).join('');
          }
          
          function updateTaskHistory(history) {
            const container = document.getElementById('taskHistory');
            
            if (history.length === 0) {
              container.innerHTML = '<div class="no-tasks">最近のタスクはありません</div>';
              return;
            }
            
            container.innerHTML = history.map(entry => \`
              <div class="history-item">
                <div class="history-header">
                  <div class="history-name">\${entry.taskId}</div>
                  <div class="history-time">\${new Date(entry.endTime).toLocaleString()}</div>
                </div>
                <div class="history-details">
                  <span>実行時間: \${entry.duration}ms</span>
                  <span>ステータス: \${entry.success ? '成功' : '失敗'}</span>
                  <span>終了コード: \${entry.exitCode}</span>
                </div>
              </div>
            \`).join('');
          }
          
          // 拡張機能からのメッセージを処理
          window.addEventListener('message', event => {
            const message = event.data;
            
            switch (message.type) {
              case 'updateRunningTasks':
                updateRunningTasks(message.tasks);
                break;
              case 'updateAvailableTasks':
                updateAvailableTasks(message.tasks);
                break;
              case 'updateTaskTemplates':
                updateTaskTemplates(message.templates);
                break;
              case 'updateTaskHistory':
                updateTaskHistory(message.history);
                break;
            }
          });
        </script>
      </body>
      </html>
    `;
  }
  private setupWebviewMessageHandling(): void {
    if (!this.taskPanel) return;
    this.taskPanel.webview.onDidReceiveMessage(async message => {
      switch (message.type) {
        case 'runTask':
          try {
            await this.taskManager.executeTask(message.taskId);
          } catch (error) {
            TraeAPI.window.showErrorMessage(`タスクの実行に失敗しました: ${error}`);
          }
          break;
        case 'terminateTask':
          const success = await this.taskManager.terminateTask(message.executionId);
          if (!success) {
            TraeAPI.window.showErrorMessage('タスクの終了に失敗しました');
          }
          break;
        case 'createFromTemplate':
          await this.createTaskFromTemplate(message.templateId);
          break;
      }
    });
  }
  private async createTaskFromTemplate(templateId: string): Promise<void> {
    const label = await TraeAPI.window.showInputBox({
      prompt: 'タスクラベルを入力してください',
      placeHolder: 'マイカスタムタスク'
    });
    
    if (!label) return;
    
    try {
      const task = this.taskManager.createTaskFromTemplate(templateId, { label });
      this.taskManager.registerTask(task);
      
      TraeAPI.window.showInformationMessage(`タスク '${label}' が正常に作成されました`);
      this.updateTaskPanel();
    } catch (error) {
      TraeAPI.window.showErrorMessage(`タスクの作成に失敗しました: ${error}`);
    }
  }
  private async updateTaskPanel(): Promise<void> {
    if (!this.taskPanel) return;
    // 実行中タスクの更新
    const runningTasks = this.taskManager.getRunningTasks();
    this.taskPanel.webview.postMessage({
      type: 'updateRunningTasks',
      tasks: runningTasks
    });
    // 利用可能タスクの更新
    const allTasks = this.taskManager.getTasks();
    const providedTasks = await this.taskManager.getProvidedTasks();
    this.taskPanel.webview.postMessage({
      type: 'updateAvailableTasks',
      tasks: [...allTasks, ...providedTasks]
    });
    // テンプレートの更新
    const templates = this.taskManager.getTemplates();
    this.taskPanel.webview.postMessage({
      type: 'updateTaskTemplates',
      templates
    });
    // 履歴の更新
    const history = this.taskManager.getTaskHistory(10);
    this.taskPanel.webview.postMessage({
      type: 'updateTaskHistory',
      history
    });
  }
  dispose(): void {
    this.statusBarItem.dispose();
    this.taskPanel?.dispose();
  }
}インターフェース定義 
typescript
interface Task {
  id: string;
  label: string;
  type: 'shell' | 'process' | 'custom';
  command: string;
  args?: string[];
  cwd?: string;
  env?: Record<string, string>;
  group?: string;
  detail?: string;
  source?: string;
  isBackground?: boolean;
  customHandler?: (execution: TaskExecution) => Promise<void>;
  customTerminator?: (execution: TaskExecution) => Promise<void>;
}
interface TaskProvider {
  provideTasks(): Promise<Task[]>;
  dispose?(): void;
}
interface TaskTemplate {
  id: string;
  name: string;
  description: string;
  template: Partial<Task>;
}
interface TaskExecutionOptions {
  showOutput?: boolean;
  cwd?: string;
  env?: Record<string, string>;
}
interface TaskCreationOptions {
  id?: string;
  label: string;
  detail?: string;
}
interface TaskHistoryEntry {
  taskId: string;
  startTime: number;
  endTime: number;
  duration: number;
  success: boolean;
  exitCode: number;
  error?: string;
}
interface TaskChangeEvent {
  type: 'added' | 'removed';
  task: Task;
}API リファレンス 
コアインターフェース 
TaskController 
typescript
interface TaskController {
  // タスク管理
  registerTask(task: Task): void;
  unregisterTask(taskId: string): boolean;
  getTask(taskId: string): Task | undefined;
  getTasks(): Task[];
  getTasksByGroup(group: string): Task[];
  
  // タスク実行
  executeTask(taskId: string, options?: TaskExecutionOptions): Promise<TaskExecution>;
  terminateTask(executionId: string): Promise<boolean>;
  getRunningTasks(): TaskExecution[];
  
  // プロバイダー
  registerTaskProvider(provider: TaskProvider): void;
  getProvidedTasks(): Promise<Task[]>;
  
  // テンプレート
  registerTemplate(template: TaskTemplate): void;
  getTemplate(id: string): TaskTemplate | undefined;
  getTemplates(): TaskTemplate[];
  createTaskFromTemplate(templateId: string, options: TaskCreationOptions): Task;
  
  // 履歴
  getTaskHistory(limit?: number): TaskHistoryEntry[];
  
  // イベント
  onDidStartTask: TraeAPI.Event<TaskExecution>;
  onDidEndTask: TraeAPI.Event<TaskExecution>;
  onDidChangeTask: TraeAPI.Event<TaskChangeEvent>;
}TaskItem 
typescript
interface TaskItem extends TraeAPI.TreeItem {
  task: Task;
  execution?: TaskExecution;
}TaskRun 
typescript
interface TaskRun {
  task: Task;
  execution: TaskExecution;
  token: TraeAPI.CancellationToken;
}TaskRunRequest 
typescript
interface TaskRunRequest {
  include?: Task[];
  exclude?: Task[];
  continuous?: boolean;
}ベストプラクティス 
1. 適切なタスクタイプの選択 
typescript
// シェルコマンドの場合
const shellTask: Task = {
  id: 'build',
  label: 'Build Project',
  type: 'shell',
  command: 'npm run build'
};
// 直接プロセス実行の場合
const processTask: Task = {
  id: 'test',
  label: 'Run Tests',
  type: 'process',
  command: 'node',
  args: ['test.js']
};
// カスタム処理の場合
const customTask: Task = {
  id: 'custom',
  label: 'Custom Task',
  type: 'custom',
  command: 'custom',
  customHandler: async (execution) => {
    // カスタム処理の実装
  }
};2. 変数の活用 
typescript
const task: Task = {
  id: 'build-workspace',
  label: 'Build Workspace',
  type: 'shell',
  command: 'npm',
  args: ['run', 'build'],
  cwd: '${workspaceFolder}',
  env: {
    NODE_ENV: '${env:NODE_ENV}'
  }
};3. エラーハンドリング 
typescript
try {
  const execution = await taskManager.executeTask('build');
  console.log('Task completed successfully');
} catch (error) {
  console.error('Task failed:', error);
  TraeAPI.window.showErrorMessage(`Build failed: ${error.message}`);
}4. リソース管理 
typescript
class MyTaskProvider implements TaskProvider {
  private disposables: TraeAPI.Disposable[] = [];
  constructor() {
    // イベントリスナーの登録
    this.disposables.push(
      TraeAPI.workspace.onDidChangeConfiguration(() => {
        this.refresh();
      })
    );
  }
  async provideTasks(): Promise<Task[]> {
    // タスクの提供
    return [];
  }
  dispose(): void {
    this.disposables.forEach(d => d.dispose());
  }
}関連API 
- Commands API - コマンドの登録と実行
- Workspace API - ワークスペース情報の取得