mcp-test-timebox 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +192 -0
  3. package/dist/executor/index.d.ts +8 -0
  4. package/dist/executor/index.d.ts.map +1 -0
  5. package/dist/executor/index.js +8 -0
  6. package/dist/executor/index.js.map +1 -0
  7. package/dist/executor/process-executor.d.ts +93 -0
  8. package/dist/executor/process-executor.d.ts.map +1 -0
  9. package/dist/executor/process-executor.js +161 -0
  10. package/dist/executor/process-executor.js.map +1 -0
  11. package/dist/executor/timebox-controller.d.ts +109 -0
  12. package/dist/executor/timebox-controller.d.ts.map +1 -0
  13. package/dist/executor/timebox-controller.js +140 -0
  14. package/dist/executor/timebox-controller.js.map +1 -0
  15. package/dist/report/index.d.ts +8 -0
  16. package/dist/report/index.d.ts.map +1 -0
  17. package/dist/report/index.js +12 -0
  18. package/dist/report/index.js.map +1 -0
  19. package/dist/report/log-extractor.d.ts +125 -0
  20. package/dist/report/log-extractor.d.ts.map +1 -0
  21. package/dist/report/log-extractor.js +213 -0
  22. package/dist/report/log-extractor.js.map +1 -0
  23. package/dist/report/report-generator.d.ts +148 -0
  24. package/dist/report/report-generator.d.ts.map +1 -0
  25. package/dist/report/report-generator.js +261 -0
  26. package/dist/report/report-generator.js.map +1 -0
  27. package/dist/server.d.ts +27 -0
  28. package/dist/server.d.ts.map +1 -0
  29. package/dist/server.js +150 -0
  30. package/dist/server.js.map +1 -0
  31. package/dist/tools/index.d.ts +7 -0
  32. package/dist/tools/index.d.ts.map +1 -0
  33. package/dist/tools/index.js +7 -0
  34. package/dist/tools/index.js.map +1 -0
  35. package/dist/tools/run-test.d.ts +80 -0
  36. package/dist/tools/run-test.d.ts.map +1 -0
  37. package/dist/tools/run-test.js +178 -0
  38. package/dist/tools/run-test.js.map +1 -0
  39. package/dist/utils/path-validator.d.ts +52 -0
  40. package/dist/utils/path-validator.d.ts.map +1 -0
  41. package/dist/utils/path-validator.js +128 -0
  42. package/dist/utils/path-validator.js.map +1 -0
  43. package/dist/validation/command-builder.d.ts +52 -0
  44. package/dist/validation/command-builder.d.ts.map +1 -0
  45. package/dist/validation/command-builder.js +143 -0
  46. package/dist/validation/command-builder.js.map +1 -0
  47. package/dist/validation/index.d.ts +8 -0
  48. package/dist/validation/index.d.ts.map +1 -0
  49. package/dist/validation/index.js +12 -0
  50. package/dist/validation/index.js.map +1 -0
  51. package/dist/validation/input-schema.d.ts +85 -0
  52. package/dist/validation/input-schema.d.ts.map +1 -0
  53. package/dist/validation/input-schema.js +113 -0
  54. package/dist/validation/input-schema.js.map +1 -0
  55. package/package.json +57 -0
