memorydetective 1.2.0 → 1.2.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.
- package/CHANGELOG.md +18 -1
- package/README.md +34 -0
- package/USAGE.md +39 -23
- package/dist/cli.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.2.1] — 2026-05-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- README: new fourth example "End-to-end: leak → file → fix suggestion" walking through the complete chat-driven workflow with v1.2's Swift source-bridging tools.
|
|
14
|
+
- USAGE.md: section 4 ("Common follow-up requests") expanded with prompts that exercise `swiftGetSymbolDefinition`, `swiftFindSymbolReferences`, `swiftGetSymbolsOverview`, `swiftGetHoverInfo`, `swiftSearchPattern`. New `reachableFromCycle` row added.
|
|
15
|
+
- USAGE.md: section 3 ("How fixes flow") rewritten to reflect the new responsibility split — `memorydetective` now covers diagnose **and** source bridging; the agent owns "decide and apply the edit".
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- USAGE.md concrete end-to-end example replaced with a richer 9-step flow that exercises memgraph analysis + `reachableFromCycle` + Swift LSP tools end-to-end.
|
|
20
|
+
|
|
21
|
+
### Notes
|
|
22
|
+
|
|
23
|
+
- Doc-only release. No code changes from `1.2.0`.
|
|
24
|
+
|
|
9
25
|
## [1.2.0] — 2026-05-01
|
|
10
26
|
|
|
11
27
|
Swift source-bridging. The agent can now go from "found a leak in the cycle" to "find the file/line in this project" without leaving chat. 20 → 25 tools.
|
|
@@ -119,7 +135,8 @@ When called with no arguments it starts the MCP server over stdio.
|
|
|
119
135
|
- **`captureMemgraph`** does not work on physical iOS devices — `leaks(1)` only attaches to processes on the local Mac (which includes iOS simulators). Memory Graph capture from a physical device still requires Xcode.
|
|
120
136
|
- **`detectLeaksInXCUITest`** is flagged experimental: orchestration logic is implemented but not yet validated against a wide set of production XCUITest runs.
|
|
121
137
|
|
|
122
|
-
[Unreleased]: https://github.com/carloshpdoc/memorydetective/compare/v1.2.
|
|
138
|
+
[Unreleased]: https://github.com/carloshpdoc/memorydetective/compare/v1.2.1...HEAD
|
|
139
|
+
[1.2.1]: https://github.com/carloshpdoc/memorydetective/compare/v1.2.0...v1.2.1
|
|
123
140
|
[1.2.0]: https://github.com/carloshpdoc/memorydetective/compare/v1.1.0...v1.2.0
|
|
124
141
|
[1.1.0]: https://github.com/carloshpdoc/memorydetective/compare/v1.0.1...v1.1.0
|
|
125
142
|
[1.0.1]: https://github.com/carloshpdoc/memorydetective/compare/v1.0.0...v1.0.1
|
package/README.md
CHANGED
|
@@ -72,6 +72,40 @@ memorydetective classify ~/Desktop/myapp.memgraph
|
|
|
72
72
|
> **Claude:** *(calls `listTraceDevices` → `recordTimeProfile` → `analyzeHangs`)*
|
|
73
73
|
> Recorded `~/Desktop/run.trace` (90s, attached to DemoApp on iPhone 17 Pro Max). Found **23 user-visible hangs** (>500 ms each), longest 1.16 s, average 769 ms — severe load. All on the main thread. Sample-level hotspots aren't symbolicated yet; open the trace in Instruments for that step (or wait for `analyzeTimeProfile` v0.2).
|
|
74
74
|
|
|
75
|
+
### End-to-end: leak → file → fix suggestion
|
|
76
|
+
|
|
77
|
+
The full loop, from artifact to code edit. Combines memory-graph analysis with Swift source-bridging.
|
|
78
|
+
|
|
79
|
+
> **You:** Tem um leak. Memgraph em `~/Desktop/example-leaks.memgraph`. The cycle points at `DetailViewModel`. Find where it lives in the project `~/Development/myapp/` and suggest a fix.
|
|
80
|
+
>
|
|
81
|
+
> **Claude:** *(calls `analyzeMemgraph` → `classifyCycle` → `reachableFromCycle`)*
|
|
82
|
+
> Found **4 ROOT CYCLE blocks**. The dominant one matches `swiftui.tag-index-projection` with high confidence. `reachableFromCycle` confirms 4 `DetailViewModel` instances pinning ~1100 `NSURLSessionConfiguration`s each — the VM is the culprit, the network stack is collateral.
|
|
83
|
+
>
|
|
84
|
+
> *(calls `swiftSearchPattern` to locate `.tag(` + `onImageSliderTap` patterns in `~/Development/myapp/Sources/`)*
|
|
85
|
+
> 1 match in `MyApp/Views/MediaCarousel.swift:142`.
|
|
86
|
+
>
|
|
87
|
+
> *(calls `swiftGetSymbolDefinition({ symbolName: "DetailViewModel", projectRoot: "...", candidatePaths: ["MyApp/ViewModels/"] })`)*
|
|
88
|
+
> Declared at `MyApp/ViewModels/DetailViewModel.swift:18` — `class DetailViewModel: ObservableObject`.
|
|
89
|
+
>
|
|
90
|
+
> *(calls `swiftFindSymbolReferences` to scope the blast radius — 7 callsites)*
|
|
91
|
+
>
|
|
92
|
+
> **Suggested fix at `MediaCarousel.swift:142`:**
|
|
93
|
+
> ```swift
|
|
94
|
+
> // before
|
|
95
|
+
> .tag(index)
|
|
96
|
+
> .onImageSliderTap { index in
|
|
97
|
+
> self.viewModel.handlePhotoTap(at: index)
|
|
98
|
+
> }
|
|
99
|
+
>
|
|
100
|
+
> // after — static helper + weak captures
|
|
101
|
+
> .tag(index)
|
|
102
|
+
> .onImageSliderTap { [weak vm = _viewModel.wrappedValue,
|
|
103
|
+
> weak coord = self.coordinator] index in
|
|
104
|
+
> Self.handlePhotoTap(index: index, viewModel: vm, coordinator: coord)
|
|
105
|
+
> }
|
|
106
|
+
> ```
|
|
107
|
+
> Want me to apply this and verify with a fresh `.memgraph`?
|
|
108
|
+
|
|
75
109
|
---
|
|
76
110
|
|
|
77
111
|
## Configure
|
package/USAGE.md
CHANGED
|
@@ -117,15 +117,18 @@ Claude orchestrates the full flow (see [section 3](#3-how-fixes-actually-flow-fr
|
|
|
117
117
|
|
|
118
118
|
## 3. How fixes actually flow from diagnosis to edit
|
|
119
119
|
|
|
120
|
-
`memorydetective`
|
|
120
|
+
`memorydetective` covers the diagnose side **and the source-bridging side**. It tells you **what** is wrong, **where in the cycle**, **what type of fix** is needed, **where the relevant types live in your project** (via Swift LSP integration), and **every callsite that references them**. It does not edit your code — that final step still belongs to your LLM agent.
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
So the workflow has two halves:
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
| Half | Owned by `memorydetective` | Owned by the LLM agent |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| **Diagnose** | ✅ memgraph parsing, cycle classification, fix-hint catalog, hangs / allocations / app-launch / animation hitches | |
|
|
127
|
+
| **Locate in source** | ✅ `swiftGetSymbolDefinition`, `swiftFindSymbolReferences`, `swiftSearchPattern`, `swiftGetSymbolsOverview`, `swiftGetHoverInfo` (SourceKit-LSP under the hood) | |
|
|
128
|
+
| **Decide the actual edit** | | ✅ The agent reads the surrounding code, picks the right capture-list pattern, writes the diff |
|
|
129
|
+
| **Apply the edit** | | ✅ The agent's `Edit`/`MultiEdit` tools write to the user's file |
|
|
125
130
|
|
|
126
|
-
|
|
127
|
-
- LLM agents already excel at code editing. Letting them keep that role keeps `memorydetective` focused.
|
|
128
|
-
- The catalog of known patterns becomes the knowledge moat; the LLM brings the codebase awareness.
|
|
131
|
+
The split between "locate" (us) and "edit" (the agent) is intentional. Locating is a deterministic SourceKit-LSP query; editing requires understanding the user's surrounding code style, naming conventions, and intent — that's the LLM's strength. We give the agent every piece of structured information it needs to make the edit, and step back at the file-write boundary.
|
|
129
132
|
|
|
130
133
|
### Concrete end-to-end example
|
|
131
134
|
|
|
@@ -133,39 +136,45 @@ Configuration (one-time): `memorydetective` registered as an MCP server in Claud
|
|
|
133
136
|
|
|
134
137
|
You open Claude Code in your iOS project and ask:
|
|
135
138
|
|
|
136
|
-
> **You:**
|
|
139
|
+
> **You:** Tem um leak. Memgraph em `~/Desktop/example-leaks.memgraph`. The cycle points at `DetailViewModel`. Find where it lives in the project `~/Development/myapp/` and suggest a fix.
|
|
137
140
|
|
|
138
141
|
What Claude does, step by step:
|
|
139
142
|
|
|
140
|
-
1. **Calls `analyzeMemgraph(path
|
|
141
|
-
- Receives: 60,436 leaks, 4 ROOT CYCLE blocks, top cycle is a `_DictionaryStorage` chain.
|
|
143
|
+
1. **Calls `analyzeMemgraph(path)`**
|
|
144
|
+
- Receives: 60,436 leaks, 4 ROOT CYCLE blocks, top cycle is a `_DictionaryStorage` chain. App-level classes in chain: `DetailViewModel`, `GraphQLClient`, `ItemRepositoryImpl`.
|
|
145
|
+
|
|
146
|
+
2. **Calls `classifyCycle(path)`**
|
|
147
|
+
- Receives: `swiftui.tag-index-projection` (high confidence) + fix hint pointing at `.tag()` modifier capturing `self`.
|
|
148
|
+
|
|
149
|
+
3. **Calls `reachableFromCycle({ rootClassName: "DetailViewModel" })`**
|
|
150
|
+
- Confirms 4 `DetailViewModel` instances and ~1100 `NSURLSessionConfiguration`s reachable from each — the VM is the culprit pinning the network stack as collateral, not the other way around.
|
|
151
|
+
|
|
152
|
+
4. **Calls `swiftSearchPattern({ pattern: "\\.tag\\(", filePath: "..." })` across views**
|
|
153
|
+
- Surfaces 1 match in `MyApp/Views/MediaCarousel.swift:142`.
|
|
142
154
|
|
|
143
|
-
|
|
144
|
-
-
|
|
155
|
+
5. **Calls `swiftGetSymbolDefinition({ symbolName: "DetailViewModel", projectRoot: "~/Development/myapp/", candidatePaths: ["MyApp/ViewModels/"] })`**
|
|
156
|
+
- Returns `MyApp/ViewModels/DetailViewModel.swift:18` — `class DetailViewModel: ObservableObject`.
|
|
145
157
|
|
|
146
|
-
|
|
147
|
-
-
|
|
148
|
-
- `Grep` for `onImageSliderTap` (or whatever closure name surfaced in the chain).
|
|
149
|
-
- `Read` the candidate files to see how the closure is captured.
|
|
158
|
+
6. **Calls `swiftFindSymbolReferences({ symbolName: "DetailViewModel", filePath: "..." })`**
|
|
159
|
+
- Lists 7 callsites across the codebase. Helps the agent gauge fix blast radius.
|
|
150
160
|
|
|
151
|
-
|
|
161
|
+
7. **Presents the proposed fix in chat:**
|
|
152
162
|
|
|
153
|
-
> **Claude:** Found the cycle. `
|
|
163
|
+
> **Claude:** Found the cycle. `MediaCarousel.swift:142` passes `onImageSliderTap` as a closure that captures `self`. The `.tag(index)` modifier on the carousel `ForEach` triggers the SwiftUI internal `TagIndexProjection` observation that pins `DetailViewModel`.
|
|
154
164
|
>
|
|
155
|
-
> Suggested fix:
|
|
165
|
+
> Suggested fix:
|
|
156
166
|
> ```swift
|
|
157
167
|
> // before
|
|
158
168
|
> onImageSliderTap: { index in
|
|
159
169
|
> self.viewModel.handlePhotoTap(at: index)
|
|
160
170
|
> }
|
|
161
171
|
>
|
|
162
|
-
> // after
|
|
172
|
+
> // after — static helper + weak captures
|
|
163
173
|
> onImageSliderTap: { [weak vm = _viewModel.wrappedValue,
|
|
164
174
|
> weak coord = self.coordinator] index in
|
|
165
175
|
> Self.handlePhotoTap(index: index, viewModel: vm, coordinator: coord)
|
|
166
176
|
> }
|
|
167
177
|
>
|
|
168
|
-
> // plus a static helper:
|
|
169
178
|
> private static func handlePhotoTap(
|
|
170
179
|
> index: Int,
|
|
171
180
|
> viewModel: DetailViewModel?,
|
|
@@ -175,11 +184,11 @@ What Claude does, step by step:
|
|
|
175
184
|
>
|
|
176
185
|
> Want me to apply this?
|
|
177
186
|
|
|
178
|
-
|
|
187
|
+
8. **You accept** → Claude calls its own `Edit` tool → diff appears in your IDE → you review and commit.
|
|
179
188
|
|
|
180
|
-
|
|
189
|
+
9. **(Optional) Verify the fix:** export a fresh `.memgraph` after running the same flow, then ask Claude:
|
|
181
190
|
|
|
182
|
-
> **You:** Did the fix work? Compare `~/Desktop/
|
|
191
|
+
> **You:** Did the fix work? Compare `~/Desktop/before.memgraph` to `~/Desktop/after.memgraph`.
|
|
183
192
|
|
|
184
193
|
Claude calls `diffMemgraphs` — instance counts dropped, the `swiftui.tag-index-projection` cycle is gone from `cycles.persisted`, present in `cycles.goneFromBefore`.
|
|
185
194
|
|
|
@@ -202,12 +211,19 @@ Once you have the diagnosis, here are useful follow-up prompts you can paste int
|
|
|
202
211
|
| Prompt | What Claude calls |
|
|
203
212
|
|---|---|
|
|
204
213
|
| "How many `DetailViewModel` instances are leaking?" | `countAlive(path, className: "DetailViewModel")` |
|
|
214
|
+
| "How many `NSURLSessionConfiguration`s are *inside* the cycle rooted at `DetailViewModel`?" | `reachableFromCycle(path, rootClassName: "DetailViewModel", className: "NSURLSessionConfiguration")` |
|
|
205
215
|
| "Show the retain chain that keeps `DetailViewModel` alive." | `findRetainers(path, className: "DetailViewModel")` |
|
|
206
216
|
| "Compare `~/Desktop/before.memgraph` to `~/Desktop/after.memgraph` — did the leak go away?" | `diffMemgraphs(before, after)` |
|
|
207
217
|
| "Render the cycle as a Mermaid graph for the PR description." | `renderCycleGraph(path, format: "mermaid")` |
|
|
208
218
|
| "Profile this app on my iPhone for 90 seconds and tell me about hangs." | `listTraceDevices` → `recordTimeProfile` → `analyzeHangs` |
|
|
209
219
|
| "Pull the last 5 minutes of `error`-level logs from `MyApp`." | `logShow(last: "5m", process: "MyApp", level: "default")` |
|
|
210
220
|
| "Run my XCUITest with leak detection." | `detectLeaksInXCUITest(workspace, scheme, testIdentifier, …)` |
|
|
221
|
+
| **Source bridging — combine with the memory tools above:** | |
|
|
222
|
+
| "Where is `DetailViewModel` declared in this project?" | `swiftGetSymbolDefinition(symbolName, candidatePaths)` |
|
|
223
|
+
| "Find every reference to `DetailViewModel` across the codebase." | `swiftFindSymbolReferences(symbolName, filePath)` |
|
|
224
|
+
| "What types live in `MediaCarousel.swift`?" | `swiftGetSymbolsOverview(filePath)` |
|
|
225
|
+
| "What's the type at this position in this file?" | `swiftGetHoverInfo(filePath, line, character)` |
|
|
226
|
+
| "Search for `[weak self]` captures in this file." | `swiftSearchPattern(filePath, pattern: "\\[weak self\\]")` |
|
|
211
227
|
|
|
212
228
|
The agent decides which tool to call based on your prompt — you don't need to remember the tool names.
|
|
213
229
|
|
package/dist/cli.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorydetective",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|