@lasso-ai/cli 1.0.26 → 1.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/agent.js CHANGED
@@ -351,7 +351,7 @@ function localCommand(provider, model, prompt) {
351
351
  return { command: "claude", args: ["-p", prompt || "", "--output-format", "stream-json", "--verbose", "--permission-mode", "plan", "--max-turns", "3", ...(selectedModel ? ["--model", selectedModel] : [])] };
352
352
  }
353
353
  if (provider === "opencode") {
354
- return { command: "opencode", args: ["run", "--format", "json", "--print-logs", "--log-level", "INFO", "--agent", "plan", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
354
+ return { command: "opencode", args: ["run", "--format", "json", "--agent", "plan", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
355
355
  }
356
356
  return { command: "codex", args: ["exec", "--json", "--sandbox", "read-only", "--skip-git-repo-check", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
357
357
  }
@@ -394,7 +394,6 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress) {
394
394
  if (progress)
395
395
  onProgress?.(progress.message, progress.detail);
396
396
  else if (stream === "stderr" && line.trim()) {
397
- onProgress?.(`${label} · ${snippet(line, 110)}`);
398
397
  if (provider === "opencode" && /quota exceeded|authentication failed|invalid api key|unauthorized|forbidden/i.test(line)) {
399
398
  onProgress?.("OpenCode · provider rejected the request; stopping this task…");
400
399
  child.kill("SIGTERM");
package/dist/overlay.js CHANGED
@@ -10916,6 +10916,9 @@
10916
10916
  .lasso-task-badge { min-width: 18px; padding: 2px 5px; border-radius: 999px; background: var(--lo-indigo); color: #fff; font-size: 10px; text-align: center; }
10917
10917
  .lasso-task-close, .lasso-task-back { border: 0; background: transparent; color: var(--lo-text-3); cursor: pointer; }
10918
10918
  .lasso-task-close { font-size: 20px; line-height: 20px; }
10919
+ .lasso-task-tabs { display: flex; gap: 4px; margin: 10px 0 8px; padding: 3px; border-radius: var(--lo-radius-md); background: var(--lo-surface-2); }
10920
+ .lasso-task-tab { flex: 1; padding: 6px 8px; border: 0; border-radius: 7px; background: transparent; color: var(--lo-text-3); font: inherit; font-size: 11px; cursor: pointer; }
10921
+ .lasso-task-tab.active { background: var(--lo-surface); color: var(--lo-text); box-shadow: 0 1px 3px rgba(0,0,0,.16); }
10919
10922
  .lasso-task-body { min-height: 90px; max-height: 470px; overflow: auto; }
10920
10923
  .lasso-task-list { display: flex; flex-direction: column; gap: 3px; }
10921
10924
  .lasso-task-row { width: 100%; display: flex; align-items: center; gap: 9px; padding: 9px; border: 0; border-radius: var(--lo-radius-md); background: transparent; color: var(--lo-text); text-align: left; cursor: pointer; }
@@ -10928,6 +10931,7 @@
10928
10931
  .lasso-task-row-copy { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
10929
10932
  .lasso-task-row-copy strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 500; }
10930
10933
  .lasso-task-row-copy small, .lasso-task-message { color: var(--lo-text-3); font-size: 11px; }
10934
+ .lasso-task-review-label { margin-left: auto; padding: 4px 7px; border-radius: var(--lo-radius-full); background: color-mix(in srgb, #fbbf24 16%, transparent); color: #fbbf24; font-size: 10px; font-weight: 600; cursor: pointer; }
10931
10935
  .lasso-task-empty { padding: 25px 10px; color: var(--lo-text-3); font-size: 12px; text-align: center; }
10932
10936
  .lasso-task-detail { padding: 4px 2px; }
10933
10937
  .lasso-task-detail-heading { justify-content: space-between; margin-bottom: 12px; }
@@ -12128,6 +12132,7 @@
12128
12132
  var detail = null;
12129
12133
  var badge = null;
12130
12134
  var selectionListeners = /* @__PURE__ */ new Set();
12135
+ var reviewListeners = /* @__PURE__ */ new Set();
12131
12136
  var statusLabel = {
12132
12137
  queued: "Queued",
12133
12138
  thinking: "Thinking",
@@ -12146,13 +12151,29 @@
12146
12151
  return Boolean(other) && change.filePath === other.filePath && change.oldString === other.oldString && change.newString === other.newString;
12147
12152
  });
12148
12153
  }
12154
+ function normalizedInstruction(value2) {
12155
+ return value2.trim().replace(/\s+/g, " ").toLowerCase();
12156
+ }
12157
+ function findReusableAgentTask(instruction, element) {
12158
+ const normalized = normalizedInstruction(instruction);
12159
+ return state.agentTasks.find(
12160
+ (task) => task.status !== "complete" && task.status !== "error" && task.status !== "stopped" && normalizedInstruction(task.instruction) === normalized && task.element === (element || void 0)
12161
+ );
12162
+ }
12149
12163
  function onAgentTaskSelected(listener) {
12150
12164
  selectionListeners.add(listener);
12151
12165
  return () => selectionListeners.delete(listener);
12152
12166
  }
12167
+ function onAgentTaskReviewRequested(listener) {
12168
+ reviewListeners.add(listener);
12169
+ return () => reviewListeners.delete(listener);
12170
+ }
12153
12171
  function notifyTaskSelection(task) {
12154
12172
  for (const listener of selectionListeners) listener(task);
12155
12173
  }
12174
+ function notifyTaskReviewRequested(task) {
12175
+ for (const listener of reviewListeners) listener(task);
12176
+ }
12156
12177
  function createAgentTask(instruction, messages = state.chatHistory, changesHistory = state.changesHistory, element = state.selected, selectionId = state.selectionId) {
12157
12178
  const now = (/* @__PURE__ */ new Date()).toISOString();
12158
12179
  const task = {
@@ -12222,6 +12243,12 @@
12222
12243
  notifyTaskSelection(task || null);
12223
12244
  renderTasks();
12224
12245
  }
12246
+ function requestAgentTaskReview(id) {
12247
+ const task = getAgentTask(id);
12248
+ if (!task || task.status !== "review") return;
12249
+ selectAgentTask(task.id);
12250
+ notifyTaskReviewRequested(task);
12251
+ }
12225
12252
  function toggleTaskPanel(force) {
12226
12253
  if (!panel) return;
12227
12254
  panel.hidden = force === void 0 ? !panel.hidden : !force;
@@ -12241,6 +12268,10 @@
12241
12268
  </div>
12242
12269
  <button class="lasso-task-close" type="button" aria-label="Close tasks">\xD7</button>
12243
12270
  </div>
12271
+ <div class="lasso-task-tabs" role="tablist" aria-label="Task status">
12272
+ <button class="lasso-task-tab active" type="button" role="tab" data-task-tab="active" aria-selected="true">In progress</button>
12273
+ <button class="lasso-task-tab" type="button" role="tab" data-task-tab="complete" aria-selected="false">Completed</button>
12274
+ </div>
12244
12275
  <div class="lasso-task-body">
12245
12276
  <div class="lasso-task-list"></div>
12246
12277
  <div class="lasso-task-detail" hidden></div>
@@ -12251,27 +12282,47 @@
12251
12282
  detail = panel.querySelector(".lasso-task-detail");
12252
12283
  badge = panel.querySelector(".lasso-task-badge");
12253
12284
  panel.querySelector(".lasso-task-close").addEventListener("click", () => toggleTaskPanel(false));
12285
+ panel.querySelectorAll(".lasso-task-tab").forEach((tab) => {
12286
+ tab.addEventListener("click", () => {
12287
+ panel?.querySelectorAll(".lasso-task-tab").forEach((item) => {
12288
+ const active = item === tab;
12289
+ item.classList.toggle("active", active);
12290
+ item.setAttribute("aria-selected", String(active));
12291
+ });
12292
+ renderTasks();
12293
+ });
12294
+ });
12254
12295
  renderTasks();
12255
12296
  return panel;
12256
12297
  }
12257
12298
  function renderTasks() {
12258
- if (!list || !detail || !badge) return;
12299
+ const panelEl = panel;
12300
+ if (!panelEl || !list || !detail || !badge) return;
12259
12301
  const activeCount = state.agentTasks.filter((task) => task.status !== "complete" && task.status !== "error" && task.status !== "stopped").length;
12260
12302
  badge.hidden = state.agentTasks.length === 0;
12261
12303
  badge.textContent = String(activeCount || state.agentTasks.length);
12262
12304
  const navBadge = getDOM().shadow.querySelector(".lasso-agent-tasks-badge");
12263
12305
  if (navBadge) navBadge.hidden = activeCount === 0;
12264
12306
  list.replaceChildren();
12265
- if (!state.agentTasks.length) {
12307
+ const activeTab = panelEl.querySelector(".lasso-task-tab.active")?.dataset.taskTab || "active";
12308
+ const visibleTasks = state.agentTasks.filter((task) => activeTab === "complete" ? task.status === "complete" : task.status !== "complete");
12309
+ if (!visibleTasks.length) {
12266
12310
  list.innerHTML = `<div class="lasso-task-empty">Prompt an element to start a task.</div>`;
12267
12311
  } else {
12268
- for (const task of state.agentTasks) {
12312
+ for (const task of visibleTasks) {
12269
12313
  const button = document.createElement("button");
12270
12314
  button.type = "button";
12271
12315
  button.className = `lasso-task-row${task.id === state.activeTaskId ? " active" : ""}`;
12272
- button.innerHTML = `<span class="lasso-task-status ${task.status}"></span><span class="lasso-task-row-copy"><strong></strong><small>${statusLabel[task.status]}</small></span>`;
12316
+ button.innerHTML = `<span class="lasso-task-status ${task.status}"></span><span class="lasso-task-row-copy"><strong></strong><small>${statusLabel[task.status]}</small></span>${task.status === "review" ? '<span class="lasso-task-review-label">Review</span>' : ""}`;
12273
12317
  button.querySelector("strong").textContent = task.instruction;
12274
12318
  button.addEventListener("click", () => selectAgentTask(task.id));
12319
+ if (task.status === "review") {
12320
+ const review = button.querySelector(".lasso-task-review-label");
12321
+ review.addEventListener("click", (event) => {
12322
+ event.stopPropagation();
12323
+ requestAgentTaskReview(task.id);
12324
+ });
12325
+ }
12275
12326
  list.appendChild(button);
12276
12327
  }
12277
12328
  }
@@ -12310,16 +12361,12 @@
12310
12361
  changes.textContent = `${pendingChanges.length} proposed file change${pendingChanges.length === 1 ? "" : "s"}`;
12311
12362
  detail.appendChild(changes);
12312
12363
  if (selected.status === "review") {
12313
- const apply = document.createElement("button");
12314
- apply.type = "button";
12315
- apply.className = "lasso-task-apply";
12316
- apply.textContent = "Apply changes";
12317
- apply.addEventListener("click", () => {
12318
- if (state.bridgeSocket?.readyState !== WebSocket.OPEN) return;
12319
- state.bridgeSocket.send(JSON.stringify({ type: "apply", taskId: selected.id, changes: pendingChanges }));
12320
- updateAgentTask(selected.id, { status: "working", message: "Applying changes\u2026", activity: [...selected.activity, "Applying changes\u2026"] });
12321
- });
12322
- detail.appendChild(apply);
12364
+ const review = document.createElement("button");
12365
+ review.type = "button";
12366
+ review.className = "lasso-task-apply";
12367
+ review.textContent = "Review changes";
12368
+ review.addEventListener("click", () => requestAgentTaskReview(selected.id));
12369
+ detail.appendChild(review);
12323
12370
  }
12324
12371
  }
12325
12372
  }
@@ -18466,6 +18513,9 @@
18466
18513
  cli: terminal_default
18467
18514
  };
18468
18515
  function providerIcon(provider, active = false) {
18516
+ if (provider === "cli") {
18517
+ return `<span class="${active ? "lasso-model-active-icon" : "lasso-model-item-icon"} provider-cli" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" focusable="false"><circle cx="12" cy="12" r="9" fill="currentColor" opacity=".16"/><circle cx="12" cy="9" r="3" fill="currentColor"/><path d="M6.5 18c.9-2.4 2.7-3.6 5.5-3.6s4.6 1.2 5.5 3.6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></span>`;
18518
+ }
18469
18519
  const icon = providerIcons[provider];
18470
18520
  return `<span class="${active ? "lasso-model-active-icon" : "lasso-model-item-icon"} provider-${provider}" aria-hidden="true"><svg viewBox="0 0 ${icon.width} ${icon.height}" xmlns="http://www.w3.org/2000/svg" focusable="false">${icon.body}</svg></span>`;
18471
18521
  }
@@ -18591,7 +18641,7 @@
18591
18641
  <div class="lasso-review-files"></div>
18592
18642
  <div class="lasso-review-actions">
18593
18643
  <button class="lasso-review-undo" type="button">Keep editing</button>
18594
- <button class="lasso-review-apply primary" type="button">Apply changes</button>
18644
+ <button class="lasso-review-apply primary" type="button">Accept &amp; apply</button>
18595
18645
  </div>
18596
18646
  `;
18597
18647
  dom.shadow.appendChild(rev);
@@ -18600,6 +18650,11 @@
18600
18650
  if (task) restoreTaskPrompt(task);
18601
18651
  else resetPromptSession();
18602
18652
  });
18653
+ onAgentTaskReviewRequested((task) => {
18654
+ state.promptTaskId = task.id;
18655
+ state.pendingChanges = [...task.pendingChanges.length ? task.pendingChanges : task.changes || []];
18656
+ showReview(state.pendingChanges, task.message);
18657
+ });
18603
18658
  const promptTop = el.querySelector(".lasso-prompt-top");
18604
18659
  let dragState = null;
18605
18660
  promptTop.addEventListener("pointerdown", (event) => {
@@ -18745,6 +18800,11 @@
18745
18800
  if (!taskId2 || !state.bridgeSocket || state.bridgeSocket.readyState !== WebSocket.OPEN || !state.pendingChanges.length) return;
18746
18801
  requestAgentNotificationPermission();
18747
18802
  state.bridgeSocket.send(JSON.stringify({ type: "apply", taskId: taskId2, changes: state.pendingChanges }));
18803
+ updateAgentTask(taskId2, {
18804
+ status: "working",
18805
+ message: "Applying accepted changes\u2026",
18806
+ activity: [...getAgentTask(taskId2)?.activity || [], "Applying accepted changes\u2026"]
18807
+ });
18748
18808
  appendChat("assistant", "Applying the reviewed change\u2026", taskId2);
18749
18809
  });
18750
18810
  return { prompt: el, review: rev };
@@ -19092,7 +19152,6 @@
19092
19152
  }
19093
19153
  if (promptEl) promptEl.classList.add("visible");
19094
19154
  setSelectMode(false);
19095
- if (task.status === "review" && state.pendingChanges.length) showReview(state.pendingChanges, task.message);
19096
19155
  setAgentStatus(task.status, task.message, task.detail, task.id, false);
19097
19156
  requestAnimationFrame(() => promptInput?.focus());
19098
19157
  }
@@ -19211,6 +19270,11 @@
19211
19270
  const selectionId = state.selectionId;
19212
19271
  const model = state.selectedModel;
19213
19272
  const screenshotPromise = state.screenshotPromise;
19273
+ const reusableTask = findReusableAgentTask(instruction, selectedElement);
19274
+ if (reusableTask) {
19275
+ selectAgentTask(reusableTask.id);
19276
+ return;
19277
+ }
19214
19278
  appendChat("user", instruction);
19215
19279
  const messages = state.chatHistory.map((message) => ({ ...message }));
19216
19280
  const changesHistory = state.changesHistory.map((entry) => ({ ...entry, changes: entry.changes.map((change) => ({ ...change })) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lasso-ai/cli",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "Select any part of your running app, describe a change, and let AI edit the real source code.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cli/index.js",