ur-agent 1.35.0 → 1.36.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.
- package/CHANGELOG.md +9 -0
- package/README.md +26 -8
- package/bin/ur.js +31 -178
- package/dist/cli.js +2645 -983
- package/docs/CONFIGURATION.md +45 -0
- package/docs/TROUBLESHOOTING.md +16 -5
- package/docs/USAGE.md +8 -0
- package/docs/VALIDATION.md +3 -2
- package/docs/providers.md +74 -26
- package/documentation/index.html +2 -2
- package/extensions/vscode-ur-inline-diffs/extension.js +125 -15
- package/extensions/vscode-ur-inline-diffs/package.json +19 -7
- package/package.json +8 -3
package/docs/CONFIGURATION.md
CHANGED
|
@@ -15,6 +15,15 @@ UR-AGENT supports official provider access paths only:
|
|
|
15
15
|
vendor's official CLI using your subscription login. They are optional and
|
|
16
16
|
never required for normal UR runtime.
|
|
17
17
|
|
|
18
|
+
Explicit API and local/server providers are UR-native: UR owns request
|
|
19
|
+
shaping, native tool-call parsing, native streaming, and the UR-run Bash/File
|
|
20
|
+
tool permission, sandbox, and verifier flow. Subscription CLI providers cross
|
|
21
|
+
an external vendor CLI boundary instead — UR passes prompt text to the
|
|
22
|
+
official CLI and receives final text output. UR-native tool calling, streaming,
|
|
23
|
+
Bash/File tool execution, and sandbox guarantees apply to UR-run tools and
|
|
24
|
+
final UR output, not to actions the external CLI performs internally. See
|
|
25
|
+
[Provider Guide](providers.md) for the full provider capability matrix.
|
|
26
|
+
|
|
18
27
|
UR-AGENT never scrapes browser sessions, extracts OAuth refresh tokens, reads
|
|
19
28
|
hidden provider auth files, bypasses provider restrictions, or proxies consumer
|
|
20
29
|
web sessions as APIs.
|
|
@@ -229,11 +238,47 @@ The default policy separates command behavior into read, write, execute, and
|
|
|
229
238
|
network permission classes. It asks before destructive operations, recommends
|
|
230
239
|
sandboxing for write/execute/network commands, and denies common secret-file or
|
|
231
240
|
secret-like environment exfiltration patterns before broad Bash allow rules.
|
|
241
|
+
Command classification parses the shell command with an AST parser and falls
|
|
242
|
+
back to a conservative heuristic when parsing fails; anything it cannot
|
|
243
|
+
confidently classify is routed to the normal permission prompt instead of
|
|
244
|
+
being silently allowed.
|
|
232
245
|
|
|
233
246
|
Run `ur safety init` to write `.ur/safety-policy.json`. Commit it only when the
|
|
234
247
|
rules are safe and useful for the whole team; keep machine-local secrets and
|
|
235
248
|
local settings out of Git.
|
|
236
249
|
|
|
250
|
+
## Sandbox
|
|
251
|
+
|
|
252
|
+
`ur sandbox` inspects the OS-level sandbox that wraps UR-run Bash/File tool
|
|
253
|
+
commands:
|
|
254
|
+
|
|
255
|
+
```sh
|
|
256
|
+
ur sandbox status
|
|
257
|
+
ur sandbox check
|
|
258
|
+
ur sandbox eval "rm -rf build"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
UR enforces this policy before running a UR Bash/File tool call, not after.
|
|
262
|
+
Sandbox behavior has three modes, controlled by `sandbox.enabled` and
|
|
263
|
+
`sandbox.failIfUnavailable` in settings:
|
|
264
|
+
|
|
265
|
+
- **disabled** — `sandbox.enabled: false` (default). No OS-level confinement;
|
|
266
|
+
permission checks from the safety policy still apply.
|
|
267
|
+
- **recommended** — `sandbox.enabled: true`, `sandbox.failIfUnavailable: false`.
|
|
268
|
+
Commands run sandboxed when OS support is available; if it is not, UR warns
|
|
269
|
+
and continues unsandboxed rather than blocking work.
|
|
270
|
+
- **required** — `sandbox.enabled: true`, `sandbox.failIfUnavailable: true`.
|
|
271
|
+
UR fails closed: it refuses to start rather than run without a working
|
|
272
|
+
sandbox.
|
|
273
|
+
|
|
274
|
+
OS confinement depends on platform support: `sandbox-exec` (Seatbelt) on
|
|
275
|
+
macOS, or `bwrap` (bubblewrap) on Linux/WSL2. `ur sandbox check` reports
|
|
276
|
+
missing dependencies for the current platform.
|
|
277
|
+
|
|
278
|
+
This sandbox covers UR-run Bash/File tool commands only. For subscription CLI
|
|
279
|
+
providers, it does not extend to actions the external CLI performs internally
|
|
280
|
+
— see [Provider Guide](providers.md).
|
|
281
|
+
|
|
237
282
|
## Project Context Pack
|
|
238
283
|
|
|
239
284
|
`ur context-pack` writes durable architecture context and task memory:
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -30,13 +30,19 @@ ur --version
|
|
|
30
30
|
|
|
31
31
|
### Bun is missing or too old
|
|
32
32
|
|
|
33
|
-
- Likely cause: UR
|
|
34
|
-
|
|
35
|
-
-
|
|
33
|
+
- Likely cause: UR is not Node-native. Every install path — npm, GitHub, and
|
|
34
|
+
source checkouts — executes the CLI through Bun (this repository pins
|
|
35
|
+
`bun@1.3.14`). The npm-installed `bin/ur.js` launcher starts under Node
|
|
36
|
+
only to detect Bun and re-exec into it; if Bun is missing or too old, the
|
|
37
|
+
launcher prints `UR-AGENT requires Bun ... at runtime` and exits instead of
|
|
38
|
+
falling back to Node.
|
|
39
|
+
- Fix: install Bun, then rerun. Set `BUN_BIN` to an absolute Bun path if
|
|
40
|
+
`bun` is installed but not on `PATH`.
|
|
36
41
|
|
|
37
42
|
```sh
|
|
38
43
|
npm install -g bun # or: curl -fsSL https://bun.sh/install | bash
|
|
39
44
|
bun --version
|
|
45
|
+
ur --version
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
### Invalid or corrupted settings
|
|
@@ -156,12 +162,17 @@ ur -p --allowed-tools "Read,Edit,Bash(git:*)" "run the task"
|
|
|
156
162
|
### Permission or sandbox issues
|
|
157
163
|
|
|
158
164
|
- Likely cause: the requested command is classified as write/execute/network
|
|
159
|
-
and requires approval
|
|
160
|
-
|
|
165
|
+
and requires approval; OS sandbox dependencies (`sandbox-exec` on macOS,
|
|
166
|
+
`bwrap` on Linux/WSL2) are missing; or `sandbox.failIfUnavailable` (required
|
|
167
|
+
mode) is set and UR refused to start without a working sandbox.
|
|
168
|
+
- Fix: inspect the policy and the sandbox status; install missing sandbox
|
|
169
|
+
dependencies, or relax `sandbox.enabled`/`sandbox.failIfUnavailable` in
|
|
170
|
+
settings if required mode is not intended.
|
|
161
171
|
|
|
162
172
|
```sh
|
|
163
173
|
ur safety check --command "<the command>"
|
|
164
174
|
ur sandbox status
|
|
175
|
+
ur sandbox check
|
|
165
176
|
```
|
|
166
177
|
|
|
167
178
|
### Tests fail after an agent edit
|
package/docs/USAGE.md
CHANGED
|
@@ -122,6 +122,14 @@ using your subscription login (`ur auth <provider>`). The generic
|
|
|
122
122
|
`subscription` entry is an internal placeholder and is hidden from listings;
|
|
123
123
|
UR does not list fake subscription models.
|
|
124
124
|
|
|
125
|
+
Subscription CLI providers have an explicit external-CLI boundary. UR passes
|
|
126
|
+
prompt text to the official CLI and receives final text output. UR-native tool
|
|
127
|
+
calling, UR Bash/File tool execution, UR-native streaming, local command
|
|
128
|
+
permissions, sandbox guarantees, and verifier/done-gate checks apply to UR-run
|
|
129
|
+
tools/final UR output, not to actions the external CLI performs internally.
|
|
130
|
+
Use `ur provider status` or `ur provider doctor <provider>` to see provider
|
|
131
|
+
kind, external CLI usage, native tool/streaming support, and the boundary text.
|
|
132
|
+
|
|
125
133
|
Provider values accept canonical IDs and common aliases. For example,
|
|
126
134
|
`openai-api`, `anthropic-api`, `gemini-api`, `openrouter`, `ollama`,
|
|
127
135
|
`lmstudio`, `llama.cpp`, and `vllm` are UR-native runtime providers, and
|
package/docs/VALIDATION.md
CHANGED
|
@@ -12,13 +12,14 @@ You need:
|
|
|
12
12
|
- A second Ollama server on the LAN if you want to test network discovery.
|
|
13
13
|
- UR installed globally (`npm install -g ur-agent`) or this repo installed
|
|
14
14
|
globally (`bun add -g github:Maitham16/UR`) or a
|
|
15
|
-
local checkout (`bun run dev`).
|
|
15
|
+
local checkout (`bun run dev`). Bun is required at runtime for every path —
|
|
16
|
+
the npm launcher detects and execs Bun automatically; UR is not Node-native.
|
|
16
17
|
|
|
17
18
|
## 0. Smoke
|
|
18
19
|
|
|
19
20
|
```sh
|
|
20
21
|
ur --version
|
|
21
|
-
# expected: the version from package.json, e.g. "1.35.
|
|
22
|
+
# expected: the version from package.json, e.g. "1.35.1 (UR-AGENT)"
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
## 0.1 Permission safety and context pack (1.19.0)
|
package/docs/providers.md
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
UR-AGENT integrates official model access paths only. API-key providers, local
|
|
4
4
|
runtimes, and OpenAI-compatible servers are UR-native backends: UR owns the
|
|
5
|
-
conversation loop, tool
|
|
6
|
-
providers (Codex CLI, Claude Code, Gemini CLI,
|
|
7
|
-
bridges: they are first-class in `/model` and
|
|
8
|
-
vendor's official CLI using your subscription
|
|
9
|
-
required dependencies, and never used as a
|
|
5
|
+
conversation loop, native tool-call parsing, streaming, errors, and UR-run tool
|
|
6
|
+
execution. Subscription CLI providers (Codex CLI, Claude Code, Gemini CLI,
|
|
7
|
+
Antigravity) are external app bridges: they are first-class in `/model` and
|
|
8
|
+
dispatch each turn through the vendor's official CLI using your subscription
|
|
9
|
+
login. They are optional, never required dependencies, and never used as a
|
|
10
|
+
silent fallback.
|
|
10
11
|
|
|
11
12
|
## Legal auth policy
|
|
12
13
|
|
|
@@ -25,21 +26,59 @@ variables only when the user explicitly selects API mode.
|
|
|
25
26
|
|
|
26
27
|
## Provider matrix
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
29
|
+
Concise capability matrix — provider kind, native tool calls, native streaming,
|
|
30
|
+
multimodal input, external CLI boundary, and sandbox scope:
|
|
31
|
+
|
|
32
|
+
| Provider | Access type | Provider kind | External CLI | Native tools | Native streaming | Multimodal input | Sandbox scope | Runtime backend | Legal path |
|
|
33
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
34
|
+
| Subscription | subscription | subscription-placeholder | no | no | no | n/a | n/a (no runtime) | `subscription:unconfigured` | independent subscription runtime only |
|
|
35
|
+
| OpenAI API | API | UR-native | no | yes | yes | yes | UR Bash/File sandbox | `api:openai` | `OPENAI_API_KEY` |
|
|
36
|
+
| Claude API | API | UR-native | no | yes | yes | yes | UR Bash/File sandbox | `api:anthropic` | `ANTHROPIC_API_KEY` |
|
|
37
|
+
| Gemini API | API | UR-native | no | yes | yes | yes | UR Bash/File sandbox | `api:gemini` | `GEMINI_API_KEY` |
|
|
38
|
+
| OpenRouter | API/router | UR-native | no | yes | yes | yes | UR Bash/File sandbox | `api:openrouter` | `OPENROUTER_API_KEY` |
|
|
39
|
+
| Ollama | local | UR-native | no | yes | yes | yes* | UR Bash/File sandbox | `ollama` | localhost Ollama runtime |
|
|
40
|
+
| LM Studio | local/server | UR-native | no | yes | yes | yes | UR Bash/File sandbox | `openai-compatible:lmstudio` | local OpenAI-compatible server |
|
|
41
|
+
| llama.cpp | local/server | UR-native | no | yes | yes | yes | UR Bash/File sandbox | `openai-compatible:llama.cpp` | local OpenAI-compatible server |
|
|
42
|
+
| vLLM | local/server | UR-native | no | yes | yes | yes | UR Bash/File sandbox | `openai-compatible:vllm` | OpenAI-compatible server |
|
|
43
|
+
| Codex CLI | subscription | subscription-cli | yes | no | no | no† | UR-run tools/output only† | `subscription-cli:codex` | official Codex CLI login |
|
|
44
|
+
| Claude Code | subscription | subscription-cli | yes | no | no | no† | UR-run tools/output only† | `subscription-cli:claude-code` | official Claude Code CLI login |
|
|
45
|
+
| Gemini CLI | subscription | subscription-cli | yes | no | no | no† | UR-run tools/output only† | `subscription-cli:gemini` | official Gemini Code Assist login |
|
|
46
|
+
| Antigravity | subscription | subscription-cli | yes | no | no | no† | UR-run tools/output only† | `subscription-cli:antigravity` | official Antigravity CLI login, where supported |
|
|
47
|
+
|
|
48
|
+
\* Ollama forwards images only to models that advertise vision support;
|
|
49
|
+
unsupported models get a text placeholder instead of the image.
|
|
50
|
+
|
|
51
|
+
† External vendor CLI boundary (see below): UR passes prompt text only to the
|
|
52
|
+
official CLI, so image blocks are not forwarded, and UR-native tool/streaming/
|
|
53
|
+
sandbox guarantees stop at UR-run tools and final UR output.
|
|
54
|
+
|
|
55
|
+
Native tools and native streaming mean UR's own request/response loop parses
|
|
56
|
+
tool calls and streams tokens for that provider. Multimodal input means UR
|
|
57
|
+
preserves image content blocks (resized/normalized with `sharp`) into that
|
|
58
|
+
provider's wire format instead of stripping them. Sandbox scope states what
|
|
59
|
+
UR's OS-level sandbox (macOS `sandbox-exec`, Linux `bwrap`) actually covers
|
|
60
|
+
for that provider — see [Sandbox](CONFIGURATION.md#sandbox) for mode
|
|
61
|
+
details.
|
|
62
|
+
|
|
63
|
+
## Runtime boundary
|
|
64
|
+
|
|
65
|
+
UR-native providers use UR's provider adapters and tool loop. For those
|
|
66
|
+
providers, UR owns request shaping (including multimodal image-block mapping),
|
|
67
|
+
native tool-call parsing, native streaming, and the UR-run Bash/File tool
|
|
68
|
+
permission, sandbox, and verifier flow.
|
|
69
|
+
|
|
70
|
+
Subscription CLI providers use a different boundary:
|
|
71
|
+
|
|
72
|
+
> External vendor CLI boundary: UR passes prompt text to the official CLI and
|
|
73
|
+
> receives final text output. UR-native tool calling, UR Bash/File tool
|
|
74
|
+
> execution, UR-native streaming, local command permissions, sandbox guarantees,
|
|
75
|
+
> and verifier/done-gate checks apply to UR-run tools/final UR output, not to
|
|
76
|
+
> actions the external CLI performs internally.
|
|
77
|
+
|
|
78
|
+
That means the external CLI may have its own tool use, streaming, filesystem
|
|
79
|
+
access, network access, permissions, and safety behavior. UR reports CLI
|
|
80
|
+
failures as provider-scoped errors and does not fabricate assistant text or
|
|
81
|
+
silently switch to another provider.
|
|
43
82
|
|
|
44
83
|
## Commands
|
|
45
84
|
|
|
@@ -78,8 +117,9 @@ through that provider's backend:
|
|
|
78
117
|
- **Local/server providers** connect to the configured local or OpenAI-compatible endpoint (`/v1/chat/completions` for LM Studio, llama.cpp and vLLM; the native tags/chat API for Ollama)
|
|
79
118
|
- **Subscription CLI providers** (Codex CLI, Claude Code, Gemini CLI,
|
|
80
119
|
Antigravity) dispatch the turn through the vendor's official CLI using your
|
|
81
|
-
subscription login.
|
|
82
|
-
|
|
120
|
+
subscription login. They do not use UR-native tool calling, UR-native
|
|
121
|
+
streaming, or UR Bash/File tool execution inside the external CLI. Failures
|
|
122
|
+
remain provider-scoped and never fall back to Ollama or any other provider.
|
|
83
123
|
- The generic **`subscription`** entry is an internal placeholder with no
|
|
84
124
|
models and no backend; it is hidden from listings. Choose a specific
|
|
85
125
|
subscription CLI, API, or local/server provider instead.
|
|
@@ -108,6 +148,8 @@ You see all configured providers with:
|
|
|
108
148
|
- Credential type: `cli-login`, `api-key`, `local-runtime`, or `openai-compatible-endpoint`
|
|
109
149
|
- Short status message (e.g., "OPENAI_API_KEY found", "CLI not found", "localhost reachable")
|
|
110
150
|
- Runtime kind: `UR-native` or `external app bridge`
|
|
151
|
+
- Provider kind and boundary: `ur-native`, `subscription-cli`, or
|
|
152
|
+
`subscription-placeholder`
|
|
111
153
|
|
|
112
154
|
**Step 2: Model Selection**
|
|
113
155
|
|
|
@@ -124,6 +166,9 @@ After selecting a model, the confirmation shows:
|
|
|
124
166
|
- Selected model name
|
|
125
167
|
- Model source (live/cache/static)
|
|
126
168
|
- Runtime backend
|
|
169
|
+
- Provider status and doctor output also show provider kind, whether an
|
|
170
|
+
external CLI is used, whether UR-native tool calls/streaming are supported,
|
|
171
|
+
and the exact safety boundary.
|
|
127
172
|
- Effort level (if applicable)
|
|
128
173
|
- Thinking status (if enabled)
|
|
129
174
|
|
|
@@ -173,7 +218,7 @@ ur config set provider anthropic-api
|
|
|
173
218
|
| API providers (openai-api, anthropic-api, gemini-api, openrouter) | Live discovery from the provider's `/models` endpoint using your connected key (curated fallback until connected) | live |
|
|
174
219
|
| Local/server providers (ollama, lmstudio, llama.cpp, vllm) | Dynamic discovery from the selected provider endpoint | live |
|
|
175
220
|
| OpenAI-compatible | Dynamic discovery from configured endpoint | live |
|
|
176
|
-
| Subscription CLIs (codex-cli, claude-code-cli, gemini-cli, antigravity-cli) | Curated list (the official CLIs expose no models API); first-class in `/model`, dispatched via the official CLI. Log in with `ur auth <provider>` | static |
|
|
221
|
+
| Subscription CLIs (codex-cli, claude-code-cli, gemini-cli, antigravity-cli) | Curated list (the official CLIs expose no models API); first-class in `/model`, dispatched via the official CLI. External CLI behavior depends on the vendor CLI. Log in with `ur auth <provider>` | static |
|
|
177
222
|
|
|
178
223
|
### API vs Subscription distinction
|
|
179
224
|
|
|
@@ -220,7 +265,9 @@ ur connect logout openai-api # clear a stored key
|
|
|
220
265
|
- **Subscription providers** (`codex-cli`, `claude-code-cli`, `gemini-cli`,
|
|
221
266
|
`antigravity-cli`) connect through their official CLI login using your own
|
|
222
267
|
account; the session is persisted by that CLI. UR never scrapes or copies
|
|
223
|
-
those tokens.
|
|
268
|
+
those tokens. UR-native tools, sandbox guarantees, local command permissions,
|
|
269
|
+
and verifier/done-gate checks apply to UR-run tools/final UR output, not to
|
|
270
|
+
internal actions performed by the external CLI.
|
|
224
271
|
- **API providers** (`openai-api`, `anthropic-api`, `gemini-api`, `openrouter`)
|
|
225
272
|
store the key in your OS keychain (macOS Keychain, with an encrypted file
|
|
226
273
|
fallback) — the same secure store UR uses for its own credentials. At runtime
|
|
@@ -268,8 +315,9 @@ ur provider status
|
|
|
268
315
|
- Run `ur provider doctor <provider>` to check CLI presence and login status
|
|
269
316
|
- Install the vendor's official CLI if it is missing, then log in with
|
|
270
317
|
`ur auth <chatgpt|claude|gemini|antigravity>`
|
|
271
|
-
- Remember these run through the external vendor CLI; UR-native
|
|
272
|
-
|
|
318
|
+
- Remember these run through the external vendor CLI; UR-native tool calls,
|
|
319
|
+
UR-native streaming, Bash/File tool semantics, sandbox guarantees, and local
|
|
320
|
+
command permissions do not apply to what the external CLI does internally
|
|
273
321
|
|
|
274
322
|
**Provider shows "unavailable":**
|
|
275
323
|
- Check API key: `echo $OPENAI_API_KEY`
|
package/documentation/index.html
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
<main id="content" class="content">
|
|
45
45
|
<header class="topbar">
|
|
46
46
|
<div>
|
|
47
|
-
<p class="eyebrow">Version 1.
|
|
47
|
+
<p class="eyebrow">Version 1.36.0</p>
|
|
48
48
|
<h1>UR-AGENT Documentation</h1>
|
|
49
49
|
<p class="lead">A practical, tutorial-style reference for installing, configuring, automating, extending, and operating UR-AGENT.</p>
|
|
50
50
|
</div>
|
|
@@ -159,7 +159,7 @@ ur config set provider.fallback ollama</code></pre>
|
|
|
159
159
|
</article>
|
|
160
160
|
<article>
|
|
161
161
|
<h3>Status bar and updates</h3>
|
|
162
|
-
<pre><code>Ollama | llama3 | ask | main | update 1.
|
|
162
|
+
<pre><code>Ollama | llama3 | ask | main | update 1.36.0 available</code></pre>
|
|
163
163
|
<p>The interactive status bar shows only important runtime state: provider, model, mode, branch, active tasks, checks status when known, and update availability. It is hidden in CI, dumb terminals, and non-interactive mode.</p>
|
|
164
164
|
</article>
|
|
165
165
|
</div>
|
|
@@ -52,13 +52,58 @@ function escapeHtml(text) {
|
|
|
52
52
|
.replace(/"/g, '"')
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function formatCount(count, singular, plural = `${singular}s`) {
|
|
56
|
+
return `${count} ${count === 1 ? singular : plural}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function formatRelativeTime(value) {
|
|
60
|
+
if (!value) return 'unknown time'
|
|
61
|
+
const date = new Date(value)
|
|
62
|
+
if (Number.isNaN(date.getTime())) return String(value)
|
|
63
|
+
const deltaMs = Date.now() - date.getTime()
|
|
64
|
+
const minute = 60 * 1000
|
|
65
|
+
const hour = 60 * minute
|
|
66
|
+
const day = 24 * hour
|
|
67
|
+
if (deltaMs < minute) return 'just now'
|
|
68
|
+
if (deltaMs < hour) return `${Math.max(1, Math.floor(deltaMs / minute))}m ago`
|
|
69
|
+
if (deltaMs < day) return `${Math.floor(deltaMs / hour)}h ago`
|
|
70
|
+
if (deltaMs < 7 * day) return `${Math.floor(deltaMs / day)}d ago`
|
|
71
|
+
return date.toLocaleDateString()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function statusIcon(status) {
|
|
75
|
+
switch (status) {
|
|
76
|
+
case 'applied':
|
|
77
|
+
return new vscode.ThemeIcon('check', new vscode.ThemeColor('testing.iconPassed'))
|
|
78
|
+
case 'rejected':
|
|
79
|
+
return new vscode.ThemeIcon('circle-slash', new vscode.ThemeColor('testing.iconFailed'))
|
|
80
|
+
case 'commented':
|
|
81
|
+
return new vscode.ThemeIcon('comment-discussion', new vscode.ThemeColor('charts.yellow'))
|
|
82
|
+
default:
|
|
83
|
+
return new vscode.ThemeIcon('diff', new vscode.ThemeColor('charts.blue'))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
55
87
|
class DiffItem extends vscode.TreeItem {
|
|
56
88
|
constructor(bundle) {
|
|
57
|
-
|
|
89
|
+
const title = bundle.title || bundle.id
|
|
90
|
+
super(title, vscode.TreeItemCollapsibleState.None)
|
|
58
91
|
this.bundle = bundle
|
|
59
92
|
this.contextValue = 'diff'
|
|
60
|
-
|
|
61
|
-
|
|
93
|
+
const fileCount = bundle.files?.length ?? 0
|
|
94
|
+
const changedAt = bundle.updatedAt ?? bundle.createdAt
|
|
95
|
+
this.description = `${bundle.status ?? 'captured'} · ${formatCount(fileCount, 'file')} · ${formatRelativeTime(changedAt)}`
|
|
96
|
+
this.iconPath = statusIcon(bundle.status)
|
|
97
|
+
this.tooltip = new vscode.MarkdownString(
|
|
98
|
+
[
|
|
99
|
+
`**${escapeHtml(title)}**`,
|
|
100
|
+
'',
|
|
101
|
+
`- ID: \`${escapeHtml(bundle.id)}\``,
|
|
102
|
+
`- Status: ${escapeHtml(bundle.status ?? 'captured')}`,
|
|
103
|
+
`- Files: ${fileCount}`,
|
|
104
|
+
`- Patch: \`${escapeHtml(bundle.patchFile)}\``,
|
|
105
|
+
].join('\n'),
|
|
106
|
+
)
|
|
62
107
|
this.command = {
|
|
63
108
|
command: 'urInlineDiffs.open',
|
|
64
109
|
title: 'Open Inline Diff',
|
|
@@ -67,6 +112,27 @@ class DiffItem extends vscode.TreeItem {
|
|
|
67
112
|
}
|
|
68
113
|
}
|
|
69
114
|
|
|
115
|
+
class ActionItem extends vscode.TreeItem {
|
|
116
|
+
constructor(label, description, icon, command, tooltip) {
|
|
117
|
+
super(label, vscode.TreeItemCollapsibleState.None)
|
|
118
|
+
this.contextValue = 'urAction'
|
|
119
|
+
this.description = description
|
|
120
|
+
this.iconPath = new vscode.ThemeIcon(icon)
|
|
121
|
+
this.tooltip = tooltip ?? `${label}${description ? ` — ${description}` : ''}`
|
|
122
|
+
this.command = command
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
class InfoItem extends vscode.TreeItem {
|
|
127
|
+
constructor(label, description, icon = 'info') {
|
|
128
|
+
super(label, vscode.TreeItemCollapsibleState.None)
|
|
129
|
+
this.contextValue = 'urInfo'
|
|
130
|
+
this.description = description
|
|
131
|
+
this.iconPath = new vscode.ThemeIcon(icon)
|
|
132
|
+
this.tooltip = `${label}${description ? ` — ${description}` : ''}`
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
70
136
|
class DiffProvider {
|
|
71
137
|
constructor() {
|
|
72
138
|
this._onDidChangeTreeData = new vscode.EventEmitter()
|
|
@@ -83,12 +149,41 @@ class DiffProvider {
|
|
|
83
149
|
|
|
84
150
|
getChildren() {
|
|
85
151
|
const root = workspaceRoot()
|
|
86
|
-
if (!root)
|
|
87
|
-
|
|
152
|
+
if (!root) {
|
|
153
|
+
return [
|
|
154
|
+
new InfoItem('Open a workspace folder', 'UR inline diffs are scoped to the active project', 'folder-opened'),
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const manifest = loadManifest(root)
|
|
159
|
+
const diffs = manifest
|
|
88
160
|
.diffs
|
|
89
161
|
.slice()
|
|
90
162
|
.sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)))
|
|
91
|
-
|
|
163
|
+
|
|
164
|
+
if (diffs.length === 0) {
|
|
165
|
+
return [
|
|
166
|
+
new InfoItem(
|
|
167
|
+
'Ready for inline review',
|
|
168
|
+
fs.existsSync(manifestPath(root)) ? 'No pending diff bundles' : 'No diff bundles captured yet',
|
|
169
|
+
'pass',
|
|
170
|
+
),
|
|
171
|
+
new ActionItem(
|
|
172
|
+
'Show UR status',
|
|
173
|
+
'Provider, model, plugins',
|
|
174
|
+
'pulse',
|
|
175
|
+
{ command: 'urInlineDiffs.status', title: 'Show UR Status' },
|
|
176
|
+
),
|
|
177
|
+
new ActionItem(
|
|
178
|
+
'Refresh',
|
|
179
|
+
path.relative(root, manifestPath(root)),
|
|
180
|
+
'refresh',
|
|
181
|
+
{ command: 'urInlineDiffs.refresh', title: 'Refresh Inline Diffs' },
|
|
182
|
+
),
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return diffs.map(bundle => new DiffItem(bundle))
|
|
92
187
|
}
|
|
93
188
|
}
|
|
94
189
|
|
|
@@ -103,18 +198,29 @@ function renderDiffHtml(root, bundle) {
|
|
|
103
198
|
<meta charset="utf-8">
|
|
104
199
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
105
200
|
<style>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
201
|
+
:root { color-scheme: light dark; }
|
|
202
|
+
body { font: 13px/1.5 var(--vscode-font-family); color: var(--vscode-foreground); padding: 20px; margin: 0; }
|
|
203
|
+
header { border-bottom: 1px solid var(--vscode-panel-border); padding-bottom: 14px; margin-bottom: 16px; }
|
|
204
|
+
h1 { font-size: 20px; font-weight: 600; margin: 0 0 6px; }
|
|
205
|
+
h2 { font-size: 14px; margin: 20px 0 10px; }
|
|
206
|
+
.meta, .where { color: var(--vscode-descriptionForeground); }
|
|
207
|
+
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
|
|
208
|
+
.chip { border: 1px solid var(--vscode-panel-border); border-radius: 4px; padding: 3px 8px; background: var(--vscode-sideBar-background); }
|
|
209
|
+
pre { background: var(--vscode-editor-background); border: 1px solid var(--vscode-panel-border); border-radius: 6px; padding: 14px; overflow: auto; font-family: var(--vscode-editor-font-family); }
|
|
110
210
|
.comments { margin-top: 18px; }
|
|
111
|
-
.comment { border
|
|
112
|
-
.where { color: var(--vscode-descriptionForeground); }
|
|
211
|
+
.comment { border: 1px solid var(--vscode-panel-border); border-radius: 6px; padding: 10px 12px; margin-bottom: 10px; background: var(--vscode-sideBar-background); }
|
|
113
212
|
</style>
|
|
114
213
|
</head>
|
|
115
214
|
<body>
|
|
116
|
-
<
|
|
117
|
-
|
|
215
|
+
<header>
|
|
216
|
+
<h1>${escapeHtml(bundle.title)}</h1>
|
|
217
|
+
<div class="meta">${escapeHtml(bundle.id)} · ${escapeHtml(formatRelativeTime(bundle.updatedAt ?? bundle.createdAt))}</div>
|
|
218
|
+
<div class="chips">
|
|
219
|
+
<span class="chip">${escapeHtml(bundle.status ?? 'captured')}</span>
|
|
220
|
+
<span class="chip">${escapeHtml(formatCount(bundle.files?.length ?? 0, 'file'))}</span>
|
|
221
|
+
<span class="chip">${escapeHtml(formatCount(comments.length, 'comment'))}</span>
|
|
222
|
+
</div>
|
|
223
|
+
</header>
|
|
118
224
|
<pre>${escapeHtml(patch)}</pre>
|
|
119
225
|
<section class="comments">
|
|
120
226
|
<h2>Comments</h2>
|
|
@@ -256,9 +362,13 @@ async function showStatus(channel) {
|
|
|
256
362
|
function activate(context) {
|
|
257
363
|
const provider = new DiffProvider()
|
|
258
364
|
const channel = vscode.window.createOutputChannel('UR')
|
|
365
|
+
const tree = vscode.window.createTreeView('urInlineDiffs', {
|
|
366
|
+
treeDataProvider: provider,
|
|
367
|
+
showCollapseAll: false,
|
|
368
|
+
})
|
|
259
369
|
context.subscriptions.push(
|
|
260
370
|
channel,
|
|
261
|
-
|
|
371
|
+
tree,
|
|
262
372
|
vscode.commands.registerCommand('urInlineDiffs.refresh', () => provider.refresh()),
|
|
263
373
|
vscode.commands.registerCommand('urInlineDiffs.open', item => openDiff(item)),
|
|
264
374
|
vscode.commands.registerCommand('urInlineDiffs.comment', item => commentDiff(item, provider)),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "ur-inline-diffs",
|
|
3
3
|
"displayName": "UR Inline Diffs",
|
|
4
4
|
"description": "Review, apply, and reject UR inline diff bundles from .ur/ide/diffs inside VS Code.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.36.0",
|
|
6
6
|
"publisher": "ur-agent",
|
|
7
7
|
"engines": {
|
|
8
8
|
"vscode": "^1.92.0"
|
|
@@ -41,27 +41,39 @@
|
|
|
41
41
|
"commands": [
|
|
42
42
|
{
|
|
43
43
|
"command": "urInlineDiffs.refresh",
|
|
44
|
-
"title": "UR: Refresh Inline Diffs"
|
|
44
|
+
"title": "UR: Refresh Inline Diffs",
|
|
45
|
+
"icon": "$(refresh)"
|
|
45
46
|
},
|
|
46
47
|
{
|
|
47
48
|
"command": "urInlineDiffs.open",
|
|
48
|
-
"title": "UR: Open Inline Diff"
|
|
49
|
+
"title": "UR: Open Inline Diff",
|
|
50
|
+
"icon": "$(diff)"
|
|
49
51
|
},
|
|
50
52
|
{
|
|
51
53
|
"command": "urInlineDiffs.comment",
|
|
52
|
-
"title": "UR: Comment On Inline Diff"
|
|
54
|
+
"title": "UR: Comment On Inline Diff",
|
|
55
|
+
"icon": "$(comment-discussion)"
|
|
53
56
|
},
|
|
54
57
|
{
|
|
55
58
|
"command": "urInlineDiffs.apply",
|
|
56
|
-
"title": "UR: Apply Inline Diff"
|
|
59
|
+
"title": "UR: Apply Inline Diff",
|
|
60
|
+
"icon": "$(check)"
|
|
57
61
|
},
|
|
58
62
|
{
|
|
59
63
|
"command": "urInlineDiffs.reject",
|
|
60
|
-
"title": "UR: Reject Inline Diff"
|
|
64
|
+
"title": "UR: Reject Inline Diff",
|
|
65
|
+
"icon": "$(circle-slash)"
|
|
61
66
|
},
|
|
62
67
|
{
|
|
63
68
|
"command": "urInlineDiffs.status",
|
|
64
|
-
"title": "UR: Show Status (provider, model, plugins)"
|
|
69
|
+
"title": "UR: Show Status (provider, model, plugins)",
|
|
70
|
+
"icon": "$(pulse)"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"viewsWelcome": [
|
|
74
|
+
{
|
|
75
|
+
"view": "urInlineDiffs",
|
|
76
|
+
"contents": "UR inline diffs appear here when a review bundle is captured.\n[Show Status](command:urInlineDiffs.status) [Refresh](command:urInlineDiffs.refresh)"
|
|
65
77
|
}
|
|
66
78
|
],
|
|
67
79
|
"menus": {
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ur-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.36.0",
|
|
4
4
|
"description": "UR-AGENT — autonomous engineering workflow engine (plan, execute, test, verify, document, benchmark, reproduce)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "bun@1.3.14",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18.18",
|
|
9
|
+
"bun": ">=1.3.0"
|
|
10
|
+
},
|
|
7
11
|
"repository": {
|
|
8
12
|
"type": "git",
|
|
9
13
|
"url": "git+https://github.com/Maitham16/UR.git"
|
|
@@ -53,7 +57,9 @@
|
|
|
53
57
|
"package:check": "node scripts/package-check.mjs",
|
|
54
58
|
"prepack": "bun run release:check"
|
|
55
59
|
},
|
|
56
|
-
"dependencies": {
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"sharp": "^0.35.1"
|
|
62
|
+
},
|
|
57
63
|
"devDependencies": {
|
|
58
64
|
"@alcalzone/ansi-tokenize": "^0.1.0",
|
|
59
65
|
"@aws-sdk/credential-provider-node": "^3.972.58",
|
|
@@ -117,7 +123,6 @@
|
|
|
117
123
|
"react-compiler-runtime": "^1.0.0",
|
|
118
124
|
"react-reconciler": "^0.33.0",
|
|
119
125
|
"semver": "^7.6.0",
|
|
120
|
-
"sharp": "^0.35.1",
|
|
121
126
|
"shell-quote": "^1.8.0",
|
|
122
127
|
"signal-exit": "^4.1.0",
|
|
123
128
|
"stack-utils": "^2.0.0",
|