ur-agent 1.36.1 → 1.37.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.
@@ -0,0 +1,2571 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/extension.ts
31
+ var extension_exports = {};
32
+ __export(extension_exports, {
33
+ activate: () => activate,
34
+ deactivate: () => deactivate
35
+ });
36
+ module.exports = __toCommonJS(extension_exports);
37
+ var vscode17 = __toESM(require("vscode"));
38
+
39
+ // src/actions/actions.ts
40
+ var vscode = __toESM(require("vscode"));
41
+ async function openBackgroundLog(item) {
42
+ const logFile = item?.task.logFile;
43
+ if (!logFile) {
44
+ vscode.window.showWarningMessage("No log file for this background task.");
45
+ return;
46
+ }
47
+ try {
48
+ const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(logFile));
49
+ await vscode.window.showTextDocument(doc, { preview: true });
50
+ } catch (error) {
51
+ vscode.window.showErrorMessage(
52
+ `Could not open background task log: ${error instanceof Error ? error.message : String(error)}`
53
+ );
54
+ }
55
+ }
56
+
57
+ // src/actions/actionsTreeProvider.ts
58
+ var vscode4 = __toESM(require("vscode"));
59
+
60
+ // src/diffs/store.ts
61
+ var fs = __toESM(require("node:fs"));
62
+ var path = __toESM(require("node:path"));
63
+ var vscode2 = __toESM(require("vscode"));
64
+ function workspaceRoot() {
65
+ return vscode2.workspace.workspaceFolders?.[0]?.uri.fsPath;
66
+ }
67
+ function diffsRoot(root) {
68
+ return path.join(root, ".ur", "ide", "diffs");
69
+ }
70
+ function manifestPath(root) {
71
+ return path.join(diffsRoot(root), "manifest.json");
72
+ }
73
+ function patchPath(root, bundle) {
74
+ return path.join(diffsRoot(root), bundle.patchFile);
75
+ }
76
+ function metadataPath(root, bundle) {
77
+ return path.join(diffsRoot(root), bundle.metadataFile);
78
+ }
79
+ function readJson(file, fallback) {
80
+ try {
81
+ return JSON.parse(fs.readFileSync(file, "utf8"));
82
+ } catch {
83
+ return fallback;
84
+ }
85
+ }
86
+ function writeJson(file, value) {
87
+ fs.mkdirSync(path.dirname(file), { recursive: true });
88
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}
89
+ `);
90
+ }
91
+ function loadManifest(root) {
92
+ const manifest = readJson(manifestPath(root), { version: 1, diffs: [] });
93
+ return Array.isArray(manifest.diffs) ? manifest : { version: 1, diffs: [] };
94
+ }
95
+ function loadBundleMetadata(root, bundle) {
96
+ return readJson(metadataPath(root, bundle), bundle);
97
+ }
98
+ function readPatch(root, bundle) {
99
+ const file = patchPath(root, bundle);
100
+ return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
101
+ }
102
+ function writeManifest(root, manifest) {
103
+ writeJson(manifestPath(root), manifest);
104
+ }
105
+ function writeBundleMetadata(root, bundle) {
106
+ writeJson(metadataPath(root, bundle), bundle);
107
+ }
108
+
109
+ // src/diffs/treeProvider.ts
110
+ var vscode3 = __toESM(require("vscode"));
111
+
112
+ // src/util/format.ts
113
+ function escapeHtml(text) {
114
+ return String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
115
+ }
116
+ function formatCount(count, singular, plural = `${singular}s`) {
117
+ return `${count} ${count === 1 ? singular : plural}`;
118
+ }
119
+ function formatRelativeTime(value) {
120
+ if (!value) return "unknown time";
121
+ const date = new Date(value);
122
+ if (Number.isNaN(date.getTime())) return String(value);
123
+ const deltaMs = Date.now() - date.getTime();
124
+ const minute = 60 * 1e3;
125
+ const hour = 60 * minute;
126
+ const day = 24 * hour;
127
+ if (deltaMs < minute) return "just now";
128
+ if (deltaMs < hour) return `${Math.max(1, Math.floor(deltaMs / minute))}m ago`;
129
+ if (deltaMs < day) return `${Math.floor(deltaMs / hour)}h ago`;
130
+ if (deltaMs < 7 * day) return `${Math.floor(deltaMs / day)}d ago`;
131
+ return date.toLocaleDateString();
132
+ }
133
+ function errorMessage(error) {
134
+ return error instanceof Error ? error.message : String(error);
135
+ }
136
+ function processErrorMessage(error) {
137
+ if (typeof error === "object" && error !== null && "stderr" in error) {
138
+ const stderr = error.stderr;
139
+ if (typeof stderr === "string" && stderr.trim()) return stderr.trim();
140
+ }
141
+ return errorMessage(error);
142
+ }
143
+
144
+ // src/diffs/treeProvider.ts
145
+ function statusIcon(status) {
146
+ switch (status) {
147
+ case "approved":
148
+ return new vscode3.ThemeIcon("check", new vscode3.ThemeColor("testing.iconPassed"));
149
+ case "rejected":
150
+ return new vscode3.ThemeIcon("circle-slash", new vscode3.ThemeColor("testing.iconFailed"));
151
+ case "commented":
152
+ return new vscode3.ThemeIcon("comment-discussion", new vscode3.ThemeColor("charts.yellow"));
153
+ default:
154
+ return new vscode3.ThemeIcon("diff", new vscode3.ThemeColor("charts.blue"));
155
+ }
156
+ }
157
+ var DiffTreeItem = class extends vscode3.TreeItem {
158
+ bundle;
159
+ constructor(bundle) {
160
+ const title = bundle.title || bundle.id;
161
+ super(title, vscode3.TreeItemCollapsibleState.None);
162
+ this.bundle = bundle;
163
+ this.contextValue = "diff";
164
+ const fileCount = bundle.files?.length ?? 0;
165
+ const changedAt = bundle.updatedAt ?? bundle.createdAt;
166
+ this.description = `${bundle.status ?? "captured"} \xB7 ${formatCount(fileCount, "file")} \xB7 ${formatRelativeTime(changedAt)}`;
167
+ this.iconPath = statusIcon(bundle.status);
168
+ this.tooltip = new vscode3.MarkdownString(
169
+ [
170
+ `**${escapeHtml(title)}**`,
171
+ "",
172
+ `- ID: \`${escapeHtml(bundle.id)}\``,
173
+ `- Status: ${escapeHtml(bundle.status ?? "captured")}`,
174
+ `- Files: ${fileCount}`,
175
+ `- Patch: \`${escapeHtml(bundle.patchFile)}\``
176
+ ].join("\n")
177
+ );
178
+ this.command = {
179
+ command: "urInlineDiffs.open",
180
+ title: "Open Inline Diff",
181
+ arguments: [this]
182
+ };
183
+ }
184
+ };
185
+ var InfoItem = class extends vscode3.TreeItem {
186
+ constructor(label, description, icon = "info") {
187
+ super(label, vscode3.TreeItemCollapsibleState.None);
188
+ this.contextValue = "urInfo";
189
+ this.description = description;
190
+ this.iconPath = new vscode3.ThemeIcon(icon);
191
+ this.tooltip = `${label}${description ? ` \u2014 ${description}` : ""}`;
192
+ }
193
+ };
194
+ var DiffTreeProvider = class {
195
+ _onDidChangeTreeData = new vscode3.EventEmitter();
196
+ onDidChangeTreeData = this._onDidChangeTreeData.event;
197
+ refresh() {
198
+ this._onDidChangeTreeData.fire();
199
+ }
200
+ getTreeItem(item) {
201
+ return item;
202
+ }
203
+ getChildren() {
204
+ const root = workspaceRoot();
205
+ if (!root) {
206
+ return [
207
+ new InfoItem(
208
+ "Open a workspace folder",
209
+ "UR inline diffs are scoped to the active project",
210
+ "folder-opened"
211
+ )
212
+ ];
213
+ }
214
+ const manifest = loadManifest(root);
215
+ const diffs = manifest.diffs.slice().sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)));
216
+ if (diffs.length === 0) {
217
+ return [];
218
+ }
219
+ return diffs.map((bundle) => new DiffTreeItem(bundle));
220
+ }
221
+ };
222
+
223
+ // src/bridge/urCli.ts
224
+ var import_node_child_process = require("node:child_process");
225
+ var import_node_util = require("node:util");
226
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
227
+ async function runUrCli(args, options) {
228
+ try {
229
+ const { stdout, stderr } = await execFileAsync("ur", args, {
230
+ cwd: options.cwd,
231
+ shell: false
232
+ });
233
+ return { stdout, stderr };
234
+ } catch (error) {
235
+ throw new Error(formatUrCliError(args, error));
236
+ }
237
+ }
238
+ async function runUrCliCapture(args, options) {
239
+ try {
240
+ const { stdout, stderr } = await execFileAsync("ur", args, {
241
+ cwd: options.cwd,
242
+ shell: false
243
+ });
244
+ return { stdout, stderr, exitCode: 0 };
245
+ } catch (error) {
246
+ if (isCapturedNonZeroExit(error)) {
247
+ return { stdout: error.stdout, stderr: error.stderr, exitCode: error.code };
248
+ }
249
+ throw new Error(formatUrCliError(args, error));
250
+ }
251
+ }
252
+ function formatUrCliError(args, error) {
253
+ const stderr = hasStderr(error) ? error.stderr.trim() : "";
254
+ const detail = stderr || (error instanceof Error ? error.message : String(error));
255
+ return `Failed to run \`ur ${args.join(" ")}\`: ${detail}. Ensure the UR CLI is installed and on PATH.`;
256
+ }
257
+ function hasStderr(error) {
258
+ return typeof error === "object" && error !== null && "stderr" in error && typeof error.stderr === "string";
259
+ }
260
+ function isCapturedNonZeroExit(error) {
261
+ return typeof error === "object" && error !== null && typeof error.code === "number" && typeof error.stdout === "string";
262
+ }
263
+
264
+ // src/actions/background.ts
265
+ var VALID_STATUSES = ["queued", "running", "completed", "failed", "canceled"];
266
+ function isRecord(value) {
267
+ return typeof value === "object" && value !== null;
268
+ }
269
+ function parseBackgroundListJson(raw) {
270
+ let data;
271
+ try {
272
+ data = JSON.parse(raw);
273
+ } catch {
274
+ return [];
275
+ }
276
+ if (!isRecord(data) || !Array.isArray(data.tasks)) return [];
277
+ const summaries = [];
278
+ for (const entry of data.tasks) {
279
+ if (!isRecord(entry)) continue;
280
+ if (typeof entry.id !== "string" || typeof entry.task !== "string") continue;
281
+ const status = VALID_STATUSES.includes(entry.status) ? entry.status : "queued";
282
+ summaries.push({
283
+ id: entry.id,
284
+ task: entry.task,
285
+ status,
286
+ logFile: typeof entry.logFile === "string" ? entry.logFile : ""
287
+ });
288
+ }
289
+ return summaries;
290
+ }
291
+ async function loadBackgroundTasks(cwd) {
292
+ try {
293
+ const { stdout } = await runUrCliCapture(["bg", "list", "--json"], { cwd });
294
+ return parseBackgroundListJson(stdout);
295
+ } catch {
296
+ return [];
297
+ }
298
+ }
299
+
300
+ // src/actions/actionsTreeProvider.ts
301
+ function backgroundStatusIcon(status) {
302
+ switch (status) {
303
+ case "completed":
304
+ return new vscode4.ThemeIcon("check", new vscode4.ThemeColor("testing.iconPassed"));
305
+ case "failed":
306
+ return new vscode4.ThemeIcon("error", new vscode4.ThemeColor("testing.iconFailed"));
307
+ case "canceled":
308
+ return new vscode4.ThemeIcon("circle-slash", new vscode4.ThemeColor("charts.yellow"));
309
+ case "running":
310
+ return new vscode4.ThemeIcon("sync~spin", new vscode4.ThemeColor("charts.blue"));
311
+ default:
312
+ return new vscode4.ThemeIcon("clock");
313
+ }
314
+ }
315
+ var BackgroundTaskItem = class extends vscode4.TreeItem {
316
+ task;
317
+ constructor(task) {
318
+ super(task.task, vscode4.TreeItemCollapsibleState.None);
319
+ this.task = task;
320
+ this.contextValue = "backgroundTask";
321
+ this.description = task.status;
322
+ this.iconPath = backgroundStatusIcon(task.status);
323
+ this.tooltip = `${task.id} \u2014 ${task.status}${task.logFile ? `
324
+ ${task.logFile}` : ""}`;
325
+ if (task.logFile) {
326
+ this.command = { command: "urActions.openBackgroundLog", title: "Open Log", arguments: [this] };
327
+ }
328
+ }
329
+ };
330
+ var SectionItem = class extends vscode4.TreeItem {
331
+ constructor(kind, label, count) {
332
+ super(label, count > 0 ? vscode4.TreeItemCollapsibleState.Expanded : vscode4.TreeItemCollapsibleState.None);
333
+ this.kind = kind;
334
+ this.description = String(count);
335
+ this.contextValue = "urActionsSection";
336
+ }
337
+ kind;
338
+ };
339
+ var InfoItem2 = class extends vscode4.TreeItem {
340
+ constructor(label, description, icon = "info") {
341
+ super(label, vscode4.TreeItemCollapsibleState.None);
342
+ this.contextValue = "urInfo";
343
+ this.description = description;
344
+ this.iconPath = new vscode4.ThemeIcon(icon);
345
+ this.tooltip = `${label}${description ? ` \u2014 ${description}` : ""}`;
346
+ }
347
+ };
348
+ var ActionsTreeProvider = class {
349
+ _onDidChangeTreeData = new vscode4.EventEmitter();
350
+ onDidChangeTreeData = this._onDidChangeTreeData.event;
351
+ diffs = [];
352
+ backgroundTasks = [];
353
+ loaded = false;
354
+ refresh() {
355
+ this.loaded = false;
356
+ this._onDidChangeTreeData.fire();
357
+ }
358
+ getTreeItem(item) {
359
+ return item;
360
+ }
361
+ async getChildren(element) {
362
+ const root = workspaceRoot();
363
+ if (!root) {
364
+ return [new InfoItem2("Open a workspace folder", "UR actions are scoped to the active project", "folder-opened")];
365
+ }
366
+ if (!this.loaded) {
367
+ this.diffs = loadManifest(root).diffs;
368
+ this.backgroundTasks = await loadBackgroundTasks(root);
369
+ this.loaded = true;
370
+ }
371
+ if (!element) {
372
+ if (this.diffs.length === 0 && this.backgroundTasks.length === 0) {
373
+ return [];
374
+ }
375
+ return [
376
+ new SectionItem("diffs", "Diff Bundles", this.diffs.length),
377
+ new SectionItem("background", "Background Tasks", this.backgroundTasks.length)
378
+ ];
379
+ }
380
+ if (element instanceof SectionItem && element.kind === "diffs") {
381
+ if (this.diffs.length === 0) {
382
+ return [new InfoItem2("No diff bundles", "Captured review bundles will appear here", "diff")];
383
+ }
384
+ return this.diffs.slice().sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt))).map((bundle) => new DiffTreeItem(bundle));
385
+ }
386
+ if (element instanceof SectionItem && element.kind === "background") {
387
+ if (this.backgroundTasks.length === 0) {
388
+ return [new InfoItem2("No background tasks", "Tasks started with `ur bg run` will appear here", "circle-outline")];
389
+ }
390
+ return this.backgroundTasks.map((task) => new BackgroundTaskItem(task));
391
+ }
392
+ return [];
393
+ }
394
+ };
395
+
396
+ // src/chat/chatController.ts
397
+ var import_node_crypto2 = require("node:crypto");
398
+ var vscode6 = __toESM(require("vscode"));
399
+
400
+ // src/bridge/types.ts
401
+ function isControlRequest(message) {
402
+ return message.type === "control_request" && typeof message.request_id === "string";
403
+ }
404
+ function isControlCancelRequest(message) {
405
+ return message.type === "control_cancel_request" && typeof message.request_id === "string";
406
+ }
407
+ function isCanUseToolRequest(message) {
408
+ return message.request?.subtype === "can_use_tool";
409
+ }
410
+
411
+ // src/bridge/urProcess.ts
412
+ var import_node_child_process2 = require("node:child_process");
413
+ var NdjsonBuffer = class {
414
+ buffer = "";
415
+ /** Feed a raw chunk (may contain zero, one, or many complete lines, and may
416
+ * split a line across two calls). Returns every complete, parseable line
417
+ * found. Malformed lines are dropped, never thrown — the CLI's own
418
+ * stdout-guard (streamJsonStdoutGuard.ts) already diverts non-JSON writes
419
+ * to stderr, so a malformed line here means something unexpected slipped
420
+ * through, not a reason to crash the extension. */
421
+ push(chunk) {
422
+ this.buffer += chunk;
423
+ const messages = [];
424
+ for (; ; ) {
425
+ const newline = this.buffer.indexOf("\n");
426
+ if (newline === -1) break;
427
+ const line = this.buffer.slice(0, newline);
428
+ this.buffer = this.buffer.slice(newline + 1);
429
+ const parsed = parseNdjsonLine(line);
430
+ if (parsed) messages.push(parsed);
431
+ }
432
+ return messages;
433
+ }
434
+ /** Whatever is left with no trailing newline yet (a genuinely partial line
435
+ * stays buffered; call this only once the stream has actually ended). */
436
+ flush() {
437
+ const rest = this.buffer;
438
+ this.buffer = "";
439
+ const parsed = parseNdjsonLine(rest);
440
+ return parsed ? [parsed] : [];
441
+ }
442
+ };
443
+ function parseNdjsonLine(line) {
444
+ const trimmed = line.trim();
445
+ if (!trimmed) return null;
446
+ try {
447
+ const value = JSON.parse(trimmed);
448
+ if (value && typeof value === "object" && typeof value.type === "string") {
449
+ return value;
450
+ }
451
+ return null;
452
+ } catch {
453
+ return null;
454
+ }
455
+ }
456
+ function buildUrArgs(request) {
457
+ const args = [
458
+ "-p",
459
+ "--output-format",
460
+ "stream-json",
461
+ "--verbose",
462
+ "--permission-prompt-tool",
463
+ "stdio"
464
+ ];
465
+ if (request.resumeSessionId) args.push("--resume", request.resumeSessionId);
466
+ if (request.model) args.push("--model", request.model);
467
+ args.push(request.prompt);
468
+ return args;
469
+ }
470
+ function buildControlResponse(requestId, decision) {
471
+ return {
472
+ type: "control_response",
473
+ response: {
474
+ request_id: requestId,
475
+ subtype: "success",
476
+ response: decision
477
+ }
478
+ };
479
+ }
480
+ var defaultSpawn = (command, args, options) => (0, import_node_child_process2.spawn)(command, args, options);
481
+ function runUrTurn(request, handlers, deps = {}) {
482
+ const spawnFn = deps.spawn ?? defaultSpawn;
483
+ const command = deps.command ?? "ur";
484
+ const args = buildUrArgs(request);
485
+ let child;
486
+ try {
487
+ child = spawnFn(command, args, {
488
+ cwd: request.cwd,
489
+ shell: false,
490
+ stdio: ["pipe", "pipe", "pipe"]
491
+ });
492
+ } catch (error) {
493
+ handlers.onExit({
494
+ ok: false,
495
+ exitCode: null,
496
+ canceled: false,
497
+ sawResult: false,
498
+ stderr: "",
499
+ error: `Failed to start \`${command}\`: ${errorMessage2(error)}. Ensure the UR CLI is installed and on PATH.`
500
+ });
501
+ return { cancel: () => {
502
+ } };
503
+ }
504
+ const stdoutBuffer = new NdjsonBuffer();
505
+ const stderrChunks = [];
506
+ let sawResult = false;
507
+ let resultIsError = false;
508
+ let canceled = false;
509
+ let settled = false;
510
+ const finish = (exitCode, spawnError) => {
511
+ if (settled) return;
512
+ settled = true;
513
+ const stderr = stderrChunks.join("");
514
+ const ok = !canceled && !spawnError && sawResult && !resultIsError;
515
+ handlers.onExit({
516
+ ok,
517
+ exitCode,
518
+ canceled,
519
+ sawResult,
520
+ stderr,
521
+ error: spawnError ?? (!ok && !canceled ? deriveErrorMessage(sawResult, resultIsError, exitCode, stderr) : void 0)
522
+ });
523
+ };
524
+ const handleMessage = (message) => {
525
+ if (message.type === "result") {
526
+ sawResult = true;
527
+ resultIsError = message.is_error === true;
528
+ }
529
+ if (isControlRequest(message) && isCanUseToolRequest(message)) {
530
+ void handlers.onControlRequest(message).then((decision) => {
531
+ writeControlResponse(child, message.request_id, decision);
532
+ }).catch((error) => {
533
+ writeControlResponse(child, message.request_id, {
534
+ behavior: "deny",
535
+ message: `Permission prompt failed in the extension: ${errorMessage2(error)}`
536
+ });
537
+ });
538
+ }
539
+ handlers.onMessage(message);
540
+ };
541
+ child.stdout?.on("data", (chunk) => {
542
+ for (const message of stdoutBuffer.push(chunk.toString("utf8"))) {
543
+ handleMessage(message);
544
+ }
545
+ });
546
+ child.stderr?.on("data", (chunk) => {
547
+ stderrChunks.push(chunk.toString("utf8"));
548
+ });
549
+ child.on("error", (error) => {
550
+ finish(null, `Failed to run \`${command}\`: ${errorMessage2(error)}. Ensure the UR CLI is installed and on PATH.`);
551
+ });
552
+ child.on("exit", (code) => {
553
+ for (const message of stdoutBuffer.flush()) {
554
+ handleMessage(message);
555
+ }
556
+ finish(code);
557
+ });
558
+ return {
559
+ cancel: () => {
560
+ if (settled) return;
561
+ canceled = true;
562
+ child.kill("SIGTERM");
563
+ }
564
+ };
565
+ }
566
+ function writeControlResponse(child, requestId, decision) {
567
+ try {
568
+ child.stdin?.write(`${JSON.stringify(buildControlResponse(requestId, decision))}
569
+ `);
570
+ } catch {
571
+ }
572
+ }
573
+ function deriveErrorMessage(sawResult, resultIsError, exitCode, stderr) {
574
+ if (sawResult && resultIsError) return "UR reported an error completing this turn.";
575
+ const trimmedStderr = stderr.trim();
576
+ if (trimmedStderr) return trimmedStderr;
577
+ return `UR exited with code ${exitCode ?? "unknown"} and produced no result.`;
578
+ }
579
+ function errorMessage2(error) {
580
+ return error instanceof Error ? error.message : String(error);
581
+ }
582
+
583
+ // src/context/ideContext.ts
584
+ var path2 = __toESM(require("node:path"));
585
+ function formatAttachmentLabel(attachment) {
586
+ if (attachment.kind === "file") return `@${attachment.file.path}`;
587
+ const { path: filePath, startLine, endLine } = attachment.selection;
588
+ return startLine === endLine ? `@${filePath}:${startLine}` : `@${filePath}:${startLine}-${endLine}`;
589
+ }
590
+ function formatAttachmentBlock(attachment) {
591
+ const label = formatAttachmentLabel(attachment);
592
+ if (attachment.kind === "file") return label;
593
+ const fence = languageIdToFence(attachment.selection.languageId);
594
+ return `${label}
595
+ \`\`\`${fence}
596
+ ${attachment.selection.text}
597
+ \`\`\``;
598
+ }
599
+ function buildPromptWithAttachments(prompt, attachments) {
600
+ if (attachments.length === 0) return prompt;
601
+ const blocks = attachments.map(formatAttachmentBlock).join("\n\n");
602
+ return `${blocks}
603
+
604
+ ${prompt}`;
605
+ }
606
+ function describeUnavailableReason(snapshot, kind) {
607
+ if (!snapshot.workspaceRoot) return "Open a workspace folder first.";
608
+ if (!snapshot.activeFile) return "No active editor.";
609
+ if (kind === "selection" && !snapshot.selection) return "No text selected.";
610
+ return null;
611
+ }
612
+ var FENCE_OVERRIDES = {
613
+ typescriptreact: "tsx",
614
+ javascriptreact: "jsx",
615
+ shellscript: "bash",
616
+ jsonc: "json",
617
+ plaintext: ""
618
+ };
619
+ function languageIdToFence(languageId) {
620
+ return FENCE_OVERRIDES[languageId] ?? languageId;
621
+ }
622
+ function captureEditorSnapshot() {
623
+ const vscode18 = require("vscode");
624
+ const workspaceRoot2 = vscode18.workspace.workspaceFolders?.[0]?.uri.fsPath;
625
+ const editor = vscode18.window.activeTextEditor;
626
+ if (!editor) return { workspaceRoot: workspaceRoot2 };
627
+ const absolutePath = editor.document.uri.fsPath;
628
+ const relativePath = workspaceRoot2 ? path2.relative(workspaceRoot2, absolutePath) : absolutePath;
629
+ const activeFile = { path: relativePath, languageId: editor.document.languageId };
630
+ const selection = editor.selection;
631
+ if (selection.isEmpty) return { workspaceRoot: workspaceRoot2, activeFile };
632
+ const text = editor.document.getText(selection);
633
+ const selectionSnapshot = {
634
+ path: relativePath,
635
+ languageId: editor.document.languageId,
636
+ startLine: selection.start.line + 1,
637
+ endLine: selection.end.line + 1,
638
+ text
639
+ };
640
+ return { workspaceRoot: workspaceRoot2, activeFile, selection: selectionSnapshot };
641
+ }
642
+
643
+ // src/sessions/sessionStore.ts
644
+ var import_node_crypto = require("node:crypto");
645
+ var fs2 = __toESM(require("node:fs"));
646
+ var path3 = __toESM(require("node:path"));
647
+ var SESSION_ID_PATTERN = /^[a-zA-Z0-9-]{1,128}$/;
648
+ var TITLE_MAX_LENGTH = 60;
649
+ var DEFAULT_TITLE = "New Chat";
650
+ function chatRoot(root) {
651
+ return path3.join(root, ".ur", "ide", "chat");
652
+ }
653
+ function manifestPath2(root) {
654
+ return path3.join(chatRoot(root), "manifest.json");
655
+ }
656
+ function isValidSessionId(id) {
657
+ return SESSION_ID_PATTERN.test(id);
658
+ }
659
+ function sessionFilePath(root, id) {
660
+ if (!isValidSessionId(id)) return null;
661
+ const sessionsDir = path3.join(chatRoot(root), "sessions");
662
+ const target = path3.join(sessionsDir, `${id}.json`);
663
+ const resolvedDir = path3.resolve(sessionsDir) + path3.sep;
664
+ const resolvedTarget = path3.resolve(target);
665
+ if (!resolvedTarget.startsWith(resolvedDir)) return null;
666
+ return target;
667
+ }
668
+ function readJson2(file, fallback) {
669
+ try {
670
+ return JSON.parse(fs2.readFileSync(file, "utf8"));
671
+ } catch {
672
+ return fallback;
673
+ }
674
+ }
675
+ function writeJson2(file, value) {
676
+ fs2.mkdirSync(path3.dirname(file), { recursive: true });
677
+ fs2.writeFileSync(file, `${JSON.stringify(value, null, 2)}
678
+ `);
679
+ }
680
+ function readManifest(root) {
681
+ const manifest = readJson2(manifestPath2(root), { version: 1, sessions: [] });
682
+ return Array.isArray(manifest.sessions) ? manifest : { version: 1, sessions: [] };
683
+ }
684
+ function writeManifest2(root, manifest) {
685
+ writeJson2(manifestPath2(root), manifest);
686
+ }
687
+ function upsertManifestEntry(root, session) {
688
+ const manifest = readManifest(root);
689
+ const index = manifest.sessions.findIndex((entry) => entry.id === session.id);
690
+ if (index === -1) {
691
+ manifest.sessions.push(session);
692
+ } else {
693
+ manifest.sessions[index] = session;
694
+ }
695
+ writeManifest2(root, manifest);
696
+ }
697
+ function createSession(root, options = {}) {
698
+ const now = (/* @__PURE__ */ new Date()).toISOString();
699
+ const session = {
700
+ id: (0, import_node_crypto.randomUUID)(),
701
+ title: options.title?.trim() || DEFAULT_TITLE,
702
+ workspaceRoot: root,
703
+ createdAt: now,
704
+ updatedAt: now
705
+ };
706
+ const record = { session, messages: [] };
707
+ const file = sessionFilePath(root, session.id);
708
+ if (!file) throw new Error(`Generated an invalid session id: ${session.id}`);
709
+ writeJson2(file, record);
710
+ upsertManifestEntry(root, session);
711
+ return record;
712
+ }
713
+ function listSessions(root, options = {}) {
714
+ const manifest = readManifest(root);
715
+ const sessions = options.includeArchived ? manifest.sessions : manifest.sessions.filter((s) => !s.archived);
716
+ return sessions.slice().sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
717
+ }
718
+ function readSession(root, id) {
719
+ const file = sessionFilePath(root, id);
720
+ if (!file || !fs2.existsSync(file)) return null;
721
+ return readJson2(file, null);
722
+ }
723
+ function appendMessage(root, id, message) {
724
+ const record = readSession(root, id);
725
+ if (!record) return null;
726
+ record.messages.push(message);
727
+ record.session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
728
+ if (record.session.title === DEFAULT_TITLE && message.role === "user") {
729
+ record.session.title = deriveTitle(message);
730
+ }
731
+ const file = sessionFilePath(root, id);
732
+ if (!file) return null;
733
+ writeJson2(file, record);
734
+ upsertManifestEntry(root, record.session);
735
+ return record;
736
+ }
737
+ function setCliSessionId(root, id, cliSessionId) {
738
+ const record = readSession(root, id);
739
+ if (!record) return null;
740
+ record.session.cliSessionId = cliSessionId;
741
+ record.session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
742
+ const file = sessionFilePath(root, id);
743
+ if (!file) return null;
744
+ writeJson2(file, record);
745
+ upsertManifestEntry(root, record.session);
746
+ return record;
747
+ }
748
+ function deriveTitle(message) {
749
+ const text = message.content.map((block) => block.type === "text" ? block.text : "").join(" ").trim().replace(/\s+/g, " ");
750
+ if (!text) return DEFAULT_TITLE;
751
+ return text.length > TITLE_MAX_LENGTH ? `${text.slice(0, TITLE_MAX_LENGTH - 1)}\u2026` : text;
752
+ }
753
+
754
+ // src/chat/chatPanel.ts
755
+ var vscode5 = __toESM(require("vscode"));
756
+ var ChatPanel = class _ChatPanel {
757
+ static current;
758
+ panel;
759
+ disposed = false;
760
+ disposables = [];
761
+ constructor(panel, onMessage) {
762
+ this.panel = panel;
763
+ this.panel.webview.html = renderChatHtml(this.panel.webview);
764
+ this.disposables.push(
765
+ this.panel.webview.onDidReceiveMessage((message) => onMessage(message)),
766
+ this.panel.onDidDispose(() => this.handleDispose())
767
+ );
768
+ }
769
+ static createOrShow(onMessage) {
770
+ if (_ChatPanel.current && !_ChatPanel.current.disposed) {
771
+ _ChatPanel.current.panel.reveal(vscode5.ViewColumn.Beside);
772
+ return _ChatPanel.current;
773
+ }
774
+ const panel = vscode5.window.createWebviewPanel("urChat", "UR Chat", vscode5.ViewColumn.Beside, {
775
+ enableScripts: true,
776
+ retainContextWhenHidden: true
777
+ });
778
+ const instance = new _ChatPanel(panel, onMessage);
779
+ _ChatPanel.current = instance;
780
+ return instance;
781
+ }
782
+ static get isOpen() {
783
+ return Boolean(_ChatPanel.current && !_ChatPanel.current.disposed);
784
+ }
785
+ post(message) {
786
+ if (this.disposed) return;
787
+ void this.panel.webview.postMessage(message);
788
+ }
789
+ handleDispose() {
790
+ this.disposed = true;
791
+ for (const disposable of this.disposables) disposable.dispose();
792
+ if (_ChatPanel.current === this) _ChatPanel.current = void 0;
793
+ }
794
+ };
795
+ function nonce() {
796
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
797
+ let value = "";
798
+ for (let i = 0; i < 32; i++) value += chars.charAt(Math.floor(Math.random() * chars.length));
799
+ return value;
800
+ }
801
+ function renderChatHtml(webview) {
802
+ const scriptNonce = nonce();
803
+ const csp = [
804
+ `default-src 'none'`,
805
+ `style-src ${webview.cspSource} 'unsafe-inline'`,
806
+ `script-src 'nonce-${scriptNonce}'`
807
+ ].join("; ");
808
+ return `<!doctype html>
809
+ <html lang="en">
810
+ <head>
811
+ <meta charset="utf-8">
812
+ <meta http-equiv="Content-Security-Policy" content="${csp}">
813
+ <meta name="viewport" content="width=device-width, initial-scale=1">
814
+ <style>
815
+ :root { color-scheme: light dark; }
816
+ * { box-sizing: border-box; }
817
+ body {
818
+ font: 13px/1.5 var(--vscode-font-family);
819
+ color: var(--vscode-foreground);
820
+ margin: 0;
821
+ display: flex;
822
+ flex-direction: column;
823
+ height: 100vh;
824
+ }
825
+ #banner {
826
+ display: none;
827
+ padding: 8px 14px;
828
+ background: var(--vscode-inputValidation-errorBackground);
829
+ border-bottom: 1px solid var(--vscode-inputValidation-errorBorder);
830
+ color: var(--vscode-inputValidation-errorForeground, var(--vscode-foreground));
831
+ }
832
+ #banner.visible { display: block; }
833
+ #messages { flex: 1; overflow-y: auto; padding: 14px; }
834
+ #empty-state { color: var(--vscode-descriptionForeground); padding: 24px 4px; }
835
+ #empty-state code { background: var(--vscode-textCodeBlock-background); padding: 1px 4px; border-radius: 3px; }
836
+ .message { margin-bottom: 14px; }
837
+ .message .role {
838
+ font-size: 11px;
839
+ text-transform: uppercase;
840
+ letter-spacing: 0.04em;
841
+ color: var(--vscode-descriptionForeground);
842
+ margin-bottom: 4px;
843
+ }
844
+ .message.user .bubble { background: var(--vscode-input-background); border: 1px solid var(--vscode-input-border, var(--vscode-panel-border)); }
845
+ .message.assistant .bubble { background: var(--vscode-editor-background); border: 1px solid var(--vscode-panel-border); }
846
+ .message.status .bubble { background: transparent; border: 1px dashed var(--vscode-panel-border); color: var(--vscode-descriptionForeground); font-style: italic; }
847
+ .bubble { border-radius: 6px; padding: 10px 12px; white-space: pre-wrap; word-break: break-word; }
848
+ .tool-block {
849
+ margin-top: 8px;
850
+ border: 1px solid var(--vscode-panel-border);
851
+ border-radius: 6px;
852
+ padding: 8px 10px;
853
+ background: var(--vscode-textCodeBlock-background);
854
+ font-family: var(--vscode-editor-font-family);
855
+ font-size: 12px;
856
+ }
857
+ .tool-block .tool-name { font-weight: 600; }
858
+ .tool-block.result.ok { border-left: 3px solid var(--vscode-testing-iconPassed, #2ea043); }
859
+ .tool-block.result.fail { border-left: 3px solid var(--vscode-testing-iconFailed, #f14c4c); }
860
+ #permission-prompt {
861
+ display: none;
862
+ margin: 0 14px 14px;
863
+ padding: 12px;
864
+ border: 1px solid var(--vscode-inputValidation-warningBorder, var(--vscode-panel-border));
865
+ background: var(--vscode-inputValidation-warningBackground, var(--vscode-sideBar-background));
866
+ border-radius: 6px;
867
+ }
868
+ #permission-prompt.visible { display: block; }
869
+ #permission-prompt .input-preview {
870
+ font-family: var(--vscode-editor-font-family);
871
+ font-size: 12px;
872
+ background: var(--vscode-textCodeBlock-background);
873
+ padding: 6px 8px;
874
+ border-radius: 4px;
875
+ margin: 6px 0;
876
+ max-height: 120px;
877
+ overflow: auto;
878
+ white-space: pre-wrap;
879
+ }
880
+ #permission-prompt .actions { display: flex; gap: 8px; margin-top: 8px; }
881
+ #attachments { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px; }
882
+ .attachment-chip {
883
+ display: inline-flex;
884
+ align-items: center;
885
+ gap: 6px;
886
+ background: var(--vscode-badge-background);
887
+ color: var(--vscode-badge-foreground);
888
+ border-radius: 12px;
889
+ padding: 2px 10px;
890
+ font-size: 12px;
891
+ }
892
+ .attachment-chip button { border: none; background: none; color: inherit; cursor: pointer; font-size: 13px; line-height: 1; padding: 0; }
893
+ #status-line { padding: 4px 14px; font-size: 12px; color: var(--vscode-descriptionForeground); min-height: 18px; }
894
+ #composer { display: flex; gap: 8px; padding: 10px 14px 14px; border-top: 1px solid var(--vscode-panel-border); }
895
+ #input {
896
+ flex: 1;
897
+ resize: none;
898
+ min-height: 36px;
899
+ max-height: 160px;
900
+ font: inherit;
901
+ color: var(--vscode-input-foreground);
902
+ background: var(--vscode-input-background);
903
+ border: 1px solid var(--vscode-input-border, var(--vscode-panel-border));
904
+ border-radius: 4px;
905
+ padding: 8px;
906
+ }
907
+ button {
908
+ font: inherit;
909
+ border: none;
910
+ border-radius: 4px;
911
+ padding: 8px 14px;
912
+ cursor: pointer;
913
+ background: var(--vscode-button-background);
914
+ color: var(--vscode-button-foreground);
915
+ }
916
+ button:hover { background: var(--vscode-button-hoverBackground); }
917
+ button.secondary { background: var(--vscode-button-secondaryBackground); color: var(--vscode-button-secondaryForeground); }
918
+ button:disabled { opacity: 0.5; cursor: default; }
919
+ </style>
920
+ </head>
921
+ <body>
922
+ <div id="banner"></div>
923
+ <div id="messages">
924
+ <div id="empty-state">Ask UR about this workspace. Use <code>UR: Add Selection to Chat</code> or <code>UR: Add Current File to Chat</code> to attach code first.</div>
925
+ </div>
926
+ <div id="permission-prompt">
927
+ <div><strong>UR wants to use <span id="permission-tool"></span></strong></div>
928
+ <div class="input-preview" id="permission-input"></div>
929
+ <div class="actions">
930
+ <button id="permission-allow">Allow</button>
931
+ <button id="permission-deny" class="secondary">Deny</button>
932
+ </div>
933
+ </div>
934
+ <div id="attachments"></div>
935
+ <div id="status-line"></div>
936
+ <form id="composer">
937
+ <textarea id="input" placeholder="Message UR\u2026" rows="2"></textarea>
938
+ <button id="send" type="submit">Send</button>
939
+ <button id="cancel" type="button" class="secondary" hidden>Cancel</button>
940
+ </form>
941
+ <script nonce="${scriptNonce}">
942
+ (function () {
943
+ const vscode = acquireVsCodeApi();
944
+ const messagesEl = document.getElementById('messages');
945
+ const emptyStateEl = document.getElementById('empty-state');
946
+ const bannerEl = document.getElementById('banner');
947
+ const statusLineEl = document.getElementById('status-line');
948
+ const attachmentsEl = document.getElementById('attachments');
949
+ const permissionEl = document.getElementById('permission-prompt');
950
+ const permissionToolEl = document.getElementById('permission-tool');
951
+ const permissionInputEl = document.getElementById('permission-input');
952
+ const composerEl = document.getElementById('composer');
953
+ const inputEl = document.getElementById('input');
954
+ const sendButton = document.getElementById('send');
955
+ const cancelButton = document.getElementById('cancel');
956
+
957
+ let currentStatus = 'idle';
958
+ let pendingPermissionRequestId = null;
959
+
960
+ function escapeHtml(text) {
961
+ return String(text)
962
+ .replace(/&/g, '&amp;')
963
+ .replace(/</g, '&lt;')
964
+ .replace(/>/g, '&gt;')
965
+ .replace(/"/g, '&quot;');
966
+ }
967
+
968
+ function renderContentBlock(block) {
969
+ if (block.type === 'text') {
970
+ return '<div>' + escapeHtml(block.text) + '</div>';
971
+ }
972
+ if (block.type === 'tool_use') {
973
+ return '<div class="tool-block use"><span class="tool-name">' + escapeHtml(block.name) + '</span><div>' + escapeHtml(JSON.stringify(block.input, null, 2)) + '</div></div>';
974
+ }
975
+ if (block.type === 'tool_result') {
976
+ const cls = block.ok ? 'ok' : 'fail';
977
+ return '<div class="tool-block result ' + cls + '"><span class="tool-name">' + (block.ok ? 'Tool result' : 'Tool failed') + '</span><div>' + escapeHtml(block.summary) + '</div></div>';
978
+ }
979
+ if (block.type === 'permission_request') {
980
+ const resolved = block.resolved ? ' \u2014 ' + block.resolved : ' \u2014 pending';
981
+ return '<div class="tool-block"><span class="tool-name">Permission: ' + escapeHtml(block.toolName) + '</span>' + escapeHtml(resolved) + '</div>';
982
+ }
983
+ return '';
984
+ }
985
+
986
+ function appendMessageEl(message) {
987
+ emptyStateEl.style.display = 'none';
988
+ const wrapper = document.createElement('div');
989
+ wrapper.className = 'message ' + message.role;
990
+ const roleLabel = document.createElement('div');
991
+ roleLabel.className = 'role';
992
+ roleLabel.textContent = message.role;
993
+ const bubble = document.createElement('div');
994
+ bubble.className = 'bubble';
995
+ bubble.innerHTML = message.content.map(renderContentBlock).join('');
996
+ wrapper.appendChild(roleLabel);
997
+ wrapper.appendChild(bubble);
998
+ messagesEl.appendChild(wrapper);
999
+ messagesEl.scrollTop = messagesEl.scrollHeight;
1000
+ }
1001
+
1002
+ function renderAll(messages) {
1003
+ messagesEl.innerHTML = '';
1004
+ if (messages.length === 0) {
1005
+ messagesEl.appendChild(emptyStateEl);
1006
+ emptyStateEl.style.display = 'block';
1007
+ return;
1008
+ }
1009
+ for (const message of messages) appendMessageEl(message);
1010
+ }
1011
+
1012
+ function renderAttachments(attachments) {
1013
+ attachmentsEl.innerHTML = '';
1014
+ attachments.forEach(function (attachment, index) {
1015
+ const chip = document.createElement('span');
1016
+ chip.className = 'attachment-chip';
1017
+ const label = document.createElement('span');
1018
+ label.textContent = attachment.label;
1019
+ const remove = document.createElement('button');
1020
+ remove.type = 'button';
1021
+ remove.textContent = '\\u00d7';
1022
+ remove.addEventListener('click', function () {
1023
+ vscode.postMessage({ type: 'removeAttachment', index: index });
1024
+ });
1025
+ chip.appendChild(label);
1026
+ chip.appendChild(remove);
1027
+ attachmentsEl.appendChild(chip);
1028
+ });
1029
+ }
1030
+
1031
+ function applyStatus(status) {
1032
+ currentStatus = status;
1033
+ const running = status === 'running';
1034
+ sendButton.disabled = running;
1035
+ cancelButton.hidden = !running;
1036
+ if (status === 'idle') statusLineEl.textContent = '';
1037
+ else if (status === 'running') statusLineEl.textContent = 'Running\u2026';
1038
+ else if (status === 'canceled') statusLineEl.textContent = 'Canceled.';
1039
+ else if (status === 'error') statusLineEl.textContent = 'UR reported an error. See above.';
1040
+ }
1041
+
1042
+ function showBanner(message) {
1043
+ bannerEl.textContent = message;
1044
+ bannerEl.classList.add('visible');
1045
+ }
1046
+
1047
+ window.addEventListener('message', function (event) {
1048
+ const message = event.data;
1049
+ if (message.type === 'init') {
1050
+ renderAll(message.messages);
1051
+ renderAttachments(message.attachments);
1052
+ applyStatus(message.status);
1053
+ } else if (message.type === 'messageAppended') {
1054
+ appendMessageEl(message.message);
1055
+ } else if (message.type === 'statusChanged') {
1056
+ applyStatus(message.status);
1057
+ } else if (message.type === 'permissionRequest') {
1058
+ pendingPermissionRequestId = message.requestId;
1059
+ permissionToolEl.textContent = message.toolName;
1060
+ permissionInputEl.textContent = JSON.stringify(message.input, null, 2);
1061
+ permissionEl.classList.add('visible');
1062
+ } else if (message.type === 'permissionResolved') {
1063
+ if (pendingPermissionRequestId === message.requestId) {
1064
+ pendingPermissionRequestId = null;
1065
+ permissionEl.classList.remove('visible');
1066
+ }
1067
+ } else if (message.type === 'attachmentsChanged') {
1068
+ renderAttachments(message.attachments);
1069
+ } else if (message.type === 'errorBanner') {
1070
+ showBanner(message.message);
1071
+ } else if (message.type === 'sessionRenamed') {
1072
+ document.title = message.title;
1073
+ }
1074
+ });
1075
+
1076
+ composerEl.addEventListener('submit', function (event) {
1077
+ event.preventDefault();
1078
+ const text = inputEl.value.trim();
1079
+ if (!text || currentStatus === 'running') return;
1080
+ vscode.postMessage({ type: 'send', text: text });
1081
+ inputEl.value = '';
1082
+ });
1083
+
1084
+ inputEl.addEventListener('keydown', function (event) {
1085
+ if (event.key === 'Enter' && !event.shiftKey) {
1086
+ event.preventDefault();
1087
+ composerEl.requestSubmit();
1088
+ }
1089
+ });
1090
+
1091
+ cancelButton.addEventListener('click', function () {
1092
+ vscode.postMessage({ type: 'cancel' });
1093
+ });
1094
+
1095
+ document.getElementById('permission-allow').addEventListener('click', function () {
1096
+ if (!pendingPermissionRequestId) return;
1097
+ vscode.postMessage({ type: 'permissionDecision', requestId: pendingPermissionRequestId, decision: 'allow' });
1098
+ });
1099
+ document.getElementById('permission-deny').addEventListener('click', function () {
1100
+ if (!pendingPermissionRequestId) return;
1101
+ vscode.postMessage({ type: 'permissionDecision', requestId: pendingPermissionRequestId, decision: 'deny' });
1102
+ });
1103
+
1104
+ vscode.postMessage({ type: 'ready' });
1105
+ })();
1106
+ </script>
1107
+ </body>
1108
+ </html>`;
1109
+ }
1110
+ function toWireAttachment(attachment) {
1111
+ return { label: formatAttachmentLabel(attachment) };
1112
+ }
1113
+
1114
+ // src/chat/messageMapping.ts
1115
+ function extractAssistantContentBlocks(message) {
1116
+ const content = message?.message?.content;
1117
+ if (!Array.isArray(content)) return [];
1118
+ const blocks = [];
1119
+ for (const block of content) {
1120
+ if (!block || typeof block !== "object") continue;
1121
+ const typed = block;
1122
+ if (typed.type === "text" && typeof typed.text === "string") {
1123
+ blocks.push({ type: "text", text: typed.text });
1124
+ } else if (typed.type === "tool_use" && typeof typed.id === "string" && typeof typed.name === "string") {
1125
+ blocks.push({ type: "tool_use", id: typed.id, name: typed.name, input: typed.input });
1126
+ }
1127
+ }
1128
+ return blocks;
1129
+ }
1130
+ function extractToolResultContentBlocks(message) {
1131
+ const content = message?.message?.content;
1132
+ if (!Array.isArray(content)) return [];
1133
+ const blocks = [];
1134
+ for (const block of content) {
1135
+ if (!block || typeof block !== "object") continue;
1136
+ const typed = block;
1137
+ if (typed.type === "tool_result" && typeof typed.tool_use_id === "string") {
1138
+ blocks.push({
1139
+ type: "tool_result",
1140
+ toolUseId: typed.tool_use_id,
1141
+ ok: typed.is_error !== true,
1142
+ summary: summarizeToolResultContent(typed.content)
1143
+ });
1144
+ }
1145
+ }
1146
+ return blocks;
1147
+ }
1148
+ function summarizeToolResultContent(content, max = 800) {
1149
+ if (typeof content === "string") return truncate(content, max);
1150
+ if (Array.isArray(content)) {
1151
+ const text = content.map((block) => block && typeof block === "object" && "text" in block ? String(block.text) : "").filter(Boolean).join("\n");
1152
+ return truncate(text || JSON.stringify(content), max);
1153
+ }
1154
+ return truncate(JSON.stringify(content ?? ""), max);
1155
+ }
1156
+ function truncate(text, max) {
1157
+ return text.length > max ? `${text.slice(0, max)}\u2026` : text;
1158
+ }
1159
+
1160
+ // src/chat/prompts.ts
1161
+ function selectionAttachment(selection) {
1162
+ return { kind: "selection", selection };
1163
+ }
1164
+ function buildExplainPrompt(selection) {
1165
+ return buildPromptWithAttachments(
1166
+ "Explain what this code does, step by step. Call out any non-obvious behavior, edge cases, or assumptions it makes.",
1167
+ [selectionAttachment(selection)]
1168
+ );
1169
+ }
1170
+ function buildFixPrompt(selection) {
1171
+ return buildPromptWithAttachments(
1172
+ "Find and fix any bugs in this code. Explain what was wrong and what you changed.",
1173
+ [selectionAttachment(selection)]
1174
+ );
1175
+ }
1176
+ function buildGenerateTestsPrompt(selection) {
1177
+ return buildPromptWithAttachments(
1178
+ "Write tests for this code, covering the main behavior and realistic edge cases. Match the existing test style and framework used in this project if you can tell what it is.",
1179
+ [selectionAttachment(selection)]
1180
+ );
1181
+ }
1182
+ function buildRunSpecPrompt() {
1183
+ return "List the specs in this project (.ur/specs, via `ur spec list`) and help me run the next pending task. If none exist yet, help me scaffold one with `ur spec init`.";
1184
+ }
1185
+ function buildRunWorkflowPrompt() {
1186
+ return "List the workflows available in this project (`ur workflow list`) and help me run the appropriate one for my current task.";
1187
+ }
1188
+
1189
+ // src/chat/chatController.ts
1190
+ var ChatController = class {
1191
+ _onDidChangeState = new vscode6.EventEmitter();
1192
+ onDidChangeState = this._onDidChangeState.event;
1193
+ panel;
1194
+ record;
1195
+ attachments = [];
1196
+ status = "idle";
1197
+ turnHandle;
1198
+ pendingPermissions = /* @__PURE__ */ new Map();
1199
+ // --- commands ---
1200
+ async newChat() {
1201
+ const root = this.requireWorkspaceRoot();
1202
+ if (!root) return;
1203
+ this.turnHandle?.cancel();
1204
+ this.record = createSession(root);
1205
+ this.attachments = [];
1206
+ this.status = "idle";
1207
+ this._onDidChangeState.fire();
1208
+ this.ensurePanel();
1209
+ this.syncFullState();
1210
+ }
1211
+ async openChat() {
1212
+ const root = this.requireWorkspaceRoot();
1213
+ if (!root) return;
1214
+ if (this.record) {
1215
+ this.ensurePanel();
1216
+ this.syncFullState();
1217
+ return;
1218
+ }
1219
+ const sessions = listSessions(root);
1220
+ if (sessions.length === 0) {
1221
+ await this.newChat();
1222
+ return;
1223
+ }
1224
+ const picked = await this.pickSession(sessions);
1225
+ if (picked === void 0) return;
1226
+ if (picked === "new") {
1227
+ await this.newChat();
1228
+ return;
1229
+ }
1230
+ const record = readSession(root, picked);
1231
+ if (!record) {
1232
+ await this.newChat();
1233
+ return;
1234
+ }
1235
+ this.record = record;
1236
+ this.ensurePanel();
1237
+ this.syncFullState();
1238
+ }
1239
+ cancelCurrentRequest() {
1240
+ if (!this.turnHandle || this.status !== "running") {
1241
+ vscode6.window.showInformationMessage("No UR chat request is currently running.");
1242
+ return;
1243
+ }
1244
+ this.turnHandle.cancel();
1245
+ this.denyAllPending("Request was canceled.");
1246
+ }
1247
+ addCurrentFileToChat() {
1248
+ this.stageAttachment("file");
1249
+ }
1250
+ addSelectionToChat() {
1251
+ this.stageAttachment("selection");
1252
+ }
1253
+ isRequestRunning() {
1254
+ return this.status === "running";
1255
+ }
1256
+ async explainSelection() {
1257
+ await this.runEditorAction(buildExplainPrompt);
1258
+ }
1259
+ async fixSelection() {
1260
+ await this.runEditorAction(buildFixPrompt);
1261
+ }
1262
+ async generateTestsForSelection() {
1263
+ await this.runEditorAction(buildGenerateTestsPrompt);
1264
+ }
1265
+ async sendMessage(text) {
1266
+ const trimmed = text.trim();
1267
+ if (!trimmed) return;
1268
+ const prompt = buildPromptWithAttachments(trimmed, this.attachments);
1269
+ this.attachments = [];
1270
+ this.panel?.post({ type: "attachmentsChanged", attachments: [] });
1271
+ await this.dispatchTurn(prompt);
1272
+ }
1273
+ /** Opens chat and runs a fully-formed prompt through the same pathway as a
1274
+ * manual send — used by Review Current Diff and Run Verifier so neither
1275
+ * command invents a second way to talk to UR. */
1276
+ async runStructuredPrompt(promptText) {
1277
+ await this.dispatchTurn(promptText);
1278
+ }
1279
+ dispose() {
1280
+ this.turnHandle?.cancel();
1281
+ this.denyAllPending("Extension is shutting down.");
1282
+ this._onDidChangeState.dispose();
1283
+ }
1284
+ // --- internals ---
1285
+ requireWorkspaceRoot() {
1286
+ const root = workspaceRoot();
1287
+ if (!root) {
1288
+ vscode6.window.showWarningMessage("Open a workspace folder to use UR Chat.");
1289
+ return void 0;
1290
+ }
1291
+ return root;
1292
+ }
1293
+ ensurePanel() {
1294
+ if (!ChatPanel.isOpen) {
1295
+ this.panel = ChatPanel.createOrShow((message) => this.handleWebviewMessage(message));
1296
+ }
1297
+ }
1298
+ async pickSession(sessions) {
1299
+ const items = [
1300
+ { id: "new", label: "$(add) Start New Chat" },
1301
+ ...sessions.map(
1302
+ (session) => ({
1303
+ id: session.id,
1304
+ label: session.title,
1305
+ description: new Date(session.updatedAt).toLocaleString()
1306
+ })
1307
+ )
1308
+ ];
1309
+ const picked = await vscode6.window.showQuickPick(items, {
1310
+ title: "UR Chat",
1311
+ placeHolder: "Resume a chat or start a new one"
1312
+ });
1313
+ return picked?.id;
1314
+ }
1315
+ stageAttachment(kind) {
1316
+ const snapshot = captureEditorSnapshot();
1317
+ const reason = describeUnavailableReason(snapshot, kind);
1318
+ if (reason) {
1319
+ vscode6.window.showWarningMessage(reason);
1320
+ return;
1321
+ }
1322
+ const attachment = kind === "file" ? { kind: "file", file: snapshot.activeFile } : { kind: "selection", selection: snapshot.selection };
1323
+ this.attachments.push(attachment);
1324
+ this.ensurePanel();
1325
+ this.panel?.post({ type: "attachmentsChanged", attachments: this.attachments.map(toWireAttachment) });
1326
+ }
1327
+ async runEditorAction(build) {
1328
+ const root = this.requireWorkspaceRoot();
1329
+ if (!root) return;
1330
+ const snapshot = captureEditorSnapshot();
1331
+ const reason = describeUnavailableReason(snapshot, "selection");
1332
+ if (reason) {
1333
+ vscode6.window.showWarningMessage(reason);
1334
+ return;
1335
+ }
1336
+ await this.dispatchTurn(build(snapshot.selection));
1337
+ }
1338
+ /** The single pathway every turn goes through — manual sends and editor
1339
+ * actions alike. */
1340
+ async dispatchTurn(promptText) {
1341
+ const root = this.requireWorkspaceRoot();
1342
+ if (!root) return;
1343
+ if (this.status === "running") {
1344
+ vscode6.window.showWarningMessage("UR is already running a request. Cancel it first or wait for it to finish.");
1345
+ return;
1346
+ }
1347
+ if (!this.record) this.record = createSession(root);
1348
+ this.ensurePanel();
1349
+ const sessionId = this.record.session.id;
1350
+ const userMessage = {
1351
+ id: (0, import_node_crypto2.randomUUID)(),
1352
+ sessionId,
1353
+ role: "user",
1354
+ content: [{ type: "text", text: promptText }],
1355
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1356
+ };
1357
+ appendMessage(root, sessionId, userMessage);
1358
+ this.record.messages.push(userMessage);
1359
+ this.panel?.post({ type: "messageAppended", message: userMessage });
1360
+ this.status = "running";
1361
+ this._onDidChangeState.fire();
1362
+ this.panel?.post({ type: "statusChanged", status: this.status });
1363
+ const resumeSessionId = this.record.session.cliSessionId;
1364
+ this.turnHandle = runUrTurn(
1365
+ { cwd: root, prompt: promptText, resumeSessionId },
1366
+ {
1367
+ onMessage: (message) => this.handleStreamMessage(root, sessionId, message),
1368
+ onControlRequest: (request) => this.handlePermissionRequest(request),
1369
+ onExit: (result) => this.handleTurnExit(root, result)
1370
+ }
1371
+ );
1372
+ }
1373
+ handleStreamMessage(root, sessionId, message) {
1374
+ if (message.type === "system" && message.subtype === "init" && typeof message.session_id === "string") {
1375
+ if (this.record && this.record.session.id === sessionId && !this.record.session.cliSessionId) {
1376
+ setCliSessionId(root, sessionId, message.session_id);
1377
+ this.record.session.cliSessionId = message.session_id;
1378
+ }
1379
+ return;
1380
+ }
1381
+ if (message.type === "assistant") {
1382
+ const blocks = extractAssistantContentBlocks(message);
1383
+ if (blocks.length > 0) this.appendChatMessage(root, sessionId, "assistant", blocks);
1384
+ return;
1385
+ }
1386
+ if (message.type === "user") {
1387
+ const blocks = extractToolResultContentBlocks(message);
1388
+ if (blocks.length > 0) this.appendChatMessage(root, sessionId, "status", blocks);
1389
+ return;
1390
+ }
1391
+ if (isControlCancelRequest(message)) {
1392
+ const pending = this.pendingPermissions.get(message.request_id);
1393
+ if (pending) {
1394
+ this.pendingPermissions.delete(message.request_id);
1395
+ pending.resolve({ behavior: "deny", message: "Permission request was canceled." });
1396
+ }
1397
+ this.panel?.post({ type: "permissionResolved", requestId: message.request_id });
1398
+ }
1399
+ }
1400
+ appendChatMessage(root, sessionId, role, content) {
1401
+ if (!this.record || this.record.session.id !== sessionId) return;
1402
+ const message = { id: (0, import_node_crypto2.randomUUID)(), sessionId, role, content, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
1403
+ appendMessage(root, sessionId, message);
1404
+ this.record.messages.push(message);
1405
+ this.panel?.post({ type: "messageAppended", message });
1406
+ }
1407
+ appendStatusText(root, text) {
1408
+ if (!this.record) return;
1409
+ this.appendChatMessage(root, this.record.session.id, "status", [{ type: "text", text }]);
1410
+ }
1411
+ handlePermissionRequest(request) {
1412
+ return new Promise((resolve2) => {
1413
+ const toolName = request.request.tool_name ?? "tool";
1414
+ const input = request.request.input ?? {};
1415
+ this.pendingPermissions.set(request.request_id, { resolve: resolve2, toolName, input });
1416
+ this.panel?.post({ type: "permissionRequest", requestId: request.request_id, toolName, input });
1417
+ });
1418
+ }
1419
+ handleTurnExit(root, result) {
1420
+ this.turnHandle = void 0;
1421
+ this.denyAllPending("The chat turn ended before this request was answered.");
1422
+ if (result.canceled) {
1423
+ this.status = "canceled";
1424
+ this.appendStatusText(root, "Canceled.");
1425
+ } else if (!result.ok) {
1426
+ this.status = "error";
1427
+ const message = result.error ?? "UR failed to complete this turn.";
1428
+ this.appendStatusText(root, `Error: ${message}`);
1429
+ this.panel?.post({ type: "errorBanner", message });
1430
+ } else {
1431
+ this.status = "idle";
1432
+ }
1433
+ this._onDidChangeState.fire();
1434
+ this.panel?.post({ type: "statusChanged", status: this.status });
1435
+ }
1436
+ resolvePermission(requestId, decision) {
1437
+ const pending = this.pendingPermissions.get(requestId);
1438
+ if (!pending) return;
1439
+ this.pendingPermissions.delete(requestId);
1440
+ pending.resolve(
1441
+ decision === "allow" ? { behavior: "allow", updatedInput: pending.input } : { behavior: "deny", message: "User denied this tool call from the UR Chat panel." }
1442
+ );
1443
+ this.panel?.post({ type: "permissionResolved", requestId });
1444
+ const root = workspaceRoot();
1445
+ if (root) this.appendStatusText(root, `${decision === "allow" ? "Allowed" : "Denied"} ${pending.toolName}.`);
1446
+ }
1447
+ denyAllPending(reason) {
1448
+ for (const [requestId, pending] of this.pendingPermissions) {
1449
+ pending.resolve({ behavior: "deny", message: reason });
1450
+ this.panel?.post({ type: "permissionResolved", requestId });
1451
+ }
1452
+ this.pendingPermissions.clear();
1453
+ }
1454
+ handleWebviewMessage(message) {
1455
+ if (message.type === "ready") {
1456
+ this.syncFullState();
1457
+ return;
1458
+ }
1459
+ if (message.type === "send") {
1460
+ void this.sendMessage(message.text);
1461
+ return;
1462
+ }
1463
+ if (message.type === "cancel") {
1464
+ this.cancelCurrentRequest();
1465
+ return;
1466
+ }
1467
+ if (message.type === "permissionDecision") {
1468
+ this.resolvePermission(message.requestId, message.decision);
1469
+ return;
1470
+ }
1471
+ if (message.type === "removeAttachment") {
1472
+ this.attachments.splice(message.index, 1);
1473
+ this.panel?.post({ type: "attachmentsChanged", attachments: this.attachments.map(toWireAttachment) });
1474
+ }
1475
+ }
1476
+ syncFullState() {
1477
+ if (!this.record) return;
1478
+ this.ensurePanel();
1479
+ this.panel?.post({
1480
+ type: "init",
1481
+ session: this.record.session,
1482
+ messages: this.record.messages,
1483
+ status: this.status,
1484
+ attachments: this.attachments.map(toWireAttachment)
1485
+ });
1486
+ }
1487
+ };
1488
+
1489
+ // src/chat/chatTreeProvider.ts
1490
+ var vscode7 = __toESM(require("vscode"));
1491
+ var ActionItem = class extends vscode7.TreeItem {
1492
+ constructor(label, description, icon, command, tooltip) {
1493
+ super(label, vscode7.TreeItemCollapsibleState.None);
1494
+ this.contextValue = "urChatAction";
1495
+ this.description = description;
1496
+ this.iconPath = new vscode7.ThemeIcon(icon);
1497
+ this.tooltip = tooltip ?? `${label}${description ? ` - ${description}` : ""}`;
1498
+ this.command = command;
1499
+ }
1500
+ };
1501
+ var InfoItem3 = class extends vscode7.TreeItem {
1502
+ constructor(label, description, icon = "comment-discussion") {
1503
+ super(label, vscode7.TreeItemCollapsibleState.None);
1504
+ this.contextValue = "urInfo";
1505
+ this.description = description;
1506
+ this.iconPath = new vscode7.ThemeIcon(icon);
1507
+ this.tooltip = `${label}${description ? ` - ${description}` : ""}`;
1508
+ }
1509
+ };
1510
+ var ChatTreeProvider = class {
1511
+ constructor(chat) {
1512
+ this.chat = chat;
1513
+ }
1514
+ chat;
1515
+ _onDidChangeTreeData = new vscode7.EventEmitter();
1516
+ onDidChangeTreeData = this._onDidChangeTreeData.event;
1517
+ refresh() {
1518
+ this._onDidChangeTreeData.fire();
1519
+ }
1520
+ getTreeItem(item) {
1521
+ return item;
1522
+ }
1523
+ getChildren() {
1524
+ const snapshot = captureEditorSnapshot();
1525
+ const items = [
1526
+ new InfoItem3("Start a UR chat", "Ask about this workspace or attach editor context."),
1527
+ new ActionItem("New Chat", "Start a fresh UR session", "comment-add", {
1528
+ command: "urInlineDiffs.chat.new",
1529
+ title: "New Chat"
1530
+ }),
1531
+ new ActionItem("Open Chat", "Resume an existing session", "comment", {
1532
+ command: "urInlineDiffs.chat.open",
1533
+ title: "Open Chat"
1534
+ }),
1535
+ new ActionItem("Pick Model", "Choose provider and model", "symbol-variable", {
1536
+ command: "urInlineDiffs.pickModel",
1537
+ title: "Pick Model"
1538
+ })
1539
+ ];
1540
+ if (snapshot.activeFile) {
1541
+ items.push(
1542
+ new ActionItem("Add Current File to Chat", snapshot.activeFile.path, "file-add", {
1543
+ command: "urInlineDiffs.chat.addFile",
1544
+ title: "Add Current File to Chat"
1545
+ })
1546
+ );
1547
+ }
1548
+ if (snapshot.selection) {
1549
+ const lineRange = snapshot.selection.startLine === snapshot.selection.endLine ? `${snapshot.selection.path}:${snapshot.selection.startLine}` : `${snapshot.selection.path}:${snapshot.selection.startLine}-${snapshot.selection.endLine}`;
1550
+ items.push(
1551
+ new ActionItem("Add Selection to Chat", lineRange, "selection", {
1552
+ command: "urInlineDiffs.chat.addSelection",
1553
+ title: "Add Selection to Chat"
1554
+ })
1555
+ );
1556
+ }
1557
+ if (this.chat.isRequestRunning()) {
1558
+ items.push(
1559
+ new ActionItem("Cancel Current Chat Request", "Stop the running request", "stop-circle", {
1560
+ command: "urInlineDiffs.chat.cancel",
1561
+ title: "Cancel Current Chat Request"
1562
+ })
1563
+ );
1564
+ }
1565
+ return items;
1566
+ }
1567
+ };
1568
+
1569
+ // src/diffs/actions.ts
1570
+ var import_node_child_process3 = require("node:child_process");
1571
+ var fs3 = __toESM(require("node:fs"));
1572
+ var import_node_util2 = require("node:util");
1573
+ var vscode8 = __toESM(require("vscode"));
1574
+ var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process3.execFile);
1575
+ async function commentDiff(item, provider) {
1576
+ const root = workspaceRoot();
1577
+ const bundle = item?.bundle;
1578
+ if (!root || !bundle) {
1579
+ vscode8.window.showWarningMessage("No UR inline diff selected.");
1580
+ return;
1581
+ }
1582
+ const text = await vscode8.window.showInputBox({
1583
+ title: `Comment on ${bundle.id}`,
1584
+ prompt: "Comment text",
1585
+ ignoreFocusOut: true
1586
+ });
1587
+ if (!text?.trim()) return;
1588
+ const manifest = loadManifest(root);
1589
+ const manifestBundle = manifest.diffs.find((diff) => diff.id === bundle.id);
1590
+ if (!manifestBundle) {
1591
+ vscode8.window.showErrorMessage(`UR inline diff not found: ${bundle.id}`);
1592
+ return;
1593
+ }
1594
+ const at = (/* @__PURE__ */ new Date()).toISOString();
1595
+ manifestBundle.status = "commented";
1596
+ manifestBundle.updatedAt = at;
1597
+ manifestBundle.comments = [...manifestBundle.comments ?? [], { at, text: text.trim() }];
1598
+ writeManifest(root, manifest);
1599
+ writeBundleMetadata(root, manifestBundle);
1600
+ provider.refresh();
1601
+ vscode8.window.showInformationMessage(`Added UR comment to ${bundle.id}.`);
1602
+ }
1603
+ async function applyDiff(item, provider) {
1604
+ const root = workspaceRoot();
1605
+ const bundle = item?.bundle;
1606
+ if (!root || !bundle) {
1607
+ vscode8.window.showWarningMessage("No UR inline diff selected.");
1608
+ return;
1609
+ }
1610
+ const patch = patchPath(root, bundle);
1611
+ if (!fs3.existsSync(patch)) {
1612
+ vscode8.window.showErrorMessage(`UR patch file missing for ${bundle.id}.`);
1613
+ return;
1614
+ }
1615
+ const choice = await vscode8.window.showWarningMessage(
1616
+ `Apply UR patch ${bundle.id} to your working tree? This modifies ${bundle.files?.length ?? 0} file(s).`,
1617
+ { modal: true },
1618
+ "Apply"
1619
+ );
1620
+ if (choice !== "Apply") return;
1621
+ try {
1622
+ await execFileAsync2("git", ["apply", "--whitespace=nowarn", patch], { cwd: root, shell: false });
1623
+ } catch (error) {
1624
+ vscode8.window.showErrorMessage(`Failed to apply UR patch ${bundle.id}: ${processErrorMessage(error)}`);
1625
+ return;
1626
+ }
1627
+ try {
1628
+ const { stdout } = await runUrCli(["ide", "diff", "approve", bundle.id], { cwd: root });
1629
+ provider.refresh();
1630
+ if (isNotFoundResult(stdout)) {
1631
+ vscode8.window.showWarningMessage(
1632
+ `Applied ${bundle.id} to disk, but no matching diff record was found to mark it approved.`
1633
+ );
1634
+ return;
1635
+ }
1636
+ vscode8.window.showInformationMessage(`Applied UR patch ${bundle.id}.`);
1637
+ } catch (error) {
1638
+ vscode8.window.showErrorMessage(
1639
+ `Applied ${bundle.id} to disk, but failed to record approval: ${errorMessage(error)}`
1640
+ );
1641
+ }
1642
+ }
1643
+ async function rejectDiff(item, provider) {
1644
+ const root = workspaceRoot();
1645
+ const bundle = item?.bundle;
1646
+ if (!root || !bundle) {
1647
+ vscode8.window.showWarningMessage("No UR inline diff selected.");
1648
+ return;
1649
+ }
1650
+ try {
1651
+ const { stdout } = await runUrCli(["ide", "diff", "reject", bundle.id], { cwd: root });
1652
+ provider.refresh();
1653
+ if (isNotFoundResult(stdout)) {
1654
+ vscode8.window.showErrorMessage(`UR inline diff not found: ${bundle.id}`);
1655
+ return;
1656
+ }
1657
+ vscode8.window.showInformationMessage(`Rejected UR patch ${bundle.id} (no files changed).`);
1658
+ } catch (error) {
1659
+ vscode8.window.showErrorMessage(errorMessage(error));
1660
+ }
1661
+ }
1662
+ async function showStatus(channel) {
1663
+ const root = workspaceRoot();
1664
+ if (!root) {
1665
+ vscode8.window.showWarningMessage("Open a workspace folder to query UR status.");
1666
+ return;
1667
+ }
1668
+ channel.clear();
1669
+ channel.show(true);
1670
+ channel.appendLine("Running: ur ide status");
1671
+ try {
1672
+ const { stdout } = await runUrCli(["ide", "status"], { cwd: root });
1673
+ channel.appendLine(stdout.trim());
1674
+ } catch (error) {
1675
+ channel.appendLine(errorMessage(error));
1676
+ }
1677
+ }
1678
+ function isNotFoundResult(stdout) {
1679
+ return stdout.trim().toLowerCase().includes("not found");
1680
+ }
1681
+
1682
+ // src/diffs/webview.ts
1683
+ var vscode9 = __toESM(require("vscode"));
1684
+ function renderDiffHtml(root, bundle) {
1685
+ const patch = readPatch(root, bundle);
1686
+ const comments = bundle.comments ?? [];
1687
+ return `<!doctype html>
1688
+ <html lang="en">
1689
+ <head>
1690
+ <meta charset="utf-8">
1691
+ <meta name="viewport" content="width=device-width, initial-scale=1">
1692
+ <style>
1693
+ :root { color-scheme: light dark; }
1694
+ body { font: 13px/1.5 var(--vscode-font-family); color: var(--vscode-foreground); padding: 20px; margin: 0; }
1695
+ header { border-bottom: 1px solid var(--vscode-panel-border); padding-bottom: 14px; margin-bottom: 16px; }
1696
+ h1 { font-size: 20px; font-weight: 600; margin: 0 0 6px; }
1697
+ h2 { font-size: 14px; margin: 20px 0 10px; }
1698
+ .meta, .where { color: var(--vscode-descriptionForeground); }
1699
+ .chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
1700
+ .chip { border: 1px solid var(--vscode-panel-border); border-radius: 4px; padding: 3px 8px; background: var(--vscode-sideBar-background); }
1701
+ 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); }
1702
+ .comments { margin-top: 18px; }
1703
+ .comment { border: 1px solid var(--vscode-panel-border); border-radius: 6px; padding: 10px 12px; margin-bottom: 10px; background: var(--vscode-sideBar-background); }
1704
+ </style>
1705
+ </head>
1706
+ <body>
1707
+ <header>
1708
+ <h1>${escapeHtml(bundle.title)}</h1>
1709
+ <div class="meta">${escapeHtml(bundle.id)} \xB7 ${escapeHtml(formatRelativeTime(bundle.updatedAt ?? bundle.createdAt))}</div>
1710
+ <div class="chips">
1711
+ <span class="chip">${escapeHtml(bundle.status ?? "captured")}</span>
1712
+ <span class="chip">${escapeHtml(formatCount(bundle.files?.length ?? 0, "file"))}</span>
1713
+ <span class="chip">${escapeHtml(formatCount(comments.length, "comment"))}</span>
1714
+ </div>
1715
+ </header>
1716
+ <pre>${escapeHtml(patch)}</pre>
1717
+ <section class="comments">
1718
+ <h2>Comments</h2>
1719
+ ${comments.length === 0 ? '<p class="meta">No comments yet.</p>' : comments.map((comment) => {
1720
+ const where = comment.file ? `${comment.file}${comment.line ? `:${comment.line}` : ""}` : "General";
1721
+ return `<div class="comment"><div class="where">${escapeHtml(where)} \xB7 ${escapeHtml(comment.at ?? "")}</div><div>${escapeHtml(comment.text)}</div></div>`;
1722
+ }).join("")}
1723
+ </section>
1724
+ </body>
1725
+ </html>`;
1726
+ }
1727
+ async function openDiff(item) {
1728
+ const root = workspaceRoot();
1729
+ const bundle = item?.bundle;
1730
+ if (!root || !bundle) {
1731
+ vscode9.window.showWarningMessage("No UR inline diff selected.");
1732
+ return;
1733
+ }
1734
+ const panel = vscode9.window.createWebviewPanel("urInlineDiff", `UR ${bundle.id}`, vscode9.ViewColumn.Active, {
1735
+ enableScripts: false
1736
+ });
1737
+ const latest = loadBundleMetadata(root, bundle);
1738
+ panel.webview.html = renderDiffHtml(root, latest);
1739
+ }
1740
+
1741
+ // src/model/modelPicker.ts
1742
+ var vscode10 = __toESM(require("vscode"));
1743
+ function isRecord2(value) {
1744
+ return typeof value === "object" && value !== null;
1745
+ }
1746
+ function parseProviderList(raw) {
1747
+ try {
1748
+ const data = JSON.parse(raw);
1749
+ if (!Array.isArray(data)) return [];
1750
+ return data.flatMap((entry) => {
1751
+ if (!isRecord2(entry) || typeof entry.id !== "string" || typeof entry.name !== "string") return [];
1752
+ return [
1753
+ {
1754
+ id: entry.id,
1755
+ name: entry.name,
1756
+ accessTypeLabel: typeof entry.accessTypeLabel === "string" ? entry.accessTypeLabel : void 0,
1757
+ providerKind: typeof entry.providerKind === "string" ? entry.providerKind : void 0,
1758
+ runtimeBackend: typeof entry.runtimeBackend === "string" ? entry.runtimeBackend : void 0
1759
+ }
1760
+ ];
1761
+ });
1762
+ } catch {
1763
+ return [];
1764
+ }
1765
+ }
1766
+ function parseProviderModels(raw) {
1767
+ try {
1768
+ const data = JSON.parse(raw);
1769
+ if (!isRecord2(data) || typeof data.provider !== "string" || !Array.isArray(data.models)) return void 0;
1770
+ const source = data.source === "live" || data.source === "cache" || data.source === "static" ? data.source : "static";
1771
+ return {
1772
+ provider: data.provider,
1773
+ source,
1774
+ warning: typeof data.warning === "string" ? data.warning : void 0,
1775
+ models: data.models.flatMap((model) => {
1776
+ if (!isRecord2(model) || typeof model.id !== "string") return [];
1777
+ return [
1778
+ {
1779
+ id: model.id,
1780
+ displayName: typeof model.displayName === "string" ? model.displayName : void 0,
1781
+ description: typeof model.description === "string" ? model.description : void 0,
1782
+ isDefault: Boolean(model.isDefault)
1783
+ }
1784
+ ];
1785
+ })
1786
+ };
1787
+ } catch {
1788
+ return void 0;
1789
+ }
1790
+ }
1791
+ function parseProviderStatus(raw) {
1792
+ try {
1793
+ const data = JSON.parse(raw);
1794
+ if (!isRecord2(data)) return {};
1795
+ return {
1796
+ provider: typeof data.provider === "string" ? data.provider : void 0,
1797
+ model: typeof data.model === "string" ? data.model : void 0
1798
+ };
1799
+ } catch {
1800
+ return {};
1801
+ }
1802
+ }
1803
+ function parseIdeStatus(raw) {
1804
+ try {
1805
+ const data = JSON.parse(raw);
1806
+ if (!isRecord2(data)) return {};
1807
+ const provider = isRecord2(data.provider) ? data.provider : {};
1808
+ return {
1809
+ model: typeof provider.model === "string" ? provider.model : void 0
1810
+ };
1811
+ } catch {
1812
+ return {};
1813
+ }
1814
+ }
1815
+ function selectionError(error) {
1816
+ if (error instanceof Error) return error.message;
1817
+ return String(error);
1818
+ }
1819
+ async function pickProviderModel(cwd) {
1820
+ if (!cwd) {
1821
+ vscode10.window.showWarningMessage("Open a workspace folder to pick a UR model.");
1822
+ return;
1823
+ }
1824
+ const [{ stdout: providerStdout }, statusResult, ideStatusResult] = await Promise.all([
1825
+ runUrCliCapture(["provider", "list", "--json"], { cwd }),
1826
+ runUrCliCapture(["provider", "status", "--json"], { cwd }).catch(() => ({ stdout: "" })),
1827
+ runUrCliCapture(["ide", "status", "--json"], { cwd }).catch(() => ({ stdout: "" }))
1828
+ ]);
1829
+ const providers = parseProviderList(providerStdout);
1830
+ if (providers.length === 0) {
1831
+ vscode10.window.showWarningMessage("No UR providers were reported by `ur provider list`.");
1832
+ return;
1833
+ }
1834
+ const providerStatus = parseProviderStatus(statusResult.stdout);
1835
+ const ideStatus = parseIdeStatus(ideStatusResult.stdout);
1836
+ const status = { provider: providerStatus.provider, model: providerStatus.model ?? ideStatus.model };
1837
+ const pickedProvider = await vscode10.window.showQuickPick(
1838
+ providers.map((provider) => ({
1839
+ label: provider.name,
1840
+ description: provider.id === status.provider ? "current" : provider.id,
1841
+ detail: [provider.accessTypeLabel, provider.providerKind, provider.runtimeBackend].filter(Boolean).join(" \xB7 "),
1842
+ provider
1843
+ })),
1844
+ {
1845
+ title: "UR: Pick Model",
1846
+ placeHolder: "Choose a provider first"
1847
+ }
1848
+ );
1849
+ if (!pickedProvider) return;
1850
+ let modelsStdout = "";
1851
+ try {
1852
+ const result = await runUrCliCapture(["provider", "models", pickedProvider.provider.id, "--json"], { cwd });
1853
+ modelsStdout = result.stdout;
1854
+ } catch (error) {
1855
+ vscode10.window.showWarningMessage(
1856
+ `UR could not list models for ${pickedProvider.provider.name}: ${selectionError(error)}`
1857
+ );
1858
+ return;
1859
+ }
1860
+ const modelResult = parseProviderModels(modelsStdout);
1861
+ if (!modelResult || modelResult.models.length === 0) {
1862
+ vscode10.window.showWarningMessage(
1863
+ modelResult?.warning ?? `No models are available for ${pickedProvider.provider.name}. Run "ur provider doctor ${pickedProvider.provider.id}" to troubleshoot.`
1864
+ );
1865
+ return;
1866
+ }
1867
+ if (modelResult.warning) {
1868
+ vscode10.window.showWarningMessage(modelResult.warning);
1869
+ }
1870
+ const pickedModel = await vscode10.window.showQuickPick(
1871
+ modelResult.models.map((model) => ({
1872
+ label: model.displayName ?? model.id,
1873
+ description: [
1874
+ model.id === status.model ? "current" : model.id,
1875
+ model.isDefault ? "default" : "",
1876
+ modelResult.source
1877
+ ].filter(Boolean).join(" \xB7 "),
1878
+ detail: model.description,
1879
+ model
1880
+ })),
1881
+ {
1882
+ title: `UR: Pick Model for ${pickedProvider.provider.name}`,
1883
+ placeHolder: "Choose a model for this provider"
1884
+ }
1885
+ );
1886
+ if (!pickedModel) return;
1887
+ try {
1888
+ const { stdout } = await runUrCliCapture(
1889
+ ["provider", "select-model", pickedProvider.provider.id, pickedModel.model.id, "--json"],
1890
+ { cwd }
1891
+ );
1892
+ const parsed = JSON.parse(stdout);
1893
+ if (parsed.ok) {
1894
+ vscode10.window.showInformationMessage(
1895
+ `UR model set to ${parsed.model ?? pickedModel.model.id} for ${parsed.provider ?? pickedProvider.provider.id}.`
1896
+ );
1897
+ return;
1898
+ }
1899
+ vscode10.window.showErrorMessage(parsed.message ?? "UR could not save the selected model.");
1900
+ } catch (error) {
1901
+ vscode10.window.showErrorMessage(`UR could not save the selected model: ${selectionError(error)}`);
1902
+ }
1903
+ }
1904
+
1905
+ // src/misc/quickCommands.ts
1906
+ var vscode11 = __toESM(require("vscode"));
1907
+ var UR_DOCS_URL = "https://github.com/Maitham16/UR#readme";
1908
+ async function openSettings() {
1909
+ await vscode11.commands.executeCommand("workbench.action.openSettings", "UR");
1910
+ }
1911
+ async function openDocs() {
1912
+ await vscode11.env.openExternal(vscode11.Uri.parse(UR_DOCS_URL));
1913
+ }
1914
+ async function openArtifacts() {
1915
+ const root = workspaceRoot();
1916
+ if (!root) {
1917
+ vscode11.window.showWarningMessage("Open a workspace folder to view UR artifacts.");
1918
+ return;
1919
+ }
1920
+ const uri = vscode11.Uri.joinPath(vscode11.Uri.file(root), ".ur");
1921
+ try {
1922
+ await vscode11.commands.executeCommand("revealInExplorer", uri);
1923
+ } catch {
1924
+ vscode11.window.showInformationMessage("No .ur directory yet \u2014 it is created the first time UR runs in this workspace.");
1925
+ }
1926
+ }
1927
+ async function runSpecAction(chat) {
1928
+ await chat.runStructuredPrompt(buildRunSpecPrompt());
1929
+ }
1930
+ async function runWorkflowAction(chat) {
1931
+ await chat.runStructuredPrompt(buildRunWorkflowPrompt());
1932
+ }
1933
+
1934
+ // src/options/agentOptionsPanel.ts
1935
+ var vscode12 = __toESM(require("vscode"));
1936
+
1937
+ // src/options/agentOptions.ts
1938
+ function ids(options) {
1939
+ return options.map((o) => o.id);
1940
+ }
1941
+ function isLocalOrServer(option) {
1942
+ return option.accessType === "local" || option.accessType === "server";
1943
+ }
1944
+ function buildRecommendations(options) {
1945
+ const local = options.filter(isLocalOrServer);
1946
+ const nativeStreaming = options.filter((o) => o.supportsNativeStreaming);
1947
+ const multimodal = options.filter((o) => o.multimodal === true);
1948
+ const toolCalling = options.filter((o) => o.supportsNativeToolCalls);
1949
+ const subscriptionCli = options.filter((o) => o.providerKind === "subscription-cli");
1950
+ const refactorCapable = options.filter((o) => o.providerKind === "ur-native" && o.supportsNativeToolCalls);
1951
+ const recommendations = [
1952
+ {
1953
+ category: "privacy",
1954
+ title: "Privacy",
1955
+ rationale: "Local/self-hosted runtimes keep prompts, code, and responses on your machine; nothing is sent to a third-party API.",
1956
+ recommendedProviderIds: ids(local)
1957
+ },
1958
+ {
1959
+ category: "speed",
1960
+ title: "Speed",
1961
+ rationale: "Local/self-hosted runtimes avoid a network round-trip per request. This reflects request path structure only \u2014 actual throughput depends on your hardware and the model you load.",
1962
+ recommendedProviderIds: ids(local)
1963
+ },
1964
+ {
1965
+ category: "multimodal",
1966
+ title: "Multimodal (image input)",
1967
+ rationale: "These providers accept image content at the provider level. Local/self-hosted and OpenAI-compatible endpoints are marked unknown below since support depends on the model currently loaded, not a fixed provider fact.",
1968
+ recommendedProviderIds: ids(multimodal)
1969
+ },
1970
+ {
1971
+ category: "tool-calling",
1972
+ title: "Native tool calling",
1973
+ rationale: "UR-native providers support UR-native tool-call parsing. Subscription CLI providers do not \u2014 UR passes prompt text to the external CLI and receives final text only.",
1974
+ recommendedProviderIds: ids(toolCalling)
1975
+ },
1976
+ {
1977
+ category: "native-streaming",
1978
+ title: "Native streaming",
1979
+ rationale: "These providers stream tokens as they are generated. Subscription CLI providers return final text output only, with no UR-native token stream.",
1980
+ recommendedProviderIds: ids(nativeStreaming)
1981
+ },
1982
+ {
1983
+ category: "subscription-cli-access",
1984
+ title: "Subscription CLI access",
1985
+ rationale: "For using an existing Codex CLI / Claude Code / Gemini CLI / Antigravity subscription through UR.",
1986
+ recommendedProviderIds: ids(subscriptionCli),
1987
+ caveat: subscriptionCli[0]?.safetyBoundaryLabel ?? "External vendor CLI boundary: UR passes prompt text to the official CLI and receives final text output only."
1988
+ },
1989
+ {
1990
+ category: "local-offline",
1991
+ title: "Local / offline",
1992
+ rationale: "Runs against a local or self-hosted endpoint; works without an internet connection once the runtime and model are available on your machine.",
1993
+ recommendedProviderIds: ids(local)
1994
+ },
1995
+ {
1996
+ category: "complex-refactor",
1997
+ title: "Complex, multi-step refactors",
1998
+ rationale: "Only UR-native providers with native tool calling get full UR tool-call parsing, sandbox, and verifier enforcement on every step. This is a structural capability statement, not a claim about model reasoning quality.",
1999
+ recommendedProviderIds: ids(refactorCapable)
2000
+ },
2001
+ {
2002
+ category: "docs-review",
2003
+ title: "Docs / review writing",
2004
+ rationale: "Docs and review tasks are typically single-turn text generation with light tool use, so no provider is structurally favored here \u2014 use whichever provider you already have configured for chat.",
2005
+ recommendedProviderIds: []
2006
+ }
2007
+ ];
2008
+ return recommendations;
2009
+ }
2010
+
2011
+ // src/options/providerKnowledge.ts
2012
+ var MULTIMODAL_TRUE = /* @__PURE__ */ new Set(["openai-api", "anthropic-api", "gemini-api", "openrouter"]);
2013
+ var MULTIMODAL_FALSE = /* @__PURE__ */ new Set(["codex-cli", "claude-code-cli", "gemini-cli", "antigravity-cli"]);
2014
+ function deriveMultimodalSupport(providerId) {
2015
+ if (MULTIMODAL_TRUE.has(providerId)) return true;
2016
+ if (MULTIMODAL_FALSE.has(providerId)) return false;
2017
+ return "unknown";
2018
+ }
2019
+
2020
+ // src/options/providerOptionsLoader.ts
2021
+ var PROVIDER_KINDS = ["ur-native", "subscription-cli", "subscription-placeholder"];
2022
+ var ACCESS_TYPES = ["subscription", "api", "local", "server"];
2023
+ function isRecord3(value) {
2024
+ return typeof value === "object" && value !== null;
2025
+ }
2026
+ function parseProviderListJson(raw) {
2027
+ let data;
2028
+ try {
2029
+ data = JSON.parse(raw);
2030
+ } catch {
2031
+ return [];
2032
+ }
2033
+ if (!Array.isArray(data)) return [];
2034
+ const options = [];
2035
+ for (const entry of data) {
2036
+ if (!isRecord3(entry)) continue;
2037
+ if (typeof entry.id !== "string" || typeof entry.name !== "string") continue;
2038
+ const providerKind = PROVIDER_KINDS.includes(entry.providerKind) ? entry.providerKind : "subscription-placeholder";
2039
+ const accessType = ACCESS_TYPES.includes(entry.accessType) ? entry.accessType : "api";
2040
+ options.push({
2041
+ id: entry.id,
2042
+ displayName: entry.name,
2043
+ providerKind,
2044
+ accessType,
2045
+ usesExternalCli: Boolean(entry.usesExternalCli),
2046
+ supportsNativeToolCalls: Boolean(entry.supportsNativeToolCalls),
2047
+ supportsNativeStreaming: Boolean(entry.supportsNativeStreaming),
2048
+ multimodal: deriveMultimodalSupport(entry.id),
2049
+ safetyBoundaryLabel: typeof entry.safetyBoundaryLabel === "string" ? entry.safetyBoundaryLabel : ""
2050
+ });
2051
+ }
2052
+ return options;
2053
+ }
2054
+ async function loadProviderOptions(cwd) {
2055
+ try {
2056
+ const { stdout } = await runUrCliCapture(["provider", "list", "--json"], { cwd });
2057
+ return parseProviderListJson(stdout);
2058
+ } catch {
2059
+ return [];
2060
+ }
2061
+ }
2062
+
2063
+ // src/options/agentOptionsPanel.ts
2064
+ function knownText(value) {
2065
+ if (value === "unknown") return "unknown";
2066
+ if (typeof value === "boolean") return value ? "yes" : "no";
2067
+ return String(value);
2068
+ }
2069
+ function displayNameFor(options, id) {
2070
+ return options.find((o) => o.id === id)?.displayName ?? id;
2071
+ }
2072
+ function renderProviderTable(options) {
2073
+ if (options.length === 0) return '<p class="meta">No providers found. Is the UR CLI on PATH?</p>';
2074
+ const rows = options.map(
2075
+ (o) => `<tr><td>${escapeHtml(o.displayName)}</td><td>${escapeHtml(o.providerKind)}</td><td>${escapeHtml(o.accessType)}</td><td>${escapeHtml(knownText(o.multimodal))}</td></tr>`
2076
+ ).join("");
2077
+ return `<table><thead><tr><th>Provider</th><th>Provider kind</th><th>Access type</th><th>Multimodal</th></tr></thead><tbody>${rows}</tbody></table>`;
2078
+ }
2079
+ function renderCategory(options, rec) {
2080
+ const names = rec.recommendedProviderIds.length > 0 ? rec.recommendedProviderIds.map((id) => escapeHtml(displayNameFor(options, id))).join(", ") : "(no provider structurally favored)";
2081
+ const caveat = rec.caveat ? `<p class="caveat">${escapeHtml(rec.caveat)}</p>` : "";
2082
+ return `<section class="category">
2083
+ <h3>${escapeHtml(rec.title)}</h3>
2084
+ <p>${escapeHtml(rec.rationale)}</p>
2085
+ <p class="recommend"><strong>Recommended:</strong> ${names}</p>
2086
+ ${caveat}
2087
+ </section>`;
2088
+ }
2089
+ function renderOptionsHtml(options) {
2090
+ const recommendations = buildRecommendations(options);
2091
+ return `<!doctype html>
2092
+ <html lang="en">
2093
+ <head>
2094
+ <meta charset="utf-8">
2095
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2096
+ <style>
2097
+ :root { color-scheme: light dark; }
2098
+ body { font: 13px/1.6 var(--vscode-font-family); color: var(--vscode-foreground); padding: 20px; margin: 0; }
2099
+ h1 { font-size: 18px; font-weight: 600; margin: 0 0 6px; }
2100
+ h2 { font-size: 13px; margin: 20px 0 8px; color: var(--vscode-descriptionForeground); text-transform: uppercase; letter-spacing: 0.04em; }
2101
+ .disclaimer { color: var(--vscode-descriptionForeground); border-left: 3px solid var(--vscode-textBlockQuote-border); padding: 8px 12px; margin-bottom: 20px; background: var(--vscode-textBlockQuote-background); }
2102
+ table { border-collapse: collapse; width: 100%; margin-bottom: 12px; }
2103
+ th, td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--vscode-panel-border); }
2104
+ th { color: var(--vscode-descriptionForeground); font-weight: 600; }
2105
+ .category { border: 1px solid var(--vscode-panel-border); border-radius: 6px; padding: 12px 16px; margin-bottom: 14px; }
2106
+ .category h3 { margin: 0 0 6px; font-size: 13px; }
2107
+ .recommend { margin: 8px 0 0; }
2108
+ .caveat { color: var(--vscode-descriptionForeground); font-style: italic; margin: 8px 0 0; }
2109
+ .meta { color: var(--vscode-descriptionForeground); }
2110
+ button { background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 4px; padding: 6px 14px; cursor: pointer; font: inherit; }
2111
+ button:hover { background: var(--vscode-button-hoverBackground); }
2112
+ </style>
2113
+ </head>
2114
+ <body>
2115
+ <h1>UR Agent Options</h1>
2116
+ <p class="disclaimer">Based on local, curated data and the UR CLI's own provider registry only. This is not live market research and does not rank model quality.</p>
2117
+ <h2>Providers</h2>
2118
+ ${renderProviderTable(options)}
2119
+ <h2>Recommendations</h2>
2120
+ ${recommendations.map((rec) => renderCategory(options, rec)).join("")}
2121
+ <button id="refresh">Refresh</button>
2122
+ <script>
2123
+ const vscode = acquireVsCodeApi();
2124
+ document.getElementById('refresh').addEventListener('click', () => vscode.postMessage({ type: 'refresh' }));
2125
+ </script>
2126
+ </body>
2127
+ </html>`;
2128
+ }
2129
+ function renderLoadingHtml() {
2130
+ return `<!doctype html><html><body style="font-family: var(--vscode-font-family); padding: 20px; color: var(--vscode-foreground);"><p>Loading UR agent options\u2026</p></body></html>`;
2131
+ }
2132
+ var AgentOptionsPanel = class _AgentOptionsPanel {
2133
+ static current;
2134
+ panel;
2135
+ disposed = false;
2136
+ constructor(panel, onRefresh) {
2137
+ this.panel = panel;
2138
+ this.panel.webview.onDidReceiveMessage((message) => {
2139
+ if (message?.type === "refresh") onRefresh();
2140
+ });
2141
+ this.panel.onDidDispose(() => {
2142
+ this.disposed = true;
2143
+ if (_AgentOptionsPanel.current === this) _AgentOptionsPanel.current = void 0;
2144
+ });
2145
+ }
2146
+ static createOrShow(onRefresh) {
2147
+ if (_AgentOptionsPanel.current && !_AgentOptionsPanel.current.disposed) {
2148
+ _AgentOptionsPanel.current.panel.reveal(vscode12.ViewColumn.Active);
2149
+ return _AgentOptionsPanel.current;
2150
+ }
2151
+ const panel = vscode12.window.createWebviewPanel("urAgentOptions", "UR Agent Options", vscode12.ViewColumn.Active, {
2152
+ enableScripts: true
2153
+ });
2154
+ const instance = new _AgentOptionsPanel(panel, onRefresh);
2155
+ _AgentOptionsPanel.current = instance;
2156
+ return instance;
2157
+ }
2158
+ render(options) {
2159
+ if (this.disposed) return;
2160
+ this.panel.webview.html = renderOptionsHtml(options);
2161
+ }
2162
+ renderLoading() {
2163
+ if (this.disposed) return;
2164
+ this.panel.webview.html = renderLoadingHtml();
2165
+ }
2166
+ };
2167
+ async function showAgentOptions(cwd) {
2168
+ if (!cwd) {
2169
+ vscode12.window.showWarningMessage("Open a workspace folder to view UR agent options.");
2170
+ return;
2171
+ }
2172
+ let panel;
2173
+ const refresh = async () => {
2174
+ if (!panel) return;
2175
+ panel.renderLoading();
2176
+ const options = await loadProviderOptions(cwd);
2177
+ panel.render(options);
2178
+ };
2179
+ panel = AgentOptionsPanel.createOrShow(() => void refresh());
2180
+ await refresh();
2181
+ }
2182
+
2183
+ // src/review/reviewDiff.ts
2184
+ var import_node_child_process4 = require("node:child_process");
2185
+ var import_node_util3 = require("node:util");
2186
+ var vscode13 = __toESM(require("vscode"));
2187
+
2188
+ // src/review/reviewPrompt.ts
2189
+ var LARGE_DIFF_THRESHOLD = 2e4;
2190
+ function buildReviewPrompt(diff) {
2191
+ return [
2192
+ "Review the current git diff for correctness, style, and potential bugs.",
2193
+ "Point out specific issues with file references where possible, and suggest concrete improvements.",
2194
+ "",
2195
+ "```diff",
2196
+ diff,
2197
+ "```"
2198
+ ].join("\n");
2199
+ }
2200
+
2201
+ // src/review/reviewDiff.ts
2202
+ var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process4.execFile);
2203
+ async function captureGitDiff(cwd) {
2204
+ const { stdout } = await execFileAsync3("git", ["diff", "HEAD"], { cwd, shell: false });
2205
+ return stdout;
2206
+ }
2207
+ async function reviewCurrentDiff(chat) {
2208
+ const root = workspaceRoot();
2209
+ if (!root) {
2210
+ vscode13.window.showWarningMessage("Open a workspace folder to review a diff.");
2211
+ return;
2212
+ }
2213
+ let diff;
2214
+ try {
2215
+ diff = await captureGitDiff(root);
2216
+ } catch (error) {
2217
+ vscode13.window.showErrorMessage(`Could not read the current git diff: ${processErrorMessage(error)}`);
2218
+ return;
2219
+ }
2220
+ if (!diff.trim()) {
2221
+ vscode13.window.showInformationMessage("No changes to review (working tree matches HEAD).");
2222
+ return;
2223
+ }
2224
+ if (diff.length > LARGE_DIFF_THRESHOLD) {
2225
+ const choice = await vscode13.window.showWarningMessage(
2226
+ `The current diff is large (${diff.length.toLocaleString()} characters). Send it to UR for review?`,
2227
+ { modal: true },
2228
+ "Send"
2229
+ );
2230
+ if (choice !== "Send") return;
2231
+ }
2232
+ await chat.runStructuredPrompt(buildReviewPrompt(diff));
2233
+ }
2234
+
2235
+ // src/search/searchQuickPick.ts
2236
+ var vscode14 = __toESM(require("vscode"));
2237
+
2238
+ // src/search/actionRegistry.ts
2239
+ var ACTION_REGISTRY = [
2240
+ { id: "newChat", label: "New Chat", commandId: "urInlineDiffs.chat.new", description: "Start a new UR chat session" },
2241
+ { id: "openChat", label: "Open Chat", commandId: "urInlineDiffs.chat.open", description: "Open or resume a UR chat session" },
2242
+ { id: "explainSelection", label: "Explain Selection", commandId: "urInlineDiffs.chat.explainSelection", description: "Ask UR to explain the current editor selection" },
2243
+ { id: "fixSelection", label: "Fix Selection", commandId: "urInlineDiffs.chat.fixSelection", description: "Ask UR to fix the current editor selection" },
2244
+ { id: "generateTests", label: "Generate Tests", commandId: "urInlineDiffs.chat.generateTests", description: "Ask UR to generate tests for the current selection" },
2245
+ { id: "reviewCurrentDiff", label: "Review Current Diff", commandId: "urInlineDiffs.reviewCurrentDiff", description: "Send the current git diff to UR for review" },
2246
+ { id: "runVerifier", label: "Run Verifier", commandId: "urInlineDiffs.runVerifier", description: "Run the UR verifier against the current changes" },
2247
+ { id: "providerStatus", label: "Provider Status", commandId: "urInlineDiffs.status", description: "Show provider, model, and plugin status" },
2248
+ { id: "agentStatus", label: "Agent Status", commandId: "urInlineDiffs.agentStatus", description: "Open the UR agent status card" },
2249
+ { id: "agentOptions", label: "Agent Options", commandId: "urInlineDiffs.agentOptions", description: "Open curated provider recommendations" },
2250
+ { id: "pickModel", label: "Pick Model", commandId: "urInlineDiffs.pickModel", description: "Choose a provider-scoped UR model" },
2251
+ { id: "openSettings", label: "Open Settings", commandId: "urInlineDiffs.openSettings", description: "Open VS Code settings filtered to UR" },
2252
+ { id: "openDocs", label: "Open Docs", commandId: "urInlineDiffs.openDocs", description: "Open the UR documentation" },
2253
+ { id: "openArtifacts", label: "Open Artifacts", commandId: "urInlineDiffs.openArtifacts", description: "Reveal the .ur workspace directory" },
2254
+ { id: "runSpec", label: "Run Spec", commandId: "urInlineDiffs.runSpec", description: "Ask UR to list and run specs (ur spec)" },
2255
+ { id: "runWorkflow", label: "Run Workflow", commandId: "urInlineDiffs.runWorkflow", description: "Ask UR to list and run workflows (ur workflow)" },
2256
+ { id: "refreshActions", label: "Refresh IDE Actions", commandId: "urActions.refresh", description: "Refresh the UR actions panel" }
2257
+ ];
2258
+
2259
+ // src/search/searchQuickPick.ts
2260
+ async function showSearchActions() {
2261
+ const items = ACTION_REGISTRY.map((action) => ({
2262
+ label: action.label,
2263
+ detail: action.description,
2264
+ commandId: action.commandId
2265
+ }));
2266
+ const picked = await vscode14.window.showQuickPick(items, {
2267
+ title: "UR: Search Actions",
2268
+ placeHolder: "Search UR actions",
2269
+ matchOnDetail: true
2270
+ });
2271
+ if (!picked) return;
2272
+ await vscode14.commands.executeCommand(picked.commandId);
2273
+ }
2274
+
2275
+ // src/status/statusPanel.ts
2276
+ var vscode15 = __toESM(require("vscode"));
2277
+
2278
+ // src/status/statusData.ts
2279
+ function isRecord4(value) {
2280
+ return typeof value === "object" && value !== null;
2281
+ }
2282
+ function safeParseRecord(raw) {
2283
+ try {
2284
+ const parsed = JSON.parse(raw);
2285
+ return isRecord4(parsed) ? parsed : {};
2286
+ } catch {
2287
+ return {};
2288
+ }
2289
+ }
2290
+ function asKnownString(value, allowed) {
2291
+ return typeof value === "string" && allowed.includes(value) ? value : "unknown";
2292
+ }
2293
+ function asKnownBoolean(value) {
2294
+ return typeof value === "boolean" ? value : "unknown";
2295
+ }
2296
+ function parseIdeStatusJson(raw, fallbackWorkspaceRoot = "") {
2297
+ const data = safeParseRecord(raw);
2298
+ const acpRaw = isRecord4(data.acp) ? data.acp : {};
2299
+ const providerRaw = isRecord4(data.provider) ? data.provider : {};
2300
+ return {
2301
+ workspaceRoot: typeof data.workspaceRoot === "string" && data.workspaceRoot ? data.workspaceRoot : fallbackWorkspaceRoot,
2302
+ acp: {
2303
+ running: Boolean(acpRaw.running),
2304
+ port: typeof acpRaw.port === "number" ? acpRaw.port : null,
2305
+ host: typeof acpRaw.host === "string" ? acpRaw.host : "127.0.0.1"
2306
+ },
2307
+ pluginCount: typeof data.pluginCount === "number" ? data.pluginCount : 0,
2308
+ warnings: Array.isArray(data.warnings) ? data.warnings.filter((w) => typeof w === "string") : [],
2309
+ sandboxMode: asKnownString(data.sandboxMode, ["disabled", "recommended", "required"]),
2310
+ verifierMode: asKnownString(data.verifierMode, ["off", "loose", "strict"]),
2311
+ providerLabel: typeof providerRaw.label === "string" ? providerRaw.label : "Unknown provider",
2312
+ providerModel: typeof providerRaw.model === "string" ? providerRaw.model : void 0
2313
+ };
2314
+ }
2315
+ function parseProviderStatusJson(raw) {
2316
+ const data = safeParseRecord(raw);
2317
+ return {
2318
+ providerId: typeof data.provider === "string" ? data.provider : void 0,
2319
+ providerKind: asKnownString(data.providerKind, ["ur-native", "subscription-cli", "subscription-placeholder"]),
2320
+ usesExternalCli: asKnownBoolean(data.usesExternalCli),
2321
+ supportsNativeToolCalls: asKnownBoolean(data.supportsNativeToolCalls),
2322
+ supportsNativeStreaming: asKnownBoolean(data.supportsNativeStreaming),
2323
+ safetyBoundaryLabel: typeof data.safetyBoundaryLabel === "string" ? data.safetyBoundaryLabel : void 0
2324
+ };
2325
+ }
2326
+ function errorMessage3(reason) {
2327
+ return reason instanceof Error ? reason.message : String(reason);
2328
+ }
2329
+ var defaultRunner = runUrCliCapture;
2330
+ async function assembleAgentStatus(cwd, runCli = defaultRunner) {
2331
+ const [ideResult, providerResult, versionResult] = await Promise.allSettled([
2332
+ runCli(["ide", "status", "--json"], { cwd }),
2333
+ runCli(["provider", "status", "--json"], { cwd }),
2334
+ runCli(["--version"], { cwd })
2335
+ ]);
2336
+ const ide = ideResult.status === "fulfilled" ? parseIdeStatusJson(ideResult.value.stdout, cwd) : parseIdeStatusJson("", cwd);
2337
+ const provider = providerResult.status === "fulfilled" ? parseProviderStatusJson(providerResult.value.stdout) : parseProviderStatusJson("");
2338
+ const urVersion = versionResult.status === "fulfilled" ? versionResult.value.stdout.trim() || "unknown" : "unknown";
2339
+ const warnings = [...ide.warnings];
2340
+ if (ideResult.status === "rejected") warnings.push(`Could not read IDE status: ${errorMessage3(ideResult.reason)}`);
2341
+ if (providerResult.status === "rejected") warnings.push(`Could not read provider status: ${errorMessage3(providerResult.reason)}`);
2342
+ return {
2343
+ urVersion,
2344
+ workspaceRoot: ide.workspaceRoot,
2345
+ acp: ide.acp,
2346
+ provider: {
2347
+ label: ide.providerLabel,
2348
+ model: ide.providerModel,
2349
+ providerKind: provider.providerKind,
2350
+ usesExternalCli: provider.usesExternalCli,
2351
+ supportsNativeToolCalls: provider.supportsNativeToolCalls,
2352
+ supportsNativeStreaming: provider.supportsNativeStreaming,
2353
+ multimodal: provider.providerId ? deriveMultimodalSupport(provider.providerId) : "unknown",
2354
+ safetyBoundaryLabel: provider.safetyBoundaryLabel
2355
+ },
2356
+ sandboxMode: ide.sandboxMode,
2357
+ verifierMode: ide.verifierMode,
2358
+ pluginCount: ide.pluginCount,
2359
+ warnings
2360
+ };
2361
+ }
2362
+
2363
+ // src/status/statusPanel.ts
2364
+ function knownText2(value) {
2365
+ if (value === "unknown") return "unknown";
2366
+ if (typeof value === "boolean") return value ? "yes" : "no";
2367
+ return String(value);
2368
+ }
2369
+ function row(label, value) {
2370
+ return `<div class="row"><span class="label">${escapeHtml(label)}</span><span class="value">${escapeHtml(value)}</span></div>`;
2371
+ }
2372
+ function renderStatusHtml(status) {
2373
+ const acp = status.acp.running ? `running on ${status.acp.host}:${status.acp.port}` : "not running";
2374
+ const warnings = status.warnings.length === 0 ? '<p class="meta">No warnings.</p>' : `<ul>${status.warnings.map((w) => `<li>${escapeHtml(w)}</li>`).join("")}</ul>`;
2375
+ const boundary = status.provider.safetyBoundaryLabel ? row("Safety boundary", status.provider.safetyBoundaryLabel) : "";
2376
+ return `<!doctype html>
2377
+ <html lang="en">
2378
+ <head>
2379
+ <meta charset="utf-8">
2380
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2381
+ <style>
2382
+ :root { color-scheme: light dark; }
2383
+ body { font: 13px/1.5 var(--vscode-font-family); color: var(--vscode-foreground); padding: 20px; margin: 0; }
2384
+ h1 { font-size: 18px; font-weight: 600; margin: 0 0 16px; }
2385
+ .row { display: flex; justify-content: space-between; gap: 16px; padding: 6px 0; border-bottom: 1px solid var(--vscode-panel-border); }
2386
+ .label { color: var(--vscode-descriptionForeground); }
2387
+ .value { text-align: right; word-break: break-word; }
2388
+ section { margin-top: 20px; }
2389
+ h2 { font-size: 13px; margin: 0 0 8px; color: var(--vscode-descriptionForeground); text-transform: uppercase; letter-spacing: 0.04em; }
2390
+ button { background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 4px; padding: 6px 14px; cursor: pointer; font: inherit; }
2391
+ button:hover { background: var(--vscode-button-hoverBackground); }
2392
+ .meta { color: var(--vscode-descriptionForeground); }
2393
+ ul { margin: 4px 0 0; padding-left: 18px; }
2394
+ </style>
2395
+ </head>
2396
+ <body>
2397
+ <h1>UR Agent Status</h1>
2398
+ ${row("UR version", status.urVersion)}
2399
+ ${row("Workspace root", status.workspaceRoot)}
2400
+ ${row("Provider", status.provider.label)}
2401
+ ${row("Model", status.provider.model ?? "(none selected)")}
2402
+ ${row("Provider kind", knownText2(status.provider.providerKind))}
2403
+ ${row("Uses external CLI", knownText2(status.provider.usesExternalCli))}
2404
+ ${row("Native tool-call support", knownText2(status.provider.supportsNativeToolCalls))}
2405
+ ${row("Native streaming support", knownText2(status.provider.supportsNativeStreaming))}
2406
+ ${row("Multimodal support", knownText2(status.provider.multimodal))}
2407
+ ${boundary}
2408
+ ${row("Sandbox mode", knownText2(status.sandboxMode))}
2409
+ ${row("Verifier mode", knownText2(status.verifierMode))}
2410
+ ${row("ACP server", acp)}
2411
+ ${row("Plugins loaded", String(status.pluginCount))}
2412
+ <section>
2413
+ <h2>Warnings</h2>
2414
+ ${warnings}
2415
+ </section>
2416
+ <section>
2417
+ <button id="refresh">Refresh</button>
2418
+ </section>
2419
+ <script>
2420
+ const vscode = acquireVsCodeApi();
2421
+ document.getElementById('refresh').addEventListener('click', () => vscode.postMessage({ type: 'refresh' }));
2422
+ </script>
2423
+ </body>
2424
+ </html>`;
2425
+ }
2426
+ function renderLoadingHtml2() {
2427
+ return `<!doctype html><html><body style="font-family: var(--vscode-font-family); padding: 20px; color: var(--vscode-foreground);"><p>Loading UR agent status\u2026</p></body></html>`;
2428
+ }
2429
+ var StatusPanel = class _StatusPanel {
2430
+ static current;
2431
+ panel;
2432
+ disposed = false;
2433
+ constructor(panel, onRefresh) {
2434
+ this.panel = panel;
2435
+ this.panel.webview.onDidReceiveMessage((message) => {
2436
+ if (message?.type === "refresh") onRefresh();
2437
+ });
2438
+ this.panel.onDidDispose(() => {
2439
+ this.disposed = true;
2440
+ if (_StatusPanel.current === this) _StatusPanel.current = void 0;
2441
+ });
2442
+ }
2443
+ static createOrShow(onRefresh) {
2444
+ if (_StatusPanel.current && !_StatusPanel.current.disposed) {
2445
+ _StatusPanel.current.panel.reveal(vscode15.ViewColumn.Active);
2446
+ return _StatusPanel.current;
2447
+ }
2448
+ const panel = vscode15.window.createWebviewPanel("urAgentStatus", "UR Agent Status", vscode15.ViewColumn.Active, {
2449
+ enableScripts: true
2450
+ });
2451
+ const instance = new _StatusPanel(panel, onRefresh);
2452
+ _StatusPanel.current = instance;
2453
+ return instance;
2454
+ }
2455
+ render(status) {
2456
+ if (this.disposed) return;
2457
+ this.panel.webview.html = renderStatusHtml(status);
2458
+ }
2459
+ renderLoading() {
2460
+ if (this.disposed) return;
2461
+ this.panel.webview.html = renderLoadingHtml2();
2462
+ }
2463
+ };
2464
+ async function showAgentStatus(cwd) {
2465
+ if (!cwd) {
2466
+ vscode15.window.showWarningMessage("Open a workspace folder to view UR agent status.");
2467
+ return;
2468
+ }
2469
+ let panel;
2470
+ const refresh = async () => {
2471
+ if (!panel) return;
2472
+ panel.renderLoading();
2473
+ const status = await assembleAgentStatus(cwd);
2474
+ panel.render(status);
2475
+ };
2476
+ panel = StatusPanel.createOrShow(() => void refresh());
2477
+ await refresh();
2478
+ }
2479
+
2480
+ // src/verify/runVerifier.ts
2481
+ var vscode16 = __toESM(require("vscode"));
2482
+
2483
+ // src/verify/verifierPrompt.ts
2484
+ function buildVerifierPrompt() {
2485
+ return [
2486
+ 'Run verification on the current changes: spawn the verification subagent (Task tool, subagent_type="verification") to check the most recent task.',
2487
+ "Include in the subagent prompt: the files changed in the most recent task, a short summary of the approach taken, and any test/lint/build commands defined in the project.",
2488
+ "Wait for the VERDICT line from the subagent and report it verbatim, along with any findings. Do not declare the task complete unless the verdict is PASS."
2489
+ ].join("\n");
2490
+ }
2491
+
2492
+ // src/verify/runVerifier.ts
2493
+ async function runVerifier(chat) {
2494
+ const root = workspaceRoot();
2495
+ if (!root) {
2496
+ vscode16.window.showWarningMessage("Open a workspace folder to run the UR verifier.");
2497
+ return;
2498
+ }
2499
+ await chat.runStructuredPrompt(buildVerifierPrompt());
2500
+ }
2501
+
2502
+ // src/extension.ts
2503
+ function activate(context) {
2504
+ const diffTreeProvider = new DiffTreeProvider();
2505
+ const actionsTreeProvider = new ActionsTreeProvider();
2506
+ const channel = vscode17.window.createOutputChannel("UR");
2507
+ const chat = new ChatController();
2508
+ const chatTreeProvider = new ChatTreeProvider(chat);
2509
+ const chatTree = vscode17.window.createTreeView("urChat", {
2510
+ treeDataProvider: chatTreeProvider,
2511
+ showCollapseAll: false
2512
+ });
2513
+ const diffTree = vscode17.window.createTreeView("urInlineDiffs", {
2514
+ treeDataProvider: diffTreeProvider,
2515
+ showCollapseAll: false
2516
+ });
2517
+ const actionsTree = vscode17.window.createTreeView("urActions", {
2518
+ treeDataProvider: actionsTreeProvider,
2519
+ showCollapseAll: false
2520
+ });
2521
+ const bothDiffViews = {
2522
+ refresh: () => {
2523
+ diffTreeProvider.refresh();
2524
+ actionsTreeProvider.refresh();
2525
+ }
2526
+ };
2527
+ context.subscriptions.push(
2528
+ channel,
2529
+ chatTree,
2530
+ diffTree,
2531
+ actionsTree,
2532
+ chat,
2533
+ chat.onDidChangeState(() => chatTreeProvider.refresh()),
2534
+ vscode17.window.onDidChangeActiveTextEditor(() => chatTreeProvider.refresh()),
2535
+ vscode17.window.onDidChangeTextEditorSelection(() => chatTreeProvider.refresh()),
2536
+ vscode17.commands.registerCommand("urInlineDiffs.refresh", () => diffTreeProvider.refresh()),
2537
+ vscode17.commands.registerCommand("urInlineDiffs.open", (item) => openDiff(item)),
2538
+ vscode17.commands.registerCommand("urInlineDiffs.comment", (item) => commentDiff(item, bothDiffViews)),
2539
+ vscode17.commands.registerCommand("urInlineDiffs.apply", (item) => applyDiff(item, bothDiffViews)),
2540
+ vscode17.commands.registerCommand("urInlineDiffs.reject", (item) => rejectDiff(item, bothDiffViews)),
2541
+ vscode17.commands.registerCommand("urInlineDiffs.status", () => showStatus(channel)),
2542
+ vscode17.commands.registerCommand("urInlineDiffs.chat.new", () => chat.newChat()),
2543
+ vscode17.commands.registerCommand("urInlineDiffs.chat.open", () => chat.openChat()),
2544
+ vscode17.commands.registerCommand("urInlineDiffs.chat.cancel", () => chat.cancelCurrentRequest()),
2545
+ vscode17.commands.registerCommand("urInlineDiffs.chat.addFile", () => chat.addCurrentFileToChat()),
2546
+ vscode17.commands.registerCommand("urInlineDiffs.chat.addSelection", () => chat.addSelectionToChat()),
2547
+ vscode17.commands.registerCommand("urInlineDiffs.chat.explainSelection", () => chat.explainSelection()),
2548
+ vscode17.commands.registerCommand("urInlineDiffs.chat.fixSelection", () => chat.fixSelection()),
2549
+ vscode17.commands.registerCommand("urInlineDiffs.chat.generateTests", () => chat.generateTestsForSelection()),
2550
+ vscode17.commands.registerCommand("urInlineDiffs.agentStatus", () => showAgentStatus(workspaceRoot())),
2551
+ vscode17.commands.registerCommand("urInlineDiffs.agentOptions", () => showAgentOptions(workspaceRoot())),
2552
+ vscode17.commands.registerCommand("urInlineDiffs.reviewCurrentDiff", () => reviewCurrentDiff(chat)),
2553
+ vscode17.commands.registerCommand("urInlineDiffs.runVerifier", () => runVerifier(chat)),
2554
+ vscode17.commands.registerCommand("urInlineDiffs.searchActions", () => showSearchActions()),
2555
+ vscode17.commands.registerCommand("urInlineDiffs.pickModel", () => pickProviderModel(workspaceRoot())),
2556
+ vscode17.commands.registerCommand("urInlineDiffs.openSettings", () => openSettings()),
2557
+ vscode17.commands.registerCommand("urInlineDiffs.openDocs", () => openDocs()),
2558
+ vscode17.commands.registerCommand("urInlineDiffs.openArtifacts", () => openArtifacts()),
2559
+ vscode17.commands.registerCommand("urInlineDiffs.runSpec", () => runSpecAction(chat)),
2560
+ vscode17.commands.registerCommand("urInlineDiffs.runWorkflow", () => runWorkflowAction(chat)),
2561
+ vscode17.commands.registerCommand("urActions.refresh", () => actionsTreeProvider.refresh()),
2562
+ vscode17.commands.registerCommand("urActions.openBackgroundLog", (item) => openBackgroundLog(item))
2563
+ );
2564
+ }
2565
+ function deactivate() {
2566
+ }
2567
+ // Annotate the CommonJS export names for ESM import in node:
2568
+ 0 && (module.exports = {
2569
+ activate,
2570
+ deactivate
2571
+ });