Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,17 @@ unity-cli reference status --output json
unity-cli reference find-symbol --name Animator --kind class
unity-cli reference grep "class Animator " --context 3
unity-cli reference view Runtime/Export/Animation/Animator.bindings.cs --start-line 100 --max-lines 60
unity-cli reference diff --from 2022.3.10f1 --to 2023.2.20f1 --symbol UnityEngine.Animator
unity-cli reference resolve-symbol-at Assets/Scripts/Player.cs --line 42 --column 18
unity-cli reference clean --keep 1 --dry-run
```

Use `reference find-symbol` first when you already know the type name (class / interface / struct / enum). It is backed by an on-disk index per Unity version (`~/.unity/cache/UnityCsReference/<version>/.unity-cli-index/symbols.json`) and is faster than `grep` for repeated lookups. Drop back to `grep` for free-text patterns or member-level matches.

Use `reference diff` to compare two cached Unity versions. The default symbol-only mode (`--symbol <fqn>`) returns a single before/after pair, and the opt-in path mode (`--path <subpath> [--max-symbols N]`) lists added / removed / changed type definitions in a directory.

Use `reference resolve-symbol-at <project-rel-path> --line <n> --column <m>` when the user is on a specific cursor position in a project file (`Assets/...` or `Packages/...`) and wants to see the canonical Unity reference for the type under the cursor. The tool is a thin wrapper: it reads the project file, extracts the identifier at the cursor, and feeds it through `reference find-symbol` + `reference view` for each cached version.

## Examples

- "Show me how Unity implements `Animator.Play` so I can predict whether it allocates."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Version Diff Playbook

Use this flow when comparing Unity API behavior across two cached versions, or when an LLM-suggested API needs to be validated against the canonical source for a specific project version.

## When to use

- The user wants to migrate a project from Unity X to Unity Y and asks which APIs changed.
- An LLM proposed `Animator.Play(stateName, layer)` but the project pins an older Unity version where that overload may not exist.
- A bug report cites a behavior change between Unity LTS versions and the team wants evidence from the source.

## Prerequisites

1. Both Unity versions are cached locally. Run `unity-cli reference status --output json` and check that the targets are listed. If not, fetch them:

```bash
unity-cli reference fetch --version 2022.3.10f1 --branch 2022.3/staging --accept-license
unity-cli reference fetch --version 2023.2.20f1 --branch 2023.2/staging --accept-license
```

2. The Phase 2 symbol index is generated on the first `find-symbol` or `diff` call per version; no extra command is needed.

## Symbol-only diff (default)

```bash
unity-cli reference diff --from 2022.3.10f1 --to 2023.2.20f1 --symbol UnityEngine.Animator
```

Output shape (`diffs` is an array; empty if the symbol is missing in both versions):

```json
{
"ok": true,
"from": "2022.3.10f1",
"to": "2023.2.20f1",
"diffs": [
{
"symbol": "UnityEngine.Animator",
"kind": "class",
"beforePath": "Runtime/Export/Animation/Animator.bindings.cs",
"beforeLine": 6,
"afterPath": "Runtime/Export/Animation/Animator.bindings.cs",
"afterLine": 8,
"hunks": [{ "before": ["..."], "after": ["..."], "beforeStart": 1, "afterStart": 1 }]
}
]
}
```

Tips:

- The view window is 30 lines around the symbol declaration. Use `reference view` for a larger window.
- When the symbol exists in only one version, the missing side has `beforePath` / `beforeLine` (or after) as `null`. Treat that as added / removed.

## Path-range diff (opt-in)

```bash
unity-cli reference diff --from 2022.3.10f1 --to 2023.2.20f1 --path Runtime/Export/Animation --max-symbols 50
```

Returns `{added, removed, changed, truncated}`. Use this when scanning a subdirectory; raise `--max-symbols` cautiously since each `changed` entry triggers a view + line diff. `truncated: true` means the cap was hit and there may be more results.

## Cursor-driven resolve

```bash
unity-cli reference resolve-symbol-at Assets/Scripts/Player.cs --line 42 --column 18 --version 2023.2.20f1
```

Output shape:

```json
{
"ok": true,
"cursorPath": "Assets/Scripts/Player.cs",
"cursorLine": 42,
"cursorColumn": 18,
"tokenName": "Animator",
"candidates": [
{
"version": "2023.2.20f1",
"fqn": "UnityEngine.Animator",
"kind": "class",
"referencePath": "Runtime/Export/Animation/Animator.bindings.cs",
"referenceLine": 8,
"viewExcerpt": ["..."]
}
]
}
```

The CLI extracts the identifier at the cursor (alphanumeric + `_`) and returns `null` when the cursor lands on whitespace, a comment, or a string literal. When `--version` is omitted, all cached versions are scanned and `candidates` is a flat list. The `unity-cli reference resolve-symbol-at` flow is a thin wrapper around `find-symbol` + `view`; it does not touch the csharp-lsp workspace.

## Anti-patterns

- Calling `reference diff --path` over the whole `Runtime/` tree without `--max-symbols`. The default cap is 50; the result still walks both versions' indexes once.
- Trusting `tokenName` when the cursor is inside a string literal or comment. The current extractor is regex-based and does not understand C# lexing.
- Using `resolve-symbol-at` to confirm method-level behavior. Phase 2 only indexes type definitions; for member-level signatures continue with `reference grep` until Phase 2.5 lands.
83 changes: 43 additions & 40 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@
*.pdb.meta
*.mdb.meta

# Unity meta files
# Don't ignore meta files in the Unity package
!UnityCliBridge/Packages/unity-cli-bridge/**/*.meta
# Unity meta files
# Don't ignore meta files in the Unity package
!UnityCliBridge/Packages/unity-cli-bridge/**/*.meta

# Visual Studio cache directory
.vs/

# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
!lsp/*.csproj
!lsp/Server.Core/*.csproj
!roslyn-cli/*.csproj
*.csproj
!lsp/*.csproj
!lsp/Server.Core/*.csproj
!roslyn-cli/*.csproj
*.unityproj
*.sln
*.suo
Expand Down Expand Up @@ -84,45 +84,48 @@ ehthumbs_vista.db
# Test output
coverage/
.nyc_output/
tests/.reports/
tests/.todo/
tasks/todo.md

# Build output
dist/
build/
target/
.cache/
CLAUDE.local.md

.unity/
UnityCliBridge/.unity/
.mcp/
UnityCliBridge/.mcp/
.worktrees/
tests/.reports/
tests/.todo/
tasks/todo.md
# Build output
dist/
build/
target/
.cache/
CLAUDE.local.md

.unity/
UnityCliBridge/.unity/
.mcp/
UnityCliBridge/.mcp/
.worktrees/

*.local.json

lsp/bin/
lsp/out/
lsp/Server.Core/bin/
lsp/Server.Core/obj/
lsp/coverage.cobertura.xml
lsp/bin/
lsp/out/
lsp/Server.Core/bin/
lsp/Server.Core/obj/
lsp/coverage.cobertura.xml

# Claude Code Hook temporary files
.claude/.last-suggest-timestamp
.claude/.last-session-id

# Generated test files for code index scalability testing
UnityCliBridge/Assets/Scripts/Generated/
UnityCliBridge/Assets/Scripts/Generated.meta

# Generated Unity scenes
UnityCliBridge/Assets/Scenes/Generated/
UnityCliBridge/Assets/Scenes/Generated.meta

UnityCliBridge/UnityCliBridge.slnx

# Local/generated histories
history.txt
# Generated test files for code index scalability testing
UnityCliBridge/Assets/Scripts/Generated/
UnityCliBridge/Assets/Scripts/Generated.meta

# Generated Unity scenes
UnityCliBridge/Assets/Scenes/Generated/
UnityCliBridge/Assets/Scenes/Generated.meta

UnityCliBridge/UnityCliBridge.slnx

# Local/generated histories
history.txt

# Generated by reference cache symbol indexer (Phase 2 build_or_update_index)
tests/fixtures/**/.unity-cli-index/

6 changes: 5 additions & 1 deletion docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ unity-cli tool schema input_keyboard --output json
unity-cli tool schema package_manager --output json
```