@@ -0,0 +1,261 @@
1
+ /**
2
+ * ReportGenerator - レポート生成コンポーネント
3
+ *
4
+ * テスト実行結果から成果物(raw.log, summary.md, summary.json)を生成する。
5
+ *
6
+ * Requirements:
7
+ * - 4.1: report_dir に raw.log を生成する
8
+ * - 4.2: report_dir に summary.md を生成する
9
+ * - 4.3: report_dir に summary.json を生成する
10
+ * - 4.4: raw.log は stdout/stderr の出力元を区別して記録する
11
+ * - 4.5: report_dir が未指定の場合、デフォルトパスを使用する
12
+ * - 5.4: summary.json に必須フィールドを含める
13
+ * - 5.5: summary.md に人間が読みやすい形式で情報を含める
14
+ */
15
+ import { mkdir, writeFile, readdir, rm } from 'node:fs/promises';
16
+ import { join } from 'node:path';
17
+ /**
18
+ * 保持するレポートの最大数
19
+ */
20
+ const MAX_REPORTS = 5;
21
+ /**
22
+ * デフォルトのレポートディレクトリベースパス
23
+ */
24
+ const DEFAULT_REPORT_BASE = '.cache/mcp-test-timebox/reports';
25
+ /**
26
+ * タイムスタンプをISO8601形式でフォーマットする
27
+ *
28
+ * @param timestamp - Unix timestamp (ms)
29
+ * @returns ISO8601形式の文字列
30
+ */
31
+ function formatTimestamp(timestamp) {
32
+ return new Date(timestamp).toISOString();
33
+ }
34
+ /**
35
+ * タイムスタンプをディレクトリ名用にフォーマットする
36
+ *
37
+ * @returns ディレクトリ名用の文字列(例: 20260113-123456)
38
+ */
39
+ function generateTimestampDirName() {
40
+ const now = new Date();
41
+ const year = now.getFullYear();
42
+ const month = String(now.getMonth() + 1).padStart(2, '0');
43
+ const day = String(now.getDate()).padStart(2, '0');
44
+ const hours = String(now.getHours()).padStart(2, '0');
45
+ const minutes = String(now.getMinutes()).padStart(2, '0');
46
+ const seconds = String(now.getSeconds()).padStart(2, '0');
47
+ return `${year}${month}${day}-${hours}${minutes}${seconds}`;
48
+ }
49
+ /**
50
+ * ステータスに対応する絵文字を取得する
51
+ *
52
+ * @param status - 実行ステータス
53
+ * @returns 絵文字
54
+ */
55
+ function getStatusEmoji(status) {
56
+ switch (status) {
57
+ case 'pass':
58
+ return '✅';
59
+ case 'fail':
60
+ return '❌';
61
+ case 'timeout':
62
+ return '⏱️';
63
+ case 'no_output':
64
+ return '🔇';
65
+ case 'error':
66
+ return '⚠️';
67
+ default:
68
+ return '❓';
69
+ }
70
+ }
71
+ /**
72
+ * ミリ秒を人間が読みやすい形式に変換する
73
+ *
74
+ * @param ms - ミリ秒
75
+ * @returns 人間が読みやすい形式(例: "1m 23s" or "456ms")
76
+ */
77
+ function formatDuration(ms) {
78
+ if (ms < 1000) {
79
+ return `${ms}ms`;
80
+ }
81
+ const seconds = Math.floor(ms / 1000);
82
+ const minutes = Math.floor(seconds / 60);
83
+ const remainingSeconds = seconds % 60;
84
+ if (minutes > 0) {
85
+ return `${minutes}m ${remainingSeconds}s`;
86
+ }
87
+ return `${seconds}s`;
88
+ }
89
+ /**
90
+ * ReportGenerator - レポート生成の実装
91
+ */
92
+ export class ReportGenerator {
93
+ /**
94
+ * 古いレポートを削除して最新N件のみ保持する
95
+ *
96
+ * @param basePath - レポートのベースパス
97
+ */
98
+ async cleanupOldReports(basePath) {
99
+ try {
100
+ const entries = await readdir(basePath, { withFileTypes: true });
101
+ // ディレクトリのみをフィルタし、名前でソート(タイムスタンプ形式なので降順で新しい順)
102
+ const dirs = entries
103
+ .filter((entry) => entry.isDirectory())
104
+ .map((entry) => entry.name)
105
+ .sort()
106
+ .reverse();
107
+ // MAX_REPORTS を超える古いディレクトリを削除
108
+ const toDelete = dirs.slice(MAX_REPORTS);
109
+ for (const dir of toDelete) {
110
+ const dirPath = join(basePath, dir);
111
+ await rm(dirPath, { recursive: true, force: true });
112
+ }
113
+ }
114
+ catch {
115
+ // ディレクトリが存在しない場合などは無視
116
+ }
117
+ }
118
+ /**
119
+ * レポートディレクトリを作成する
120
+ *
121
+ * @param basePath - ベースパス(省略時はデフォルトパス)
122
+ * @returns 作成されたディレクトリのパス
123
+ */
124
+ async createReportDir(basePath) {
125
+ // デフォルトパスを使用(Requirements 4.5)
126
+ const base = basePath || DEFAULT_REPORT_BASE;
127
+ const timestampDir = generateTimestampDirName();
128
+ const reportDir = join(base, timestampDir);
129
+ // ディレクトリを再帰的に作成
130
+ await mkdir(reportDir, { recursive: true });
131
+ // 古いレポートをクリーンアップ
132
+ await this.cleanupOldReports(base);
133
+ return reportDir;
134
+ }
135
+ /**
136
+ * raw.log を生成する(stdout/stderr を区別して記録)
137
+ *
138
+ * @param reportDir - レポートディレクトリ
139
+ * @param entries - ログエントリの配列
140
+ * @returns 生成されたファイルのパス
141
+ */
142
+ async writeRawLog(reportDir, entries) {
143
+ const filePath = join(reportDir, 'raw.log');
144
+ // ログエントリをフォーマット(Requirements 4.4)
145
+ // フォーマット: [timestamp] [stream] data
146
+ const lines = entries.map((entry) => {
147
+ const timestamp = formatTimestamp(entry.timestamp);
148
+ return `[${timestamp}] [${entry.stream}] ${entry.data}`;
149
+ });
150
+ const content = lines.join('');
151
+ await writeFile(filePath, content, 'utf-8');
152
+ return filePath;
153
+ }
154
+ /**
155
+ * summary.md を生成する
156
+ *
157
+ * @param reportDir - レポートディレクトリ
158
+ * @param summary - 要約情報
159
+ * @returns 生成されたファイルのパス
160
+ */
161
+ async writeSummaryMd(reportDir, summary) {
162
+ const filePath = join(reportDir, 'summary.md');
163
+ // Markdown形式で要約を生成(Requirements 5.5)
164
+ const statusEmoji = getStatusEmoji(summary.status);
165
+ const durationFormatted = formatDuration(summary.durationMs);
166
+ const commandLine = `${summary.command} ${summary.args.join(' ')}`.trim();
167
+ const sections = [
168
+ `# Test Execution Summary`,
169
+ '',
170
+ `## Result`,
171
+ '',
172
+ `| Item | Value |`,
173
+ `|------|-------|`,
174
+ `| Status | ${statusEmoji} ${summary.status} |`,
175
+ `| Exit Code | ${summary.exitCode ?? 'N/A'} |`,
176
+ `| Duration | ${durationFormatted} (${summary.durationMs}ms) |`,
177
+ '',
178
+ `## Command`,
179
+ '',
180
+ '```',
181
+ commandLine,
182
+ '```',
183
+ '',
184
+ ];
185
+ // 抜粋ブロック
186
+ if (summary.excerpts.length > 0) {
187
+ sections.push(`## Excerpts`);
188
+ sections.push('');
189
+ sections.push('```');
190
+ sections.push(...summary.excerpts);
191
+ sections.push('```');
192
+ sections.push('');
193
+ }
194
+ // 末尾N行
195
+ if (summary.tailLines.length > 0) {
196
+ sections.push(`## Tail Lines`);
197
+ sections.push('');
198
+ sections.push('```');
199
+ sections.push(...summary.tailLines);
200
+ sections.push('```');
201
+ sections.push('');
202
+ }
203
+ const content = sections.join('\n');
204
+ await writeFile(filePath, content, 'utf-8');
205
+ return filePath;
206
+ }
207
+ /**
208
+ * summary.json を生成する
209
+ *
210
+ * @param reportDir - レポートディレクトリ
211
+ * @param summary - 要約情報
212
+ * @returns 生成されたファイルのパス
213
+ */
214
+ async writeSummaryJson(reportDir, summary) {
215
+ const filePath = join(reportDir, 'summary.json');
216
+ // JSON形式で要約を生成(Requirements 5.4)
217
+ // 必須フィールド: command, exit_code, status, duration_ms, excerpts, tail_lines
218
+ const jsonContent = {
219
+ command: `${summary.command} ${summary.args.join(' ')}`.trim(),
220
+ args: summary.args,
221
+ status: summary.status,
222
+ exit_code: summary.exitCode,
223
+ duration_ms: summary.durationMs,
224
+ excerpts: summary.excerpts,
225
+ tail_lines: summary.tailLines,
226
+ generated_at: new Date().toISOString(),
227
+ };
228
+ const content = JSON.stringify(jsonContent, null, 2);
229
+ await writeFile(filePath, content, 'utf-8');
230
+ return filePath;
231
+ }
232
+ /**
233
+ * すべての成果物を生成する
234
+ *
235
+ * @param reportDir - レポートディレクトリ
236
+ * @param entries - ログエントリの配列
237
+ * @param summary - 要約情報
238
+ * @returns 生成された成果物のパス
239
+ */
240
+ async writeAll(reportDir, entries, summary) {
241
+ const [rawLog, summaryMd, summaryJson] = await Promise.all([
242
+ this.writeRawLog(reportDir, entries),
243
+ this.writeSummaryMd(reportDir, summary),
244
+ this.writeSummaryJson(reportDir, summary),
245
+ ]);
246
+ return {
247
+ rawLog,
248
+ summaryMd,
249
+ summaryJson,
250
+ };
251
+ }
252
+ }
253
+ /**
254
+ * ReportGeneratorのファクトリ関数
255
+ *
256
+ * @returns 新しいReportGeneratorインスタンス
257
+ */
258
+ export function createReportGenerator() {
259
+ return new ReportGenerator();
260
+ }
261
+ //# sourceMappingURL=report-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-generator.js","sourceRoot":"","sources":["../../src/report/report-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-test-timebox MCPサーバ エントリポイント
4
+ *
5
+ * タイムボックス付きテスト専用MCPサーバ
6
+ * - stdio経由でMCPプロトコルに準拠した通信を行う
7
+ * - run_testツールのみを公開
8
+ * - stdoutはMCPプロトコル専用、ログはstderrへ
9
+ *
10
+ * Requirements:
11
+ * - 1.1: stdio経由でMCPプロトコルに準拠した通信を開始する
12
+ * - 1.2: run_testツールのみを公開する
13
+ * - 1.3: stdoutにログを出力せず、MCPプロトコル出力を破壊しない
14
+ */
15
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16
+ /**
17
+ * MCPサーバを作成する
18
+ *
19
+ * @returns 設定済みのMcpServerインスタンス
20
+ */
21
+ export declare function createMcpServer(): McpServer;
22
+ /**
23
+ * MCPサーバを起動する
24
+ * stdio transportを使用してクライアントと通信する
25
+ */
26
+ export declare function startServer(): Promise<void>;
27
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA+DpE;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,SAAS,CAgE3C;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBjD"}
package/dist/server.js ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-test-timebox MCPサーバ エントリポイント
4
+ *
5
+ * タイムボックス付きテスト専用MCPサーバ
6
+ * - stdio経由でMCPプロトコルに準拠した通信を行う
7
+ * - run_testツールのみを公開
8
+ * - stdoutはMCPプロトコル専用、ログはstderrへ
9
+ *
10
+ * Requirements:
11
+ * - 1.1: stdio経由でMCPプロトコルに準拠した通信を開始する
12
+ * - 1.2: run_testツールのみを公開する
13
+ * - 1.3: stdoutにログを出力せず、MCPプロトコル出力を破壊しない
14
+ */
15
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
+ import { z } from 'zod';
18
+ import { createRunTestTool } from './tools/run-test.js';
19
+ import { ALLOWED_RUNNERS, ALLOWED_SCOPES } from './validation/input-schema.js';
20
+ /**
21
+ * サーバ情報
22
+ */
23
+ const SERVER_INFO = {
24
+ name: 'mcp-test-timebox',
25
+ version: '0.1.0',
26
+ description: 'タイムボックス付きテスト専用MCPサーバ - テスト実行が終わらない問題を防ぐ',
27
+ };
28
+ /**
29
+ * run_testツールの入力スキーマ(Zod shape)
30
+ * MCPプロトコルで公開するスキーマ定義
31
+ *
32
+ * 注意: MCP SDKはZodObjectではなく、オブジェクトの形状(shape)を期待する
33
+ */
34
+ const runTestInputShape = {
35
+ /** テストランナー(MVPでは flutter のみ) */
36
+ runner: z.enum(ALLOWED_RUNNERS).describe(`テストランナー。許可値: ${ALLOWED_RUNNERS.join(', ')}`),
37
+ /** テスト実行スコープ */
38
+ scope: z.enum(ALLOWED_SCOPES).describe(`テスト実行スコープ。all: 全テスト, file: 特定ファイル, pattern: パターンマッチ`),
39
+ /** テスト対象(scope が file/pattern の場合必須) */
40
+ target: z.string().optional().describe('テスト対象。scope が file または pattern の場合必須'),
41
+ /** ハードタイムアウト(ミリ秒) */
42
+ timeout_ms: z.number().int().positive().describe('ハードタイムアウト(ミリ秒)。この時間を超えるとプロセスを強制終了'),
43
+ /** 無出力タイムアウト(ミリ秒) */
44
+ no_output_timeout_ms: z.number().int().positive().describe('無出力タイムアウト(ミリ秒)。この時間出力がないとプロセスを強制終了'),
45
+ /** 要約対象の末尾バイト数 */
46
+ max_output_bytes: z.number().int().positive().describe('要約対象の末尾バイト数。ログ抽出時にこのバイト数を対象とする'),
47
+ /** レポート出力ディレクトリ(相対パス、オプション) */
48
+ report_dir: z.string().optional().describe('レポート出力ディレクトリ(相対パス)。省略時はデフォルトパスを使用'),
49
+ };
50
+ /**
51
+ * デバッグログをstderrに出力する
52
+ * stdoutはMCPプロトコル専用のため、ログはstderrへ
53
+ *
54
+ * @param message - ログメッセージ
55
+ */
56
+ function debugLog(message) {
57
+ const timestamp = new Date().toISOString();
58
+ console.error(`[${timestamp}] [mcp-test-timebox] ${message}`);
59
+ }
60
+ /**
61
+ * MCPサーバを作成する
62
+ *
63
+ * @returns 設定済みのMcpServerインスタンス
64
+ */
65
+ export function createMcpServer() {
66
+ // MCPサーバインスタンスを作成
67
+ const server = new McpServer(SERVER_INFO);
68
+ // RunTestToolインスタンスを作成
69
+ const runTestTool = createRunTestTool();
70
+ // run_testツールを登録(registerToolを使用)
71
+ server.registerTool('run_test', {
72
+ description: 'タイムボックス付きでテストを実行する。固定テンプレート(flutter test等)のみ許可し、実行結果をレポートとして保存する。',
73
+ inputSchema: runTestInputShape,
74
+ }, async (params) => {
75
+ debugLog(`run_test called with params: ${JSON.stringify(params)}`);
76
+ try {
77
+ // ツールを実行
78
+ const result = await runTestTool.execute(params);
79
+ debugLog(`run_test completed with status: ${result.status}`);
80
+ // MCPレスポンス形式で返す
81
+ return {
82
+ content: [
83
+ {
84
+ type: 'text',
85
+ text: JSON.stringify(result, null, 2),
86
+ },
87
+ ],
88
+ };
89
+ }
90
+ catch (error) {
91
+ // 予期しないエラーの場合
92
+ const errorMessage = error instanceof Error ? error.message : String(error);
93
+ debugLog(`run_test error: ${errorMessage}`);
94
+ const errorResult = {
95
+ status: 'error',
96
+ exit_code: null,
97
+ duration_ms: 0,
98
+ report_dir: '',
99
+ artifacts: {
100
+ raw_log: '',
101
+ summary_md: '',
102
+ summary_json: '',
103
+ },
104
+ excerpt: '',
105
+ error_message: `予期しないエラー: ${errorMessage}`,
106
+ };
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: JSON.stringify(errorResult, null, 2),
112
+ },
113
+ ],
114
+ };
115
+ }
116
+ });
117
+ return server;
118
+ }
119
+ /**
120
+ * MCPサーバを起動する
121
+ * stdio transportを使用してクライアントと通信する
122
+ */
123
+ export async function startServer() {
124
+ debugLog('Starting MCP server...');
125
+ try {
126
+ // MCPサーバを作成
127
+ const server = createMcpServer();
128
+ // stdio transportを作成
129
+ const transport = new StdioServerTransport();
130
+ // サーバをtransportに接続
131
+ await server.connect(transport);
132
+ debugLog('MCP server started successfully');
133
+ }
134
+ catch (error) {
135
+ const errorMessage = error instanceof Error ? error.message : String(error);
136
+ debugLog(`Failed to start MCP server: ${errorMessage}`);
137
+ process.exit(1);
138
+ }
139
+ }
140
+ // メインエントリポイント
141
+ // このファイルが直接実行された場合にサーバを起動
142
+ // ESMではimport.meta.urlを使用してメインモジュールかどうかを判定
143
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
144
+ if (isMainModule) {
145
+ startServer().catch((error) => {
146
+ console.error('Fatal error:', error);
147
+ process.exit(1);
148
+ });
149
+ }
150
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tools モジュール
3
+ *
4
+ * MCPサーバが公開するツールをエクスポートする
5
+ */
6
+ export { RunTestTool, createRunTestTool, type RunTestOutput, type RunTestToolOptions, } from './run-test.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tools モジュール
3
+ *
4
+ * MCPサーバが公開するツールをエクスポートする
5
+ */
6
+ export { RunTestTool, createRunTestTool, } from './run-test.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,WAAW,EACX,iBAAiB,GAGlB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * RunTestTool - テスト実行ツール
3
+ *
4
+ * MCPサーバが公開する唯一のツール。
5
+ * 入力バリデーション、コマンド構築、プロセス実行、レポート生成を統合する。
6
+ *
7
+ * Requirements:
8
+ * - 2.1-2.6: テスト実行コマンドの制限
9
+ * - 6.1-6.5: MCPレスポンスの必須フィールド
10
+ */
11
+ /**
12
+ * RunTestToolの出力インターフェース
13
+ *
14
+ * Requirements 6.1-6.5に準拠
15
+ */
16
+ export interface RunTestOutput {
17
+ /** 実行ステータス(pass/fail/timeout/no_output/error) */
18
+ status: 'pass' | 'fail' | 'timeout' | 'no_output' | 'error';
19
+ /** 終了コード(タイムアウト/エラー時はnull) */
20
+ exit_code: number | null;
21
+ /** 実行時間(ミリ秒) */
22
+ duration_ms: number;
23
+ /** レポートディレクトリのパス */
24
+ report_dir: string;
25
+ /** 生成された成果物のパス */
26
+ artifacts: {
27
+ raw_log: string;
28
+ summary_md: string;
29
+ summary_json: string;
30
+ };
31
+ /** 抜粋(重要行の抽出結果) */
32
+ excerpt: string;
33
+ /** エラーメッセージ(エラー時のみ) */
34
+ error_message?: string;
35
+ }
36
+ /**
37
+ * RunTestToolのオプション
38
+ */
39
+ export interface RunTestToolOptions {
40
+ /** リポジトリルートパス(デフォルト: process.cwd()) */
41
+ repoRoot?: string;
42
+ /** 末尾行数(要約用、デフォルト: 20) */
43
+ tailLineCount?: number;
44
+ }
45
+ /**
46
+ * RunTestToolクラス
47
+ *
48
+ * テスト実行の全体フローを管理する
49
+ */
50
+ export declare class RunTestTool {
51
+ private readonly repoRoot;
52
+ private readonly tailLineCount;
53
+ /**
54
+ * @param options - ツールオプション
55
+ */
56
+ constructor(options?: RunTestToolOptions);
57
+ /**
58
+ * テストを実行する
59
+ *
60
+ * @param input - 入力パラメータ(未検証)
61
+ * @returns 実行結果
62
+ */
63
+ execute(input: unknown): Promise<RunTestOutput>;
64
+ /**
65
+ * エラーレスポンスを生成する
66
+ *
67
+ * @param startTime - 開始時刻
68
+ * @param errorMessage - エラーメッセージ
69
+ * @returns エラーレスポンス
70
+ */
71
+ private createErrorResponse;
72
+ }
73
+ /**
74
+ * RunTestToolのファクトリ関数
75
+ *
76
+ * @param options - ツールオプション
77
+ * @returns 新しいRunTestToolインスタンス
78
+ */
79
+ export declare function createRunTestTool(options?: RunTestToolOptions): RunTestTool;
80
+ //# sourceMappingURL=run-test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-test.d.ts","sourceRoot":"","sources":["../../src/tools/run-test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAC5D,8BAA8B;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB;IAClB,SAAS,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAiBD;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;OAEG;gBACS,OAAO,GAAE,kBAAuB;IAK5C;;;;;OAKG;IACG,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAqIrD;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;CAe5B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAE3E"}