memorydetective 1.7.0 → 1.8.1

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 (34) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +61 -29
  3. package/USAGE.md +87 -41
  4. package/dist/index.js +34 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/runtime/axe.d.ts +86 -0
  7. package/dist/runtime/axe.js +249 -0
  8. package/dist/runtime/axe.js.map +1 -0
  9. package/dist/runtime/buildSettings.d.ts +27 -0
  10. package/dist/runtime/buildSettings.js +88 -0
  11. package/dist/runtime/buildSettings.js.map +1 -0
  12. package/dist/runtime/exec.d.ts +8 -1
  13. package/dist/runtime/exec.js +8 -2
  14. package/dist/runtime/exec.js.map +1 -1
  15. package/dist/runtime/simctl.d.ts +68 -0
  16. package/dist/runtime/simctl.js +194 -0
  17. package/dist/runtime/simctl.js.map +1 -0
  18. package/dist/tools/bootAndLaunchForLeakInvestigation.d.ts +166 -0
  19. package/dist/tools/bootAndLaunchForLeakInvestigation.js +367 -0
  20. package/dist/tools/bootAndLaunchForLeakInvestigation.js.map +1 -0
  21. package/dist/tools/captureMemgraph.d.ts +29 -1
  22. package/dist/tools/captureMemgraph.js +148 -6
  23. package/dist/tools/captureMemgraph.js.map +1 -1
  24. package/dist/tools/captureScenarioState.d.ts +77 -0
  25. package/dist/tools/captureScenarioState.js +159 -0
  26. package/dist/tools/captureScenarioState.js.map +1 -0
  27. package/dist/tools/detectLeaksInXCUITest.d.ts +2 -2
  28. package/dist/tools/getInvestigationPlaybook.d.ts +15 -0
  29. package/dist/tools/getInvestigationPlaybook.js +24 -1
  30. package/dist/tools/getInvestigationPlaybook.js.map +1 -1
  31. package/dist/tools/replayScenario.d.ts +243 -0
  32. package/dist/tools/replayScenario.js +187 -0
  33. package/dist/tools/replayScenario.js.map +1 -0
  34. package/package.json +3 -2
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Drive the iOS Simulator through a sequence of UI actions to amplify a
3
+ * suspected leak before capturing a memgraph. Tied to the leak/perf workflow:
4
+ * the only reason this tool exists is to make verify-fix loops reproducible
5
+ * (same scenario before/after a fix) and to surface leaks that only manifest
6
+ * after N iterations of a flow.
7
+ *
8
+ * Soft dependency on `axe` (https://github.com/cameroncooke/AXe). When axe is
9
+ * not on PATH, this tool returns ok:false with a structured workaroundNotice
10
+ * pointing the caller at the install instructions, instead of throwing.
11
+ */
12
+ import { z } from "zod";
13
+ import { existsSync, mkdirSync } from "node:fs";
14
+ import { writeFile } from "node:fs/promises";
15
+ import { checkAxeAvailable, describeUI, swipe, tap, typeText, } from "../runtime/axe.js";
16
+ const tapActionSchema = z.object({
17
+ type: z.literal("tap"),
18
+ label: z
19
+ .string()
20
+ .optional()
21
+ .describe("Match an element by its accessibility label or identifier. Mutually exclusive with `coords` and `elementId`."),
22
+ elementId: z
23
+ .string()
24
+ .optional()
25
+ .describe("Match an element by its accessibility identifier. Mutually exclusive with `label` and `coords`."),
26
+ coords: z
27
+ .tuple([z.number(), z.number()])
28
+ .optional()
29
+ .describe("Tap at explicit [x, y] coordinates. Mutually exclusive with `label` and `elementId`."),
30
+ });
31
+ const swipeActionSchema = z.object({
32
+ type: z.literal("swipe"),
33
+ from: z.tuple([z.number(), z.number()]),
34
+ to: z.tuple([z.number(), z.number()]),
35
+ durationMs: z.number().int().positive().max(5000).default(250),
36
+ });
37
+ const waitActionSchema = z.object({
38
+ type: z.literal("wait"),
39
+ seconds: z.number().nonnegative().max(60),
40
+ });
41
+ const typeActionSchema = z.object({
42
+ type: z.literal("type"),
43
+ text: z.string(),
44
+ });
45
+ const actionSchema = z.discriminatedUnion("type", [
46
+ tapActionSchema,
47
+ swipeActionSchema,
48
+ waitActionSchema,
49
+ typeActionSchema,
50
+ ]);
51
+ export const replayScenarioShape = {
52
+ simulatorUDID: z
53
+ .string()
54
+ .min(1)
55
+ .describe("UDID of the booted simulator. Use listTraceDevices to find one."),
56
+ actions: z
57
+ .array(actionSchema)
58
+ .min(1)
59
+ .describe("Ordered list of UI actions: { type: 'tap', label|elementId|coords }, { type: 'swipe', from, to }, { type: 'wait', seconds }, or { type: 'type', text }."),
60
+ repeat: z
61
+ .number()
62
+ .int()
63
+ .positive()
64
+ .max(100)
65
+ .default(1)
66
+ .describe("Run the entire actions sequence this many times. Default 1. Use 5-10 to amplify subtle leaks that accumulate per repetition."),
67
+ settleBetweenActionsMs: z
68
+ .number()
69
+ .int()
70
+ .nonnegative()
71
+ .max(10_000)
72
+ .default(500)
73
+ .describe("Pause between consecutive actions in milliseconds. Default 500. Increase for animation-heavy flows."),
74
+ finalUITreePath: z
75
+ .string()
76
+ .optional()
77
+ .describe("When provided, after the scenario completes the final UI tree is written here as JSON for the caller to verify the app ended in the expected state."),
78
+ };
79
+ export const replayScenarioSchema = z.object(replayScenarioShape);
80
+ export async function replayScenario(input) {
81
+ const availability = await checkAxeAvailable();
82
+ if (!availability.available) {
83
+ return {
84
+ ok: false,
85
+ executedSteps: 0,
86
+ failures: [],
87
+ totalDurationMs: 0,
88
+ workaroundNotice: {
89
+ issue: "axe-not-found",
90
+ message: availability.installHint ??
91
+ "axe CLI not found. Required for replayScenario.",
92
+ },
93
+ };
94
+ }
95
+ const start = Date.now();
96
+ const failures = [];
97
+ let executedSteps = 0;
98
+ for (let iteration = 0; iteration < input.repeat; iteration++) {
99
+ for (let i = 0; i < input.actions.length; i++) {
100
+ const action = input.actions[i];
101
+ try {
102
+ await executeAction(input.simulatorUDID, action);
103
+ executedSteps++;
104
+ }
105
+ catch (err) {
106
+ failures.push({
107
+ iteration,
108
+ stepIndex: i,
109
+ reason: err.message,
110
+ });
111
+ // Continue with remaining actions in this iteration so the agent
112
+ // gets the full picture instead of one early failure masking the rest.
113
+ }
114
+ if (input.settleBetweenActionsMs > 0) {
115
+ await sleep(input.settleBetweenActionsMs);
116
+ }
117
+ }
118
+ }
119
+ let finalUITreePath;
120
+ if (input.finalUITreePath) {
121
+ try {
122
+ const tree = await describeUI(input.simulatorUDID);
123
+ ensureParentDir(input.finalUITreePath);
124
+ await writeFile(input.finalUITreePath, JSON.stringify(tree, null, 2), "utf8");
125
+ finalUITreePath = input.finalUITreePath;
126
+ }
127
+ catch (err) {
128
+ failures.push({
129
+ iteration: input.repeat,
130
+ stepIndex: input.actions.length,
131
+ reason: `Failed to capture final UI tree: ${err.message}`,
132
+ });
133
+ }
134
+ }
135
+ return {
136
+ ok: failures.length === 0,
137
+ executedSteps,
138
+ failures,
139
+ totalDurationMs: Date.now() - start,
140
+ ...(finalUITreePath ? { finalUITreePath } : {}),
141
+ };
142
+ }
143
+ async function executeAction(udid, action) {
144
+ switch (action.type) {
145
+ case "tap": {
146
+ const target = resolveTapTarget(action);
147
+ await tap(udid, target);
148
+ return;
149
+ }
150
+ case "swipe":
151
+ await swipe(udid, { x: action.from[0], y: action.from[1] }, { x: action.to[0], y: action.to[1] }, action.durationMs);
152
+ return;
153
+ case "wait":
154
+ await sleep(action.seconds * 1000);
155
+ return;
156
+ case "type":
157
+ await typeText(udid, action.text);
158
+ return;
159
+ }
160
+ }
161
+ /** Pure: convert a tap-action input shape into the runtime TapTarget union. Exposed for tests. */
162
+ export function resolveTapTarget(action) {
163
+ const provided = [action.label, action.elementId, action.coords].filter((v) => v !== undefined).length;
164
+ if (provided !== 1) {
165
+ throw new Error("tap action must provide exactly one of: label, elementId, coords.");
166
+ }
167
+ if (action.coords) {
168
+ return { kind: "coords", x: action.coords[0], y: action.coords[1] };
169
+ }
170
+ if (action.label) {
171
+ return { kind: "label", value: action.label };
172
+ }
173
+ return { kind: "elementId", value: action.elementId };
174
+ }
175
+ function ensureParentDir(filePath) {
176
+ const slash = filePath.lastIndexOf("/");
177
+ if (slash === -1)
178
+ return;
179
+ const dir = filePath.slice(0, slash);
180
+ if (!existsSync(dir)) {
181
+ mkdirSync(dir, { recursive: true });
182
+ }
183
+ }
184
+ function sleep(ms) {
185
+ return new Promise((r) => setTimeout(r, ms));
186
+ }
187
+ //# sourceMappingURL=replayScenario.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replayScenario.js","sourceRoot":"","sources":["../../src/tools/replayScenario.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "memorydetective",
3
- "version": "1.7.0",
4
- "description": "MCP server for iOS leak hunting and performance investigation. Reads .memgraph + .trace files, captures new ones via xctrace and leaks(1), classifies retain cycles.",
3
+ "version": "1.8.1",
4
+ "mcpName": "io.github.carloshpdoc/memorydetective",
5
+ "description": "MCP server for iOS leak hunting and performance investigation. 31 tools across .memgraph and .trace files, 34-pattern retain-cycle classifier with Swift fixTemplate snippets, single-call build+boot+launch with MallocStackLogging for macOS 26.x leaks --outputGraph regression, replayScenario + captureScenarioState for verify-fix loops, compareTracesByPattern for CI gating on hangs/animation-hitches/app-launch regressions, SourceKit-LSP source bridging. macOS only (depends on leaks(1) and xctrace).",
5
6
  "type": "module",
6
7
  "bin": {
7
8
  "memorydetective": "dist/index.js"