## Reference Cache (7 tools)
## Reference Cache (9 tools)

The `unity-cli reference *` family provides a local read-only mirror of the
official [UnityCsReference](https://github.com/Unity-Technologies/UnityCsReference)
Expand All @@ -268,6 +268,8 @@ fetch via `--accept-license` or `UNITY_CLI_ACCEPT_LICENSE=1`.
| `reference_view` | Display a slice of a file in the cached reference source by line range. |
| `reference_clean` | Remove old UnityCsReference snapshots, keeping the newest entries. |
| `reference_find_symbol` | Look up type definitions (class / interface / struct / enum) in the cached reference source via a per-version on-disk index. |
| `reference_diff` | Compare a symbol or path range between two cached Unity versions. Returns symbol-level hunks or `{added, removed, changed}`. |
| `reference_resolve_symbol_at` | Resolve the identifier at a project cursor position (`Assets/...` / `Packages/...`) to candidate reference cache entries with view excerpts. |

Typed CLI equivalents:

Expand All @@ -277,6 +279,8 @@ unity-cli reference status --output json
unity-cli reference find-symbol --name Animator --kind class
unity-cli reference grep "class Animator " --context 3
unity-cli reference view Runtime/Export/Animation/Animator.bindings.cs --start-line 100 --max-lines 60
unity-cli reference diff --from 2022.3.10f1 --to 2023.2.20f1 --symbol UnityEngine.Animator
unity-cli reference resolve-symbol-at Assets/Scripts/Player.cs --line 42 --column 18
unity-cli reference clean --keep 1 --dry-run
```

Expand Down
100 changes: 100 additions & 0 deletions src/app/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,43 @@ fn build_reference_call(command: &ReferenceCommand) -> (&'static str, Value) {
}
return ("reference_find_symbol", Value::Object(params));
}
ReferenceCommand::Diff {
from,
to,
symbol,
path,
max_symbols,
} => {
params.insert("from".to_string(), Value::String(from.clone()));
params.insert("to".to_string(), Value::String(to.clone()));
if let Some(s) = symbol {
params.insert("symbol".to_string(), Value::String(s.clone()));
}
if let Some(p) = path {
params.insert("path".to_string(), Value::String(p.clone()));
}
if let Some(n) = max_symbols {
params.insert("maxSymbols".to_string(), Value::Number((*n).into()));
}
return ("reference_diff", Value::Object(params));
}
ReferenceCommand::ResolveSymbolAt {
path,
line,
column,
version,
} => {
params.insert("path".to_string(), Value::String(path.clone()));
params.insert("line".to_string(), Value::Number((u64::from(*line)).into()));
params.insert(
"column".to_string(),
Value::Number((u64::from(*column)).into()),
);
if let Some(v) = version {
params.insert("version".to_string(), Value::String(v.clone()));
}
return ("reference_resolve_symbol_at", Value::Object(params));
}
ReferenceCommand::Clean {
keep,
version,
Expand Down Expand Up @@ -2160,6 +2197,69 @@ mod tests {
assert_eq!(params["dryRun"], true);
}

#[test]
fn build_reference_call_diff_symbol_mode() {
let cmd = ReferenceCommand::Diff {
from: "2022.3.10f1".to_string(),
to: "2023.2.20f1".to_string(),
symbol: Some("UnityEngine.Animator".to_string()),
path: None,
max_symbols: None,
};
let (tool, params) = build_reference_call(&cmd);
assert_eq!(tool, "reference_diff");
assert_eq!(params["from"], "2022.3.10f1");
assert_eq!(params["to"], "2023.2.20f1");
assert_eq!(params["symbol"], "UnityEngine.Animator");
assert!(params.get("path").is_none());
assert!(params.get("maxSymbols").is_none());
}

#[test]
fn build_reference_call_diff_path_mode_with_limit() {
let cmd = ReferenceCommand::Diff {
from: "v1".to_string(),
to: "v2".to_string(),
symbol: None,
path: Some("Runtime/Export".to_string()),
max_symbols: Some(20),
};
let (tool, params) = build_reference_call(&cmd);
assert_eq!(tool, "reference_diff");
assert_eq!(params["path"], "Runtime/Export");
assert_eq!(params["maxSymbols"], 20);
assert!(params.get("symbol").is_none());
}

#[test]
fn build_reference_call_resolve_symbol_at_with_version() {
let cmd = ReferenceCommand::ResolveSymbolAt {
path: "Assets/Scripts/Player.cs".to_string(),
line: 42,
column: 18,
version: Some("2023.2.20f1".to_string()),
};
let (tool, params) = build_reference_call(&cmd);
assert_eq!(tool, "reference_resolve_symbol_at");
assert_eq!(params["path"], "Assets/Scripts/Player.cs");
assert_eq!(params["line"], 42);
assert_eq!(params["column"], 18);
assert_eq!(params["version"], "2023.2.20f1");
}

#[test]
fn build_reference_call_resolve_symbol_at_minimal() {
let cmd = ReferenceCommand::ResolveSymbolAt {
path: "Packages/com.acme/Foo.cs".to_string(),
line: 1,
column: 1,
version: None,
};
let (tool, params) = build_reference_call(&cmd);
assert_eq!(tool, "reference_resolve_symbol_at");
assert!(params.get("version").is_none());
}

#[test]
fn build_reference_call_find_symbol_with_filters() {
let cmd = ReferenceCommand::FindSymbol {
Expand Down
23 changes: 23 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,29 @@ pub enum ReferenceCommand {
#[arg(long)]
version: Option<String>,
},
/// Diff a symbol or path range between two cached Unity versions.
Diff {
#[arg(long)]
from: String,
#[arg(long)]
to: String,
#[arg(long)]
symbol: Option<String>,
#[arg(long)]
path: Option<String>,
#[arg(long)]
max_symbols: Option<u64>,
},
/// Resolve the C# token at a cursor position to candidate reference cache entries.
ResolveSymbolAt {
path: String,
#[arg(long)]
line: u32,
#[arg(long)]
column: u32,
#[arg(long)]
version: Option<String>,
},
/// Remove old UnityCsReference snapshots, keeping the newest entries.
Clean {
#[arg(long, default_value_t = 1)]
Expand Down
Loading
Loading