Trace code in a tree structure. Double-click any trace point to navigate to its source, with full support for hierarchical parent-child structure.
>
# Code Trace Tree (Cursor)
Build and display code workflows as nested trees of line, file, and directory
trace points. Operate the hybrid storage used by **Code Trace Tree** (Cursor edition), then ask Cursor to reload the panel.
## Cursor vs other IDE skill packs
This skill is the **Cursor** edition (shipped as a Cursor plugin via `.cursor-plugin/plugin.json`).
- Cursor may **auto-attach** this skill when the chat matches the description (Customize → Skills → Agent Decides), or the user may invoke `/code-trace-tree`.
- Auto-attach does **not** mean auto-edit: only mutate or refresh traces when the user **explicitly asks** (e.g. generate topic-related nodes, add a tip, rebind after edits). Do not auto-sync every turn.
- JetBrains / VS Code skill zips are separate installs; they do not use this Cursor plugin bundle.
## Skill scripts location
Helper scripts live under `<Skill Root>/scripts/` (same folder as this `SKILL.md`).
**Skill Root** is the installed `code-trace-tree` skill directory. Prefer, in order:
1. This skill’s own directory (Cursor plugin install — resolve from the loaded skill / plugin path)
2. Project-local: `<repo>/.cursor/skills/code-trace-tree`
3. User global: `~/.cursor/skills/code-trace-tree`
On Windows, `~` is `%USERPROFILE%`. Resolve **Skill Root** once per session, then invoke scripts with absolute paths via `python` (or `python3` if that is what is on PATH). Keep the process CWD in the IDE project (do not `cd` into the skill folder).
```text
# Example — substitute the absolute Skill Root:
python "<Skill Root>/scripts/request_refresh.py"
```
In the rest of this file, `<Agent Skill Path>/code-trace-tree` means the same as `<Skill Root>`.
### Windows PowerShell quoting
If the agent shell is **PowerShell**, inner `"` in `--content` (and similar flags) are often stripped — e.g. `@PostMapping("/testPost")` arrives as `@PostMapping(/testPost)`, so LINE matching fails even with a correct `--line`. Insert `--%` (stop-parsing) after the script path and before those flags. `--%` is PowerShell-only (not cmd.exe / bash / Git Bash).
```text
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" --% add --file src/A.java --line 38 --content "@PostMapping(\"/testPost\")" --name testPost
```
Fallback when quotes are still awkward: distinctive substring tip + `--line` (e.g. `--content "@PostMapping(" --line 38`).
## Storage layout
| Piece | Location |
|-------|----------|
| Project id | Prefer `.idea/code-trace-tree.project.id` when present; else path match to global XML `<path>` / `<projectId>` (agents never write the `.idea` file) |
| Global XML | `<OS Config Dir>/code-trace-tree/` — folder-named for new projects (e.g. `MyProject.xml`); legacy `<projectId>.xml` still resolved by scanning `<projectId>` |
| Storage-ready (Case C bind) | `<OS Config Dir>/code-trace-tree/signals/<projectId>.storage-ready` (no TTL; written by refresh scripts) |
| Refresh signal (full) | `<OS Config Dir>/code-trace-tree/signals/<projectId>.request_refresh` (TTL 60s) |
| Refresh signal (one profile) | `<OS Config Dir>/code-trace-tree/signals/<projectId>.request_refresh_profile` (TTL 60s; body = profile name, empty → active) |
| Select signal | `<OS Config Dir>/code-trace-tree/signals/<projectId>.select_trace_points` (one UUID per line; TTL 60s) |
**Empty tree (Cursor):** when there are no trace nodes and only the default `main` profile (or no profiles), a custom empty-state webview replaces the Trace Points tree with a tip to create a root trace point from the editor/Command Palette. Recover UI (move/rename note + grey **Import stored data**) appears only when another stored global project still has a trace point. After clearing this workspace’s tree (including delete-all), the bound file is not treated as importable while empty, so recover UI stays hidden unless other projects have data.
**OS Config Dir:**
- Windows: `%LOCALAPPDATA%`
- macOS: `~/Library/Application Support`
- Linux: `$XDG_CONFIG_HOME` or `~/.config`
Resolve the bound XML with (optional project path discovers the IDE project root; default is CWD):
```text
python "<Agent Skill Path>/code-trace-tree/scripts/resolve_storage.py"
# optional:
python "<Agent Skill Path>/code-trace-tree/scripts/resolve_storage.py" /path/to/project
```
If the project has never used Code Trace Tree, there is no project id / XML yet.
Initialize storage before writing traces (or let `trace_tree add` / `move` / `delete` / `rebind` create it automatically).
**Resolve / init:** If `.idea/code-trace-tree.project.id` already exists, use that id and
its global XML (do not create a second project). If the id exists but XML is missing,
recreate XML with **that same** projectId (do not mint a new id). Else bind by XML
`<path>`. **Case C** (nothing found): create initial global XML with `<path>` only —
never create/write the `.idea` id file. Pass / resolve the project root so `<path>` is
correct; IDE binds via path match + `storage-ready`.
```text
python "<Agent Skill Path>/code-trace-tree/scripts/init_storage.py" /path/to/project
# or, when CWD is already inside the IDE project:
python "<Agent Skill Path>/code-trace-tree/scripts/init_storage.py"
```
## Preferred code workflow format
* When generating a code workflow, trace points with parent-child relationships should follow a clear nesting structure.
For example, if the parent node represents a method, its direct child nodes should represent methods called within that method, and their direct child nodes should point to the corresponding method definitions.
Example:
method A definition
- method B call
- method B definition
* Keep trace point names simple and concise. Add descriptions only when additional context is needed.
* Prefer **LINE** anchors whose trimmed text is **unique (or rare) in that file**. Avoid generic lines such as `}`, `return;`, or blank-looking braces. Occurrence index is how the plugin and `rebind` restore a line after it moves; duplicate content in the same file makes rebinding fragile.
* For multi-line calls, pass a **distinctive substring** of the best physical line (e.g. `.handleEmailTriggerRequest(`); the script stores the full trimmed line. Do not invent a logical “call name” that is not on one source line.
## Content matching and `isValid`
`isValid` is never stored. On load/reload:
| `traceType` | Valid when |
|--------------|------------|
| `LINE` | Path is a file and trimmed line at `lineNumber` matches `lineContent`, or occurrence rebinding succeeds |
| `FILE` | Path exists and is a file |
| `DIRECTORY` | Path exists and is a directory |
For `LINE` nodes, prefer the `trace_tree` scripts (they set `totalOccurrences` / `occurrenceIndex` automatically). Do **not** ask Claude to compute occurrence fields. Details: [references/data-format.md](references/data-format.md).
When adding LINE nodes, choose `lineContent` that stands out in the file so occurrence-based restore/rebind stays accurate after edits.
## Trace tree ops
Use the skill’s `scripts/trace_tree.py` to search, add, move, delete, and rebind nodes. Never pass occurrence fields.
### LINE locators (forgiving)
Stored tip is `[file, line, full-trimmed-line]`; persistence also keeps script-computed `occurrenceIndex` / `totalOccurrences` so **duplicate trimmed lines in one file** are distinct. Callers may pass a **stale line** and/or a **unique substring**; the script resolves to the full trimmed line. **Never pass occurrence fields** — the script sets them. On PowerShell, protect quoted `--content` with `--%` (see [Windows PowerShell quoting](#windows-powershell-quoting)) or use a substring tip plus `--line`.
| Tip | Result |
|-----|--------|
| Exact line + full trimmed text | Used as-is |
| Unique content (one match in file) | `--line` optional; line corrected if stale |
| Same trimmed text on 2+ lines | **Must** pass `--line` (or `[file, line, content]`); otherwise error |
| Distinctive substring (e.g. `.handleEmailTriggerRequest(`) | Anchors the matching line when unique; if several matches, pass `--line` |
| Multi-line call | Prefer the most distinctive physical line (often the `.methodName(` continuation) |
JSON output includes `resolve: { reason, needle, resolved, totalOccurrences, occurrenceIndex }`.
**Idempotent add:** Same identity → `"skipped": true` (exit 0). For LINE, identity is file + content + **occurrenceIndex** (so two `featureFlagService,` tips at different lines both add). FILE/DIRECTORY identity is path + type.
### Parent path
**Preferred:** repeat `--parent-id` from rootward ancestor → immediate parent. Omit for root on `add`. Avoids JSON quoting issues in shells.
```text
# Child of one node:
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" add … --parent-id "$PARENT_ID"
# Deeper path (rootward → parent):
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" add … --parent-id "$ID_A" --parent-id "$ID_B"
# Move to root:
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" move --id "$NODE_ID" --parent []
```
Optional `--parent` JSON (do **not** combine with `--parent-id`) for locator forms or root on `move`:
| Form | Example |
|------|---------|
| Root | `--parent []` (needed for `move` to root; `add` defaults to root) |
| Node id in JSON | `--parent '["3d41c2d1-…"]'` |
| `[file, content]` | `--parent '[["src/A.java","void methodA() {"]]'` — only when content is unique |
| `[file, line, content]` | `--parent '[["src/A.java",10,"void methodA() {"]]'` — when content repeats |
Bare strings inside `--parent` JSON are **UUIDs only**, not `traceName` labels. Prefer **`--parent-id` from a prior `search`/`add`**.
```text
method A def
method B call
method B def ← add with --parent-id idA --parent-id idB
```
**CLI shape:** `python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" <subcommand> [flags…]`
Shared flags (`--project`, `--profile`, `--dry-run`, `--no-refresh`) may appear **before or after** the subcommand:
```text
# Both OK (absolute `<Agent Skill Path>/...` scripts; keep CWD in the project):
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" search --project /path/to/project
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" --project /path/to/project search
```
Omit `--project` when the process CWD is already inside the IDE project (scripts walk upward to find `.cursor` / `.vscode` / `.idea` / `.git`, or other common project markers).
```text
# Absolute script path; do not cd into the skill folder
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" search
# line optional when content uniquely resolves; substring OK for distinctive tips:
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" add --file src/A.java --content '.handleEmailTriggerRequest(' --name 'handleEmail'
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" add --file src/A.java --line 10 --content 'void methodA() {' --name 'methodA'
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" add src/B.java 40 'void methodB() {' \
--parent-id "$PARENT_ID" \
--name 'methodB'
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" add --file src/C.java --line 20 --content 'void methodC() {' \
--parent-id "$ID_A" --parent-id "$ID_B" --name 'methodC'
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" move --file src/B.java --content 'void methodB() {' --parent '[]'
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" delete --id <uuid>
# After editing source on disk (IDE DocumentListener will NOT run):
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" rebind
python "<Agent Skill Path>/code-trace-tree/scripts/trace_tree.py" rebind --file src/A.java --file src/B.java
```
Default profile: `<activeProfileName>` (or pass `--profile`).
**Rebind after disk edits:** Agents do not edit through the IDE editor, so live line shifting does not apply. After any turn that modified project source, run `trace_tree rebind` (optionally `--file` for touched paths) before relying on locators or select/navigate. Rebind repairs `lineNumber` from trimmed `lineContent` and recomputes occurrences.
## Safe operations
| Goal | How |
|------|-----|
| List / find traces | `trace_tree search` (or parse profile XML) |
| Add root / child | `trace_tree add` with `--parent-id` (repeatable; idempotent if already present) |
| Reparent node | `trace_tree move` |
| Remove node + subtree | `trace_tree delete` |
| Repair lines after source edits | `trace_tree rebind` (required after agent disk edits) |
| Switch profile | Set `<activeProfileName>` or pass `--profile` to scripts |
## After refresh
The IDE watches **signal files** (not the XML path). After agent edits, always write a refresh signal.
| Signal | Effect |
|--------|--------|
| `request_refresh` | Full reload: all profiles, active profile, toolbar flags (`highlightingEnabled`, `namePromptEnabled`, `descriptionAreaOpened`, `advancedSettings`). Also writes `<projectId>.storage-ready` so an open Case C IDE can bind first. |
| `request_refresh_profile` | Reload one profile’s tree from XML into memory. Body = profile name (empty → active). Does **not** change active profile or toolbar flags. Also writes `storage-ready`. |
| `<projectId>.storage-ready` | Case C bind handshake (no TTL). Body = absolute project path (same as XML `<path>`). IDE filters on the body first; empty/legacy body falls back to XML `<path>`. Does not create storage. |
```text
python "<Agent Skill Path>/code-trace-tree/scripts/request_refresh.py"
python "<Agent Skill Path>/code-trace-tree/scripts/request_refresh_profile.py" main
```
Bound windows watch the shared global signals folder for that projectId. Unbound (Case C) windows watch `signals/*.storage-ready` until they bind. Refresh/select files older than 60s are ignored and removed; `storage-ready` has no TTL (agent overwrites).
## Additional resources
All under `<Agent Skill Path>/code-trace-tree/scripts/` (see Skill scripts location):
- XML schema details: [references/data-format.md](references/data-format.md)
- Resolve storage: `resolve_storage.py`
- Init storage (Case C when missing): `init_storage.py`
- Trace tree ops: `trace_tree.py`
- Request full IDE refresh: `request_refresh.py`
- Request one-profile IDE refresh: `request_refresh_profile.py`
- Select / navigate: `select_trace_points.py`
## Edit plugin data action
1. **Resolve** the project id + global XML (`resolve_storage.py`: prefer `.idea` id, else path). If missing, run `init_storage.py` (mutating `trace_tree` also auto-init). Do not create `.idea/code-trace-tree.project.id`.
2. **Read** the XML. Schema: [references/data-format.md](references/data-format.md).
3. **Edit** carefully (see rules below). Prefer atomic write: write `*.xml.tmp` then replace.
4. **Refresh IDE** (required — the plugin does not watch the XML file):
```text
python "<Agent Skill Path>/code-trace-tree/scripts/request_refresh.py"
# or one profile only:
python "<Agent Skill Path>/code-trace-tree/scripts/request_refresh_profile.py" main
```
Always write a refresh signal after agent edits so reload is explicit.
## Edit rules
- Keep `<project version="4">`, `<projectId>`, and `<path>` unless you intentionally rebind storage.
- Prefer existing `.idea/code-trace-tree.project.id` when resolving; never create/overwrite it. If that id exists but XML is gone, recreate XML with the same projectId (Case C otherwise = new global XML + `<path>` only).
- Bump `<updatedAt>` to the current epoch milliseconds when you change content.
- Every `<tracePoint>` needs `<traceType>`: `LINE`, `FILE`, or `DIRECTORY`.
- `traceName` is the user label; `baseName` is the last path segment; `tracePath` is **relative to the project root** (forward slashes preferred).
- For `LINE`: store trimmed `lineContent` and 1-based `lineNumber`. Prefer `trace_tree` scripts so `totalOccurrences` / `occurrenceIndex` are computed automatically.
- For `FILE` / `DIRECTORY`: omit line fields; `tracePath` is the file or directory path.
- Every `<tracePointNode>` needs `<id>` (UUID) and `<parentId>` (empty for roots).
- Nest children under `<children>`; child `parentId` must equal the parent node id.
- Do **not** persist `isValid` (runtime-only).
- Do not delete unrelated profiles. Default profile name is `main`.
- If the IDE has the project open, finish XML edits **before** writing the refresh request.
## Select / navigate in the IDE action
Write node UUIDs (one per line) to `signals/<projectId>.select_trace_points`, or use the helper scripts. Every open IDE window for that project watches the signal and selects / reveals those nodes. Stale signals (age > 60s) are ignored.
| Request | Tree | Editor |
|---------|------|--------|
| 1 valid id | Select + reveal | Navigate to source |
| 2+ valid ids | Select + reveal all | No navigation |
| Unknown ids only | No-op | No navigation |
Use after creating or locating traces when the user should see them in the IDE. Prefer a single id when you want the editor to jump to the source.
```text
python "<Agent Skill Path>/code-trace-tree/scripts/select_trace_points.py" <id> [id...]
```