flutter-dev-mcp 1.0.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.
@@ -0,0 +1,201 @@
1
+ import { spawn } from "node:child_process";
2
+ const runInstances = new Map();
3
+ let nextRunId = 1;
4
+ const MAX_LOG_LINES = 5000;
5
+ export async function flutterRun(projectDir, device, isDebug, dontDetach) {
6
+ const args = ["run"];
7
+ if (device) {
8
+ args.push("-d", device);
9
+ }
10
+ args.push(isDebug ? "--debug" : "--release");
11
+ const runId = nextRunId++;
12
+ const proc = spawn("flutter", args, {
13
+ cwd: projectDir,
14
+ stdio: ["pipe", "pipe", "pipe"],
15
+ });
16
+ const instance = {
17
+ process: proc,
18
+ projectDir,
19
+ logs: [],
20
+ started: false,
21
+ appId: null,
22
+ error: null,
23
+ };
24
+ runInstances.set(runId, instance);
25
+ const addLog = (line) => {
26
+ instance.logs.push(line);
27
+ if (instance.logs.length > MAX_LOG_LINES) {
28
+ instance.logs.shift();
29
+ }
30
+ };
31
+ proc.stdout.on("data", (data) => {
32
+ const text = data.toString();
33
+ for (const line of text.split("\n")) {
34
+ if (line.trim())
35
+ addLog(line);
36
+ }
37
+ });
38
+ proc.stderr.on("data", (data) => {
39
+ const text = data.toString();
40
+ for (const line of text.split("\n")) {
41
+ if (line.trim())
42
+ addLog(`[stderr] ${line}`);
43
+ }
44
+ });
45
+ proc.on("close", (code) => {
46
+ if (!instance.started) {
47
+ instance.error = `Process exited with code ${code} before app started`;
48
+ }
49
+ addLog(`[process exited with code ${code}]`);
50
+ });
51
+ proc.on("error", (err) => {
52
+ instance.error = err.message;
53
+ addLog(`[process error: ${err.message}]`);
54
+ });
55
+ // Wait for the app to start or fail
56
+ try {
57
+ await waitForAppStart(instance, dontDetach);
58
+ }
59
+ catch (err) {
60
+ return {
61
+ run_id: runId,
62
+ success: false,
63
+ error: err instanceof Error ? err.message : String(err),
64
+ };
65
+ }
66
+ return {
67
+ run_id: runId,
68
+ success: true,
69
+ };
70
+ }
71
+ function waitForAppStart(instance, dontDetach) {
72
+ return new Promise((resolve, reject) => {
73
+ // The ready indicators in flutter run output
74
+ const readyPatterns = [
75
+ /Flutter run key commands/i,
76
+ /An Observatory debugger/i,
77
+ /The Flutter DevTools debugger/i,
78
+ /A Dart VM Service/i,
79
+ ];
80
+ const timeout = setTimeout(() => {
81
+ reject(new Error("Timed out waiting for Flutter app to start (120s)"));
82
+ }, 120_000);
83
+ const checkLine = (data) => {
84
+ const text = data.toString();
85
+ for (const pattern of readyPatterns) {
86
+ if (pattern.test(text)) {
87
+ clearTimeout(timeout);
88
+ instance.started = true;
89
+ instance.process.stdout.removeListener("data", checkLine);
90
+ resolve();
91
+ return;
92
+ }
93
+ }
94
+ };
95
+ instance.process.stdout.on("data", checkLine);
96
+ instance.process.on("close", (code) => {
97
+ clearTimeout(timeout);
98
+ if (!instance.started) {
99
+ const recentLogs = instance.logs.slice(-20).join("\n");
100
+ reject(new Error(`Flutter run exited with code ${code} before app started.\nRecent output:\n${recentLogs}`));
101
+ }
102
+ });
103
+ instance.process.on("error", (err) => {
104
+ clearTimeout(timeout);
105
+ reject(new Error(`Failed to start flutter run: ${err.message}`));
106
+ });
107
+ });
108
+ }
109
+ export function flutterHotReload(runId) {
110
+ const instance = runInstances.get(runId);
111
+ if (!instance) {
112
+ return { success: false, error: `No flutter run instance found with id ${runId}` };
113
+ }
114
+ if (!instance.started || instance.process.killed) {
115
+ return { success: false, error: "Flutter run is not running" };
116
+ }
117
+ try {
118
+ instance.process.stdin.write("r");
119
+ instance.logs.push("[hot reload triggered]");
120
+ return { success: true };
121
+ }
122
+ catch (err) {
123
+ return {
124
+ success: false,
125
+ error: `Failed to send hot reload: ${err instanceof Error ? err.message : String(err)}`,
126
+ };
127
+ }
128
+ }
129
+ export function flutterHotRestart(runId) {
130
+ const instance = runInstances.get(runId);
131
+ if (!instance) {
132
+ return { success: false, error: `No flutter run instance found with id ${runId}` };
133
+ }
134
+ if (!instance.started || instance.process.killed) {
135
+ return { success: false, error: "Flutter run is not running" };
136
+ }
137
+ try {
138
+ instance.process.stdin.write("R");
139
+ instance.logs.push("[hot restart triggered]");
140
+ return { success: true };
141
+ }
142
+ catch (err) {
143
+ return {
144
+ success: false,
145
+ error: `Failed to send hot restart: ${err instanceof Error ? err.message : String(err)}`,
146
+ };
147
+ }
148
+ }
149
+ export function flutterKill(runId) {
150
+ const instance = runInstances.get(runId);
151
+ if (!instance) {
152
+ return { success: false, error: `No flutter run instance found with id ${runId}` };
153
+ }
154
+ if (instance.process.killed) {
155
+ runInstances.delete(runId);
156
+ return { success: true };
157
+ }
158
+ try {
159
+ // Send 'q' to gracefully quit, then force kill after a timeout
160
+ instance.process.stdin.write("q");
161
+ setTimeout(() => {
162
+ if (!instance.process.killed) {
163
+ instance.process.kill("SIGKILL");
164
+ }
165
+ }, 5000);
166
+ runInstances.delete(runId);
167
+ return { success: true };
168
+ }
169
+ catch {
170
+ // If stdin write fails, force kill
171
+ instance.process.kill("SIGKILL");
172
+ runInstances.delete(runId);
173
+ return { success: true };
174
+ }
175
+ }
176
+ const MAX_LOG_OUTPUT_BYTES = 24 * 1024;
177
+ export function flutterLogs(runId) {
178
+ const instance = runInstances.get(runId);
179
+ if (!instance) {
180
+ return { logs: "", error: `No flutter run instance found with id ${runId}` };
181
+ }
182
+ const allLogs = instance.logs.join("\n");
183
+ // If logs exceed max size, return the tail
184
+ if (Buffer.byteLength(allLogs, "utf-8") > MAX_LOG_OUTPUT_BYTES) {
185
+ // Take from the end
186
+ const lines = instance.logs;
187
+ const result = [];
188
+ let size = 0;
189
+ for (let i = lines.length - 1; i >= 0; i--) {
190
+ const line = lines[i];
191
+ const lineSize = Buffer.byteLength(line, "utf-8") + 1; // +1 for newline
192
+ if (size + lineSize > MAX_LOG_OUTPUT_BYTES)
193
+ break;
194
+ result.unshift(line);
195
+ size += lineSize;
196
+ }
197
+ return { logs: `[... earlier logs truncated ...]\n${result.join("\n")}` };
198
+ }
199
+ return { logs: allLogs };
200
+ }
201
+ //# sourceMappingURL=flutter-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flutter-run.js","sourceRoot":"","sources":["../src/flutter-run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ interface StoredTestResult {
2
+ test_id: number;
3
+ test_file: string;
4
+ test_name: string;
5
+ error: string;
6
+ }
7
+ interface TestRunSummary {
8
+ test_id: number;
9
+ test_file: string;
10
+ test_name: string;
11
+ error_excerpt: string;
12
+ }
13
+ interface TestRunResult {
14
+ test_run_id: number;
15
+ results: TestRunSummary[];
16
+ }
17
+ export declare function flutterTest(projectDir: string, testPath?: string, testName?: string): Promise<TestRunResult>;
18
+ export declare function flutterGetResult(testRunId: number, testIds: number[]): StoredTestResult[];
19
+ export {};
@@ -0,0 +1,203 @@
1
+ import { spawn } from "node:child_process";
2
+ // Store test results keyed by test_run_id
3
+ const testRunStore = new Map();
4
+ let nextTestRunId = 1;
5
+ const MAX_EXCERPT_LENGTH = 200;
6
+ export async function flutterTest(projectDir, testPath, testName) {
7
+ // Run pub get separately so its non-JSON output doesn't pollute the JSON stream
8
+ await runPubGet(projectDir);
9
+ const args = ["test", "--no-pub", "--reporter", "json"];
10
+ if (testName) {
11
+ args.push("--plain-name", testName);
12
+ }
13
+ if (testPath) {
14
+ args.push(testPath);
15
+ }
16
+ const output = await runFlutterTest(projectDir, args);
17
+ const events = parseJsonEvents(output);
18
+ // Build lookup maps
19
+ const suites = new Map();
20
+ const tests = new Map();
21
+ const errors = new Map();
22
+ const failedTestIds = new Set();
23
+ for (const event of events) {
24
+ switch (event.type) {
25
+ case "suite":
26
+ suites.set(event.suite.id, {
27
+ id: event.suite.id,
28
+ path: event.suite.path,
29
+ });
30
+ break;
31
+ case "testStart":
32
+ if (event.test.url !== null) {
33
+ tests.set(event.test.id, {
34
+ id: event.test.id,
35
+ name: event.test.name,
36
+ suiteID: event.test.suiteID,
37
+ rootUrl: event.test.root_url ?? event.test.url,
38
+ });
39
+ }
40
+ break;
41
+ case "error":
42
+ if (!errors.has(event.testID)) {
43
+ errors.set(event.testID, []);
44
+ }
45
+ errors.get(event.testID).push({
46
+ testID: event.testID,
47
+ error: event.error,
48
+ stackTrace: event.stackTrace,
49
+ });
50
+ break;
51
+ case "testDone":
52
+ if (event.result !== "success" && !event.hidden) {
53
+ failedTestIds.add(event.testID);
54
+ }
55
+ break;
56
+ }
57
+ }
58
+ // Build results for failed tests
59
+ const storedResults = [];
60
+ const summaryResults = [];
61
+ for (const testId of failedTestIds) {
62
+ const test = tests.get(testId);
63
+ if (!test)
64
+ continue;
65
+ const suite = suites.get(test.suiteID);
66
+ // Prefer root_url (actual test file) over suite path
67
+ const testFile = test.rootUrl
68
+ ? test.rootUrl.replace(/^file:\/\//, "")
69
+ : suite?.path ?? "unknown";
70
+ const testErrors = errors.get(testId) ?? [];
71
+ const fullError = testErrors
72
+ .map((e) => `${e.error}\n${e.stackTrace}`)
73
+ .join("\n\n");
74
+ storedResults.push({
75
+ test_id: testId,
76
+ test_file: testFile,
77
+ test_name: test.name,
78
+ error: fullError,
79
+ });
80
+ summaryResults.push({
81
+ test_id: testId,
82
+ test_file: testFile,
83
+ test_name: test.name,
84
+ error_excerpt: fullError.substring(0, MAX_EXCERPT_LENGTH) + (fullError.length > MAX_EXCERPT_LENGTH ? "..." : ""),
85
+ });
86
+ }
87
+ const testRunId = nextTestRunId++;
88
+ testRunStore.set(testRunId, storedResults);
89
+ return {
90
+ test_run_id: testRunId,
91
+ results: summaryResults,
92
+ };
93
+ }
94
+ const MAX_TOTAL_BYTES = 24 * 1024;
95
+ export function flutterGetResult(testRunId, testIds) {
96
+ const stored = testRunStore.get(testRunId);
97
+ if (!stored) {
98
+ throw new Error(`No test run found with id ${testRunId}`);
99
+ }
100
+ const results = [];
101
+ let totalSize = 0;
102
+ for (const testId of testIds) {
103
+ const result = stored.find((r) => r.test_id === testId);
104
+ if (!result)
105
+ continue;
106
+ const resultJson = JSON.stringify(result);
107
+ const resultSize = Buffer.byteLength(resultJson, "utf-8");
108
+ if (results.length === 0) {
109
+ // Always include at least one result, truncating if needed
110
+ if (resultSize > MAX_TOTAL_BYTES) {
111
+ results.push({
112
+ ...result,
113
+ error: result.error.substring(0, MAX_TOTAL_BYTES - 512) + "\n... [truncated]",
114
+ });
115
+ }
116
+ else {
117
+ results.push(result);
118
+ }
119
+ totalSize += Math.min(resultSize, MAX_TOTAL_BYTES);
120
+ }
121
+ else if (totalSize + resultSize <= MAX_TOTAL_BYTES) {
122
+ results.push(result);
123
+ totalSize += resultSize;
124
+ }
125
+ else {
126
+ // Would exceed limit, stop adding
127
+ break;
128
+ }
129
+ }
130
+ return results;
131
+ }
132
+ function runPubGet(projectDir) {
133
+ return new Promise((resolve, reject) => {
134
+ const proc = spawn("flutter", ["pub", "get"], {
135
+ cwd: projectDir,
136
+ stdio: ["ignore", "ignore", "pipe"],
137
+ });
138
+ let stderr = "";
139
+ proc.stderr.on("data", (data) => {
140
+ stderr += data.toString();
141
+ });
142
+ proc.on("close", (code) => {
143
+ if (code !== 0) {
144
+ reject(new Error(`flutter pub get failed with code ${code}: ${stderr}`));
145
+ }
146
+ else {
147
+ resolve();
148
+ }
149
+ });
150
+ proc.on("error", (err) => {
151
+ reject(new Error(`Failed to start flutter pub get: ${err.message}`));
152
+ });
153
+ });
154
+ }
155
+ function runFlutterTest(projectDir, args) {
156
+ return new Promise((resolve, reject) => {
157
+ const proc = spawn("flutter", args, {
158
+ cwd: projectDir,
159
+ stdio: ["ignore", "pipe", "pipe"],
160
+ });
161
+ let stdout = "";
162
+ let stderr = "";
163
+ proc.stdout.on("data", (data) => {
164
+ stdout += data.toString();
165
+ });
166
+ proc.stderr.on("data", (data) => {
167
+ stderr += data.toString();
168
+ });
169
+ proc.on("close", (code) => {
170
+ // flutter test exits with non-zero when tests fail - that's expected
171
+ if (stdout.length > 0) {
172
+ resolve(stdout);
173
+ }
174
+ else if (code !== 0) {
175
+ reject(new Error(`flutter test failed with code ${code}: ${stderr}`));
176
+ }
177
+ else {
178
+ resolve(stdout);
179
+ }
180
+ });
181
+ proc.on("error", (err) => {
182
+ reject(new Error(`Failed to start flutter test: ${err.message}`));
183
+ });
184
+ });
185
+ }
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
+ function parseJsonEvents(output) {
188
+ const events = [];
189
+ const lines = output.split("\n");
190
+ for (const line of lines) {
191
+ const trimmed = line.trim();
192
+ if (!trimmed)
193
+ continue;
194
+ try {
195
+ events.push(JSON.parse(trimmed));
196
+ }
197
+ catch {
198
+ // Skip non-JSON lines (flutter may output non-JSON messages)
199
+ }
200
+ }
201
+ return events;
202
+ }
203
+ //# sourceMappingURL=flutter-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flutter-test.js","sourceRoot":"","sources":["../src/flutter-test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};