@lasso-ai/cli 1.0.17 → 1.0.19

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
@@ -193,6 +193,39 @@ function extractLocalAgentText(raw, provider) {
193
193
  }
194
194
  return texts.at(-1) || raw;
195
195
  }
196
+ function extractLocalAgentProposal(raw, provider) {
197
+ const outputs = [];
198
+ for (const line of raw.split(/\r?\n/)) {
199
+ try {
200
+ const event = JSON.parse(line);
201
+ const item = event.item;
202
+ const part = event.part;
203
+ const values = provider === "claude-code"
204
+ ? [event.result, ...(event.message?.content || []).map((entry) => entry.text)]
205
+ : provider === "opencode"
206
+ ? [part?.type === "text" ? part.text : undefined, event.text, event.output_text]
207
+ : [event.text, event.output_text, item?.text, item?.output_text, item?.message];
208
+ for (const value of values)
209
+ if (typeof value === "string" && value.trim())
210
+ outputs.push(value);
211
+ }
212
+ catch {
213
+ if (line.trim())
214
+ outputs.push(line);
215
+ }
216
+ }
217
+ // Tool events are interleaved with the final answer. Try text events in
218
+ // reverse order, then the complete stream as a final fallback.
219
+ for (const output of [...outputs.reverse(), raw]) {
220
+ try {
221
+ return jsonFrom(output);
222
+ }
223
+ catch {
224
+ // Keep looking; this output may only be a progress or tool event.
225
+ }
226
+ }
227
+ throw new Error("The agent returned no valid reviewable changes. Progress output may have been mixed with the final JSON.");
228
+ }
196
229
  function snippet(value, max = 80) {
197
230
  const s = String(value ?? "").trim().replace(/\s+/g, " ");
198
231
  return s.length > max ? `${s.slice(0, max)}…` : s;
@@ -309,7 +342,7 @@ function localCommand(provider, model, prompt) {
309
342
  return { command: "claude", args: ["-p", prompt || "", "--output-format", "stream-json", "--verbose", "--permission-mode", "plan", "--max-turns", "3", ...(selectedModel ? ["--model", selectedModel] : [])] };
310
343
  }
311
344
  if (provider === "opencode") {
312
- return { command: "opencode", args: ["run", "--format", "json", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
345
+ return { command: "opencode", args: ["run", "--format", "json", "--agent", "plan", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
313
346
  }
314
347
  return { command: "codex", args: ["exec", "--json", "--sandbox", "read-only", "--skip-git-repo-check", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
315
348
  }
@@ -355,7 +388,7 @@ async function proposeWithLocalAgent(cwd, instruction, context, config, signal,
355
388
  }
356
389
  if (exitCode !== 0)
357
390
  throw new Error(localAgentError(command, stderr, exitCode));
358
- return jsonFrom(extractLocalAgentText(stdout, config.provider));
391
+ return extractLocalAgentProposal(stdout, config.provider);
359
392
  }
360
393
  async function proposeChanges(cwd, input, config, signal, onProgress) {
361
394
  const context = await contextFor(cwd, input.element);
@@ -14,6 +14,7 @@ export type BridgeMessage = {
14
14
  from: "overlay" | "cli";
15
15
  } | {
16
16
  type: "edit";
17
+ taskId: string;
17
18
  instruction: string;
18
19
  model: string;
19
20
  provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
@@ -48,6 +49,7 @@ export type BridgeMessage = {
48
49
  };
49
50
  } | {
50
51
  type: "ask";
52
+ taskId: string;
51
53
  question: string;
52
54
  model: string;
53
55
  provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
@@ -81,11 +83,14 @@ export type BridgeMessage = {
81
83
  details: string;
82
84
  } | {
83
85
  type: "apply";
86
+ taskId: string;
84
87
  changes: SourceChange[];
85
88
  } | {
86
89
  type: "undo";
90
+ taskId?: string;
87
91
  } | {
88
92
  type: "stop";
93
+ taskId?: string;
89
94
  } | {
90
95
  type: "git_status";
91
96
  } | {
@@ -101,6 +106,7 @@ export type BridgeMessage = {
101
106
  type: "git_push";
102
107
  } | {
103
108
  type: "agent_status";
109
+ taskId?: string;
104
110
  status: "thinking" | "working" | "review" | "error" | "stopped";
105
111
  message: string;
106
112
  } | {
@@ -134,18 +140,22 @@ export type ServerBridgeMessage = {
134
140
  error?: string;
135
141
  } | {
136
142
  type: "agent_status";
143
+ taskId?: string;
137
144
  status: "thinking" | "working" | "review" | "error" | "stopped";
138
145
  message: string;
139
146
  detail?: string;
140
147
  changes?: SourceChange[];
141
148
  } | {
142
149
  type: "assistant_message";
150
+ taskId?: string;
143
151
  message: string;
144
152
  } | {
145
153
  type: "applied";
154
+ taskId?: string;
146
155
  message: string;
147
156
  } | {
148
157
  type: "undone";
158
+ taskId?: string;
149
159
  message: string;
150
160
  } | {
151
161
  type: "transcribe_result";
@@ -147,10 +147,10 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
147
147
  const bridgeServer = node_http_1.default.createServer(); // dedicated, empty HTTP server
148
148
  const wss = new ws_1.WebSocketServer({ server: bridgeServer });
149
149
  let overlaySocket = null;
150
- let lastSnapshot = [];
151
- let lastEditRequest = null;
152
- let lastEditConfig = null;
153
- let reviewRefreshAttempts = 0;
150
+ const taskSnapshots = new Map();
151
+ const editRequests = new Map();
152
+ const editConfigs = new Map();
153
+ const reviewRefreshAttempts = new Map();
154
154
  const envRoots = [cwd, node_path_1.default.join(cwd, "web"), node_path_1.default.join(cwd, "server")];
155
155
  const fileEnv = envRoots.reduce((values, root) => ({
156
156
  ...values,
@@ -179,11 +179,15 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
179
179
  };
180
180
  })();
181
181
  let activeAgentController = null;
182
+ const taskControllers = new Map();
182
183
  let localAgents = new Set();
183
184
  bridgeServer.on("request", (req, res) => {
184
185
  if (req.method !== "POST" || req.url?.split("?", 1)[0] !== "/__lasso/bridge/restart")
185
186
  return;
186
187
  activeAgentController?.abort();
188
+ for (const controller of taskControllers.values())
189
+ controller.abort();
190
+ taskControllers.clear();
187
191
  activeAgentController = null;
188
192
  for (const socket of wss.clients)
189
193
  socket.close(1000, "Bridge restarted by the CLI");
@@ -270,40 +274,41 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
270
274
  socket.send(JSON.stringify({ type: "git_state", git }));
271
275
  });
272
276
  const runEditReview = (request, config, statusMessage) => {
273
- activeAgentController?.abort();
277
+ taskControllers.get(request.taskId)?.abort();
274
278
  const controller = new AbortController();
275
- activeAgentController = controller;
279
+ taskControllers.set(request.taskId, controller);
276
280
  if (statusMessage && socket.readyState === socket.OPEN) {
277
- socket.send(JSON.stringify({ type: "agent_status", status: "working", message: statusMessage }));
281
+ socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "working", message: statusMessage }));
278
282
  }
279
283
  void (0, agent_1.proposeChanges)(cwd, request, config, controller.signal, (message, detail) => {
280
284
  if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
281
- socket.send(JSON.stringify({ type: "agent_status", status: "working", message, detail }));
285
+ socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "working", message, detail }));
282
286
  }
283
287
  })
284
288
  .then((proposal) => {
285
289
  if (controller.signal.aborted || socket.readyState !== socket.OPEN)
286
290
  return;
287
291
  if (!proposalMatchesCurrentSource(cwd, proposal.changes)) {
288
- if (reviewRefreshAttempts < 1) {
289
- reviewRefreshAttempts += 1;
292
+ const refreshAttempts = reviewRefreshAttempts.get(request.taskId) || 0;
293
+ if (refreshAttempts < 1) {
294
+ reviewRefreshAttempts.set(request.taskId, refreshAttempts + 1);
290
295
  runEditReview(request, config, "The source changed while the proposal was being prepared. Refreshing the review…");
291
296
  }
292
297
  else {
293
- socket.send(JSON.stringify({ type: "agent_status", status: "error", message: "The source is still changing. Stop the dev-server edit or try the request again." }));
298
+ socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "error", message: "The source is still changing. Stop the dev-server edit or try the request again." }));
294
299
  }
295
300
  return;
296
301
  }
297
- socket.send(JSON.stringify({ type: "agent_status", status: "review", message: proposal.summary, changes: proposal.changes }));
302
+ socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "review", message: proposal.summary, changes: proposal.changes }));
298
303
  })
299
304
  .catch((error) => {
300
305
  if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
301
- socket.send(JSON.stringify({ type: "agent_status", status: "error", message: error instanceof Error ? error.message : "The agent could not prepare a change." }));
306
+ socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "error", message: error instanceof Error ? error.message : "The agent could not prepare a change." }));
302
307
  }
303
308
  })
304
309
  .finally(() => {
305
- if (activeAgentController === controller)
306
- activeAgentController = null;
310
+ if (taskControllers.get(request.taskId) === controller)
311
+ taskControllers.delete(request.taskId);
307
312
  });
308
313
  };
309
314
  socket.on("message", async (raw) => {
@@ -318,56 +323,59 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
318
323
  const localProvider = msg.provider === "cli";
319
324
  const cliProvider = msg.model.startsWith("claude-code:") ? "claude-code" : msg.model.startsWith("opencode:") ? "opencode" : "codex";
320
325
  if (!lassoKeyConfigured && !localProvider) {
321
- socket.send(JSON.stringify({ type: "agent_status", status: "error", message: "Set your Lasso API key before asking the hosted agent a question." }));
326
+ socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "error", message: "Set your Lasso API key before asking the hosted agent a question." }));
322
327
  return;
323
328
  }
324
329
  if (!agentConfig && !localProvider) {
325
- socket.send(JSON.stringify({ type: "agent_status", status: "error", message: "No hosted agent is configured. Select Claude Code/Codex or add a provider key." }));
330
+ socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "error", message: "No hosted agent is configured. Select Claude Code/Codex or add a provider key." }));
326
331
  return;
327
332
  }
328
- activeAgentController?.abort();
333
+ taskControllers.get(msg.taskId)?.abort();
329
334
  const controller = new AbortController();
330
- activeAgentController = controller;
335
+ taskControllers.set(msg.taskId, controller);
331
336
  const selectedConfig = localProvider
332
337
  ? { provider: cliProvider, model: msg.model }
333
338
  : { ...agentConfig, provider: (msg.provider || agentConfig.provider), model: msg.model };
334
339
  void (0, agent_1.answerQuestion)(cwd, { question: msg.question, context: msg.context, element: msg.element, messages: msg.messages }, selectedConfig, controller.signal, (message, detail) => {
335
340
  if (!controller.signal.aborted && socket.readyState === socket.OPEN)
336
- socket.send(JSON.stringify({ type: "agent_status", status: "working", message, detail }));
341
+ socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "working", message, detail }));
337
342
  }).then((answer) => {
338
343
  if (!controller.signal.aborted)
339
- socket.send(JSON.stringify({ type: "assistant_message", message: answer }));
344
+ socket.send(JSON.stringify({ type: "assistant_message", taskId: msg.taskId, message: answer }));
340
345
  }).catch((error) => {
341
346
  if (!controller.signal.aborted)
342
- socket.send(JSON.stringify({ type: "agent_status", status: "error", message: error instanceof Error ? error.message : "The agent could not answer." }));
347
+ socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "error", message: error instanceof Error ? error.message : "The agent could not answer." }));
343
348
  }).finally(() => {
344
- if (activeAgentController === controller)
345
- activeAgentController = null;
349
+ if (taskControllers.get(msg.taskId) === controller)
350
+ taskControllers.delete(msg.taskId);
346
351
  });
347
352
  }
348
353
  else if (msg.type === "edit") {
349
354
  const localProvider = msg.provider === "cli";
350
355
  const cliProvider = msg.model.startsWith("claude-code:") ? "claude-code" : msg.model.startsWith("opencode:") ? "opencode" : "codex";
351
356
  if (!lassoKeyConfigured && !localProvider) {
352
- socket.send(JSON.stringify({ type: "agent_status", status: "error", message: "Set VITE_LASSO_API_KEY or NEXT_LASSO_API_KEY in your app environment before sending an edit." }));
357
+ socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "error", message: "Set VITE_LASSO_API_KEY or NEXT_LASSO_API_KEY in your app environment before sending an edit." }));
353
358
  return;
354
359
  }
355
360
  if (!agentConfig && !localProvider) {
356
- socket.send(JSON.stringify({ type: "agent_status", status: "error", message: "Add a supported agent key: GOOGLE_GENERATIVE_AI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY." }));
361
+ socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "error", message: "Add a supported agent key: GOOGLE_GENERATIVE_AI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY." }));
357
362
  return;
358
363
  }
359
364
  const selectedConfig = localProvider
360
365
  ? { provider: cliProvider, model: msg.model }
361
366
  : { ...agentConfig, provider: (msg.provider || agentConfig.provider), model: msg.model };
362
- lastEditRequest = msg;
363
- lastEditConfig = selectedConfig;
364
- reviewRefreshAttempts = 0;
367
+ editRequests.set(msg.taskId, msg);
368
+ editConfigs.set(msg.taskId, selectedConfig);
369
+ reviewRefreshAttempts.set(msg.taskId, 0);
365
370
  runEditReview(msg, selectedConfig);
366
371
  }
367
372
  else if (msg.type === "stop") {
368
- activeAgentController?.abort();
369
- activeAgentController = null;
370
- socket.send(JSON.stringify({ type: "agent_status", status: "stopped", message: "Agent stopped." }));
373
+ if (msg.taskId)
374
+ taskControllers.get(msg.taskId)?.abort();
375
+ else
376
+ for (const controller of taskControllers.values())
377
+ controller.abort();
378
+ socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "stopped", message: "Agent stopped." }));
371
379
  }
372
380
  else if (msg.type === "runtime_error") {
373
381
  socket.send(JSON.stringify({ type: "agent_status", status: "error", message: `Runtime error detected${msg.selectionId ? ` for selection ${msg.selectionId}` : ""}: ${msg.details}` }));
@@ -468,7 +476,7 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
468
476
  }
469
477
  else if (msg.type === "apply") {
470
478
  try {
471
- lastSnapshot = [];
479
+ const snapshots = [];
472
480
  const planned = new Map();
473
481
  for (const change of msg.changes) {
474
482
  const filePath = resolveProposedFile(cwd, change.filePath);
@@ -483,24 +491,31 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
483
491
  }
484
492
  // Validate every change before writing any file, then apply each
485
493
  // file's replacements from the end toward the beginning.
494
+ taskSnapshots.set(msg.taskId, snapshots);
486
495
  for (const [filePath, changes] of planned) {
487
496
  const content = changes[0].content;
488
- lastSnapshot.push({ filePath, content });
497
+ snapshots.push({ filePath, content });
489
498
  const nextContent = [...changes]
490
499
  .sort((a, b) => b.start - a.start)
491
500
  .reduce((value, change) => value.slice(0, change.start) + change.newString + value.slice(change.end), content);
492
501
  node_fs_1.default.writeFileSync(filePath, nextContent);
493
502
  }
494
- socket.send(JSON.stringify({ type: "applied", message: `${msg.changes.length} file${msg.changes.length === 1 ? "" : "s"} updated. Your dev server will reload.` }));
503
+ socket.send(JSON.stringify({ type: "applied", taskId: msg.taskId, message: `${msg.changes.length} file${msg.changes.length === 1 ? "" : "s"} updated. Your dev server will reload.` }));
495
504
  }
496
505
  catch (error) {
497
- for (const snapshot of lastSnapshot)
506
+ // Validation happens before writes, but restore this task's snapshot
507
+ // if a filesystem error occurs during the write phase.
508
+ for (const snapshot of taskSnapshots.get(msg.taskId) || [])
498
509
  node_fs_1.default.writeFileSync(snapshot.filePath, snapshot.content);
499
- lastSnapshot = [];
510
+ taskSnapshots.delete(msg.taskId);
500
511
  const message = error instanceof Error ? error.message : "The change could not be applied.";
501
512
  const sourceChanged = message.includes("The source changed after the suggestion was generated");
502
- if (sourceChanged && lastEditRequest && lastEditConfig && reviewRefreshAttempts < 1) {
503
- runEditReview(lastEditRequest, lastEditConfig, "The source changed. Refreshing the review against the current file…");
513
+ const editRequest = editRequests.get(msg.taskId);
514
+ const editConfig = editConfigs.get(msg.taskId);
515
+ const refreshAttempts = reviewRefreshAttempts.get(msg.taskId) || 0;
516
+ if (sourceChanged && editRequest && editConfig && refreshAttempts < 1) {
517
+ reviewRefreshAttempts.set(msg.taskId, refreshAttempts + 1);
518
+ runEditReview(editRequest, editConfig, "The source changed. Refreshing the review against the current file…");
504
519
  }
505
520
  else {
506
521
  socket.send(JSON.stringify({ type: "agent_status", status: "error", message }));
@@ -508,11 +523,11 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
508
523
  }
509
524
  }
510
525
  else if (msg.type === "undo") {
511
- for (const snapshot of lastSnapshot)
526
+ const snapshots = taskSnapshots.get(msg.taskId || "") || [];
527
+ for (const snapshot of snapshots)
512
528
  node_fs_1.default.writeFileSync(snapshot.filePath, snapshot.content);
513
- const count = lastSnapshot.length;
514
- lastSnapshot = [];
515
- socket.send(JSON.stringify({ type: "undone", message: count ? "The accepted change was reverted." : "There is no accepted change to undo." }));
529
+ taskSnapshots.delete(msg.taskId || "");
530
+ socket.send(JSON.stringify({ type: "undone", taskId: msg.taskId, message: snapshots.length ? "The accepted change was reverted." : "There is no accepted change to undo." }));
516
531
  }
517
532
  });
518
533
  socket.on("close", () => {
package/dist/overlay.js CHANGED
@@ -7878,6 +7878,8 @@
7878
7878
  chatHistory = [];
7879
7879
  changesHistory = [];
7880
7880
  pendingChanges = [];
7881
+ agentTasks = [];
7882
+ activeTaskId = null;
7881
7883
  runtimeErrors = [];
7882
7884
  screenshotPromise = Promise.resolve({});
7883
7885
  // Agent State
@@ -10180,8 +10182,13 @@
10180
10182
  display: none;
10181
10183
  }
10182
10184
 
10185
+ .lasso-prompt-send.loading span {
10186
+ display: none;
10187
+ }
10188
+
10183
10189
  .lasso-prompt-send.loading::before {
10184
10190
  content: "";
10191
+ display: block;
10185
10192
  width: 12px;
10186
10193
  height: 12px;
10187
10194
  border: 2px solid rgba(0, 0, 0, 0.3);
@@ -10877,6 +10884,60 @@
10877
10884
  display: none;
10878
10885
  }
10879
10886
 
10887
+ .lasso-task-panel {
10888
+ position: fixed;
10889
+ right: 12px;
10890
+ bottom: 82px;
10891
+ width: 390px;
10892
+ max-width: calc(100vw - 24px);
10893
+ max-height: min(560px, calc(100vh - 104px));
10894
+ padding: 14px;
10895
+ display: flex;
10896
+ flex-direction: column;
10897
+ gap: 10px;
10898
+ color: var(--lo-text);
10899
+ background: var(--lo-surface);
10900
+ border: 1px solid var(--lo-border);
10901
+ border-radius: var(--lo-radius-xl);
10902
+ box-shadow: 0 18px 50px rgba(0,0,0,.35);
10903
+ z-index: 30;
10904
+ opacity: 0;
10905
+ transform: translateY(8px) scale(.98);
10906
+ transition: opacity 150ms ease, transform 150ms ease;
10907
+ pointer-events: auto;
10908
+ }
10909
+
10910
+ .lasso-task-panel.visible { opacity: 1; transform: translateY(0) scale(1); }
10911
+ .lasso-task-panel[hidden] { display: none; }
10912
+ .lasso-task-header, .lasso-task-title-row, .lasso-task-detail-heading { display: flex; align-items: center; }
10913
+ .lasso-task-header { justify-content: space-between; }
10914
+ .lasso-task-title-row { gap: 7px; font-size: 13px; font-weight: 600; }
10915
+ .lasso-task-badge { min-width: 18px; padding: 2px 5px; border-radius: 999px; background: var(--lo-indigo); color: #fff; font-size: 10px; text-align: center; }
10916
+ .lasso-task-close, .lasso-task-back { border: 0; background: transparent; color: var(--lo-text-3); cursor: pointer; }
10917
+ .lasso-task-close { font-size: 20px; line-height: 20px; }
10918
+ .lasso-task-body { min-height: 90px; max-height: 470px; overflow: auto; }
10919
+ .lasso-task-list { display: flex; flex-direction: column; gap: 3px; }
10920
+ .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; }
10921
+ .lasso-task-row:hover, .lasso-task-row.active { background: var(--lo-surface-hover); }
10922
+ .lasso-task-status { width: 8px; height: 8px; flex: 0 0 auto; border-radius: 50%; background: var(--lo-text-3); }
10923
+ .lasso-task-status.thinking, .lasso-task-status.working { background: var(--lo-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--lo-primary) 18%, transparent); }
10924
+ .lasso-task-status.review { background: #fbbf24; }
10925
+ .lasso-task-status.complete { background: #34d399; }
10926
+ .lasso-task-status.error { background: #f87171; }
10927
+ .lasso-task-row-copy { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
10928
+ .lasso-task-row-copy strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 500; }
10929
+ .lasso-task-row-copy small, .lasso-task-message { color: var(--lo-text-3); font-size: 11px; }
10930
+ .lasso-task-empty { padding: 25px 10px; color: var(--lo-text-3); font-size: 12px; text-align: center; }
10931
+ .lasso-task-detail { padding: 4px 2px; }
10932
+ .lasso-task-detail-heading { justify-content: space-between; margin-bottom: 12px; }
10933
+ .lasso-task-detail-status { color: var(--lo-primary); font-size: 11px; font-weight: 600; }
10934
+ .lasso-task-prompt { margin: 0 0 10px; font-size: 13px; line-height: 1.45; }
10935
+ .lasso-task-message { margin: 0 0 12px; line-height: 1.45; }
10936
+ .lasso-task-changes { margin-bottom: 10px; color: var(--lo-text-2); font-size: 11px; }
10937
+ .lasso-task-apply { padding: 7px 11px; border: 0; border-radius: var(--lo-radius-full); background: var(--lo-primary); color: #111214; font: inherit; font-size: 11px; font-weight: 600; cursor: pointer; }
10938
+
10939
+ .lasso-agent-tasks-badge { position: absolute; top: 5px; right: 5px; width: 6px; height: 6px; border-radius: 50%; background: var(--lo-primary); }
10940
+
10880
10941
  .lasso-todo-header {
10881
10942
  display: flex;
10882
10943
  align-items: center;
@@ -12059,6 +12120,138 @@
12059
12120
  };
12060
12121
  var terminal_default = data4;
12061
12122
 
12123
+ // src/overlay/tasks/tasks.ts
12124
+ var panel = null;
12125
+ var list = null;
12126
+ var detail = null;
12127
+ var badge = null;
12128
+ var statusLabel = {
12129
+ queued: "Queued",
12130
+ thinking: "Thinking",
12131
+ working: "Working",
12132
+ review: "Review",
12133
+ complete: "Complete",
12134
+ error: "Error",
12135
+ stopped: "Stopped"
12136
+ };
12137
+ function taskId() {
12138
+ return `task-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
12139
+ }
12140
+ function createAgentTask(instruction) {
12141
+ const now = (/* @__PURE__ */ new Date()).toISOString();
12142
+ const task = { id: taskId(), instruction, status: "queued", message: "Queued", createdAt: now, updatedAt: now };
12143
+ state.agentTasks.unshift(task);
12144
+ state.activeTaskId = task.id;
12145
+ renderTasks();
12146
+ return task;
12147
+ }
12148
+ function getAgentTask(id) {
12149
+ return id ? state.agentTasks.find((task) => task.id === id) : void 0;
12150
+ }
12151
+ function updateAgentTask(id, patch) {
12152
+ if (!id) return;
12153
+ const task = getAgentTask(id);
12154
+ if (!task) return;
12155
+ Object.assign(task, patch, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
12156
+ renderTasks();
12157
+ }
12158
+ function selectAgentTask(id) {
12159
+ if (!getAgentTask(id)) return;
12160
+ state.activeTaskId = id;
12161
+ renderTasks();
12162
+ }
12163
+ function toggleTaskPanel(force) {
12164
+ if (!panel) return;
12165
+ panel.hidden = force === void 0 ? !panel.hidden : !force;
12166
+ panel.classList.toggle("visible", !panel.hidden);
12167
+ if (!panel.hidden) renderTasks();
12168
+ }
12169
+ function buildTaskPanel() {
12170
+ const dom = getDOM();
12171
+ panel = document.createElement("div");
12172
+ panel.className = "lasso-task-panel";
12173
+ panel.hidden = true;
12174
+ panel.innerHTML = `
12175
+ <div class="lasso-task-header">
12176
+ <div class="lasso-task-title-row">
12177
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M7 8h10M7 12h6M7 16h8"/></svg>
12178
+ <span>Agent tasks</span><span class="lasso-task-badge" hidden>0</span>
12179
+ </div>
12180
+ <button class="lasso-task-close" type="button" aria-label="Close tasks">\xD7</button>
12181
+ </div>
12182
+ <div class="lasso-task-body">
12183
+ <div class="lasso-task-list"></div>
12184
+ <div class="lasso-task-detail" hidden></div>
12185
+ </div>
12186
+ `;
12187
+ dom.shadow.appendChild(panel);
12188
+ list = panel.querySelector(".lasso-task-list");
12189
+ detail = panel.querySelector(".lasso-task-detail");
12190
+ badge = panel.querySelector(".lasso-task-badge");
12191
+ panel.querySelector(".lasso-task-close").addEventListener("click", () => toggleTaskPanel(false));
12192
+ renderTasks();
12193
+ return panel;
12194
+ }
12195
+ function renderTasks() {
12196
+ if (!list || !detail || !badge) return;
12197
+ const running = state.agentTasks.filter((task) => task.status === "thinking" || task.status === "working").length;
12198
+ badge.hidden = state.agentTasks.length === 0;
12199
+ badge.textContent = String(running || state.agentTasks.length);
12200
+ list.replaceChildren();
12201
+ if (!state.agentTasks.length) {
12202
+ list.innerHTML = `<div class="lasso-task-empty">Prompt an element to start a task.</div>`;
12203
+ } else {
12204
+ for (const task of state.agentTasks) {
12205
+ const button = document.createElement("button");
12206
+ button.type = "button";
12207
+ button.className = `lasso-task-row${task.id === state.activeTaskId ? " active" : ""}`;
12208
+ button.innerHTML = `<span class="lasso-task-status ${task.status}"></span><span class="lasso-task-row-copy"><strong></strong><small>${statusLabel[task.status]}</small></span>`;
12209
+ button.querySelector("strong").textContent = task.instruction;
12210
+ button.addEventListener("click", () => selectAgentTask(task.id));
12211
+ list.appendChild(button);
12212
+ }
12213
+ }
12214
+ const selected = getAgentTask(state.activeTaskId);
12215
+ if (!selected) {
12216
+ detail.hidden = true;
12217
+ return;
12218
+ }
12219
+ detail.hidden = false;
12220
+ detail.replaceChildren();
12221
+ const heading = document.createElement("div");
12222
+ heading.className = "lasso-task-detail-heading";
12223
+ heading.innerHTML = `<span class="lasso-task-detail-status ${selected.status}">${statusLabel[selected.status]}</span><button type="button" class="lasso-task-back">All tasks</button>`;
12224
+ heading.querySelector("button").addEventListener("click", () => {
12225
+ state.activeTaskId = null;
12226
+ renderTasks();
12227
+ });
12228
+ const prompt = document.createElement("p");
12229
+ prompt.className = "lasso-task-prompt";
12230
+ prompt.textContent = selected.instruction;
12231
+ const activity = document.createElement("p");
12232
+ activity.className = "lasso-task-message";
12233
+ activity.textContent = selected.response || selected.message;
12234
+ detail.append(heading, prompt, activity);
12235
+ if (selected.changes?.length) {
12236
+ const changes = document.createElement("div");
12237
+ changes.className = "lasso-task-changes";
12238
+ changes.textContent = `${selected.changes.length} proposed file change${selected.changes.length === 1 ? "" : "s"}`;
12239
+ detail.appendChild(changes);
12240
+ if (selected.status === "review") {
12241
+ const apply = document.createElement("button");
12242
+ apply.type = "button";
12243
+ apply.className = "lasso-task-apply";
12244
+ apply.textContent = "Apply changes";
12245
+ apply.addEventListener("click", () => {
12246
+ if (state.bridgeSocket?.readyState !== WebSocket.OPEN) return;
12247
+ state.bridgeSocket.send(JSON.stringify({ type: "apply", taskId: selected.id, changes: selected.changes }));
12248
+ updateAgentTask(selected.id, { message: "Applying changes\u2026" });
12249
+ });
12250
+ detail.appendChild(apply);
12251
+ }
12252
+ }
12253
+ }
12254
+
12062
12255
  // src/overlay/notifications.ts
12063
12256
  var permissionRequest = null;
12064
12257
  function requestAgentNotificationPermission() {
@@ -16836,11 +17029,11 @@
16836
17029
  }
16837
17030
  function updateCommentsBadge() {
16838
17031
  const dom = getDOM();
16839
- const badge = dom.shadow.querySelector(".lasso-comments-badge");
16840
- if (!badge) return;
17032
+ const badge2 = dom.shadow.querySelector(".lasso-comments-badge");
17033
+ if (!badge2) return;
16841
17034
  const openCount = threadsInScope().filter((t) => t.root.status === "OPEN").length;
16842
- badge.classList.toggle("visible", openCount > 0);
16843
- badge.textContent = String(openCount);
17035
+ badge2.classList.toggle("visible", openCount > 0);
17036
+ badge2.textContent = String(openCount);
16844
17037
  }
16845
17038
  function commentPosition(thread) {
16846
17039
  const saved = thread.root.meta?.position;
@@ -17591,7 +17784,7 @@
17591
17784
  ta.remove();
17592
17785
  }
17593
17786
  }
17594
- var panel = null;
17787
+ var panel2 = null;
17595
17788
  var items = [];
17596
17789
  var scope = "private";
17597
17790
  var PRIVATE_STORAGE_KEY = "lasso:clipboard:private";
@@ -17650,7 +17843,7 @@
17650
17843
  <div class="lasso-clipboard-list"></div>
17651
17844
  `;
17652
17845
  getDOM().shadow.appendChild(el);
17653
- panel = el;
17846
+ panel2 = el;
17654
17847
  el.querySelector(".lasso-clipboard-close").addEventListener("click", () => toggleClipboardPanel(false));
17655
17848
  el.querySelectorAll(".lasso-clipboard-tab").forEach((button) => {
17656
17849
  button.addEventListener("click", () => {
@@ -17757,14 +17950,14 @@
17757
17950
  return TYPE_COLORS[type] ?? TYPE_COLORS.text;
17758
17951
  }
17759
17952
  function render() {
17760
- const list = panel?.querySelector(".lasso-clipboard-list");
17761
- if (!list) return;
17953
+ const list2 = panel2?.querySelector(".lasso-clipboard-list");
17954
+ if (!list2) return;
17762
17955
  const visible = items.filter((item) => item.scope === scope);
17763
17956
  if (!visible.length) {
17764
- list.innerHTML = `<div class="lasso-clipboard-empty">${scope === "shared" && !state.collabJoined ? "Join a collaboration session to share items." : "No saved items yet."}</div>`;
17957
+ list2.innerHTML = `<div class="lasso-clipboard-empty">${scope === "shared" && !state.collabJoined ? "Join a collaboration session to share items." : "No saved items yet."}</div>`;
17765
17958
  return;
17766
17959
  }
17767
- list.innerHTML = visible.map((item) => {
17960
+ list2.innerHTML = visible.map((item) => {
17768
17961
  const color = typeColor(item.type);
17769
17962
  const icon = typeIcon(item.type);
17770
17963
  const preview = escapeHtml3(item.content).slice(0, 220);
@@ -17788,7 +17981,7 @@
17788
17981
  </div>
17789
17982
  </div>`;
17790
17983
  }).join("");
17791
- list.querySelectorAll(".lasso-clipboard-item").forEach((row) => {
17984
+ list2.querySelectorAll(".lasso-clipboard-item").forEach((row) => {
17792
17985
  const item = visible.find((candidate) => candidate.uid === row.dataset.id);
17793
17986
  if (!item) return;
17794
17987
  const copyBtn = row.querySelector(".lasso-clipboard-copy-item");
@@ -17823,7 +18016,7 @@
17823
18016
  });
17824
18017
  }
17825
18018
  function setStatus(message) {
17826
- const sub = panel?.querySelector(".lasso-clipboard-subtitle");
18019
+ const sub = panel2?.querySelector(".lasso-clipboard-subtitle");
17827
18020
  if (!sub) return;
17828
18021
  const previous = sub.textContent;
17829
18022
  sub.textContent = message;
@@ -17832,10 +18025,10 @@
17832
18025
  }, 1800);
17833
18026
  }
17834
18027
  function toggleClipboardPanel(force) {
17835
- if (!panel) return;
17836
- const next = force === void 0 ? Boolean(panel.hidden) : force;
17837
- panel.hidden = !next;
17838
- panel.classList.toggle("visible", next);
18028
+ if (!panel2) return;
18029
+ const next = force === void 0 ? Boolean(panel2.hidden) : force;
18030
+ panel2.hidden = !next;
18031
+ panel2.classList.toggle("visible", next);
17839
18032
  if (next) requestItems();
17840
18033
  }
17841
18034
  function escapeHtml3(value2) {
@@ -18391,7 +18584,7 @@
18391
18584
  event.preventDefault();
18392
18585
  event.stopPropagation();
18393
18586
  if (state.bridgeSocket?.readyState === WebSocket.OPEN) {
18394
- state.bridgeSocket.send(JSON.stringify({ type: "stop" }));
18587
+ state.bridgeSocket.send(JSON.stringify({ type: "stop", taskId: state.activeTaskId || void 0 }));
18395
18588
  }
18396
18589
  appendChat("assistant", "Agent stopped.");
18397
18590
  resetAgentState();
@@ -18452,7 +18645,7 @@
18452
18645
  rev.querySelector(".lasso-review-undo").addEventListener("click", () => {
18453
18646
  if (state.pendingChanges.length && rev.querySelector(".lasso-review-apply").hidden) {
18454
18647
  if (state.bridgeSocket?.readyState === WebSocket.OPEN) {
18455
- state.bridgeSocket.send(JSON.stringify({ type: "undo" }));
18648
+ state.bridgeSocket.send(JSON.stringify({ type: "undo", taskId: state.activeTaskId || void 0 }));
18456
18649
  }
18457
18650
  return;
18458
18651
  }
@@ -18462,7 +18655,7 @@
18462
18655
  rev.querySelector(".lasso-review-apply").addEventListener("click", () => {
18463
18656
  if (!state.bridgeSocket || state.bridgeSocket.readyState !== WebSocket.OPEN || !state.pendingChanges.length) return;
18464
18657
  requestAgentNotificationPermission();
18465
- state.bridgeSocket.send(JSON.stringify({ type: "apply", changes: state.pendingChanges }));
18658
+ state.bridgeSocket.send(JSON.stringify({ type: "apply", taskId: state.activeTaskId || "", changes: state.pendingChanges }));
18466
18659
  appendChat("assistant", "Applying the reviewed change\u2026");
18467
18660
  });
18468
18661
  return { prompt: el, review: rev };
@@ -18672,9 +18865,9 @@
18672
18865
  container.append(document.createTextNode(text.slice(lastIndex)));
18673
18866
  }
18674
18867
  }
18675
- function setAgentStatus(status, message, detail) {
18868
+ function setAgentStatus(status, message, detail2) {
18676
18869
  if (!agentStatusElement || !agentStatusMessage || !sendButton) return;
18677
- const logMessage = detail?.trim() || message.trim();
18870
+ const logMessage = detail2?.trim() || message.trim();
18678
18871
  const lastLog = agentLogLines[agentLogLines.length - 1];
18679
18872
  if (agentStatusElement.dataset.status === status && agentStatusMessage.textContent === message && (status === "thinking" || status === "working") && (!logMessage || lastLog === logMessage)) return;
18680
18873
  appendAgentLog(logMessage);
@@ -18689,7 +18882,7 @@
18689
18882
  state.agentRunning = status === "thinking" || status === "working";
18690
18883
  sendButton.classList.toggle("loading", state.agentRunning);
18691
18884
  if (stopButton) stopButton.hidden = !state.agentRunning;
18692
- if (promptInput) promptInput.disabled = state.agentRunning;
18885
+ if (promptInput) promptInput.disabled = false;
18693
18886
  const label = sendButton.querySelector("span");
18694
18887
  if (label) {
18695
18888
  label.textContent = status === "review" ? "Review" : status === "error" ? "Retry" : state.agentRunning ? "" : "Send";
@@ -18705,7 +18898,7 @@
18705
18898
  if (!promptInput || !sendButton) return;
18706
18899
  const hasInstruction = Boolean(promptInput.value.trim()) || sendButton.dataset.state === "retry" && Boolean(state.lastInstruction?.trim());
18707
18900
  const isReviewAction = sendButton.dataset.state === "review";
18708
- sendButton.disabled = state.agentRunning || !hasInstruction && !isReviewAction;
18901
+ sendButton.disabled = !hasInstruction && !isReviewAction;
18709
18902
  }
18710
18903
  function resetAgentState() {
18711
18904
  state.agentRunning = false;
@@ -18842,6 +19035,7 @@
18842
19035
  });
18843
19036
  }
18844
19037
  appendChat("user", instruction);
19038
+ const task = createAgentTask(instruction);
18845
19039
  setAgentStatus("thinking", "Thinking\u2026");
18846
19040
  state.lastInstruction = instruction;
18847
19041
  promptInput.value = "";
@@ -18855,6 +19049,7 @@
18855
19049
  state.bridgeSocket.send(
18856
19050
  JSON.stringify({
18857
19051
  type: wantsAnswer ? "ask" : "edit",
19052
+ taskId: task.id,
18858
19053
  question: wantsAnswer ? instruction : void 0,
18859
19054
  instruction,
18860
19055
  messages: state.chatHistory,
@@ -19706,25 +19901,25 @@
19706
19901
  const parts2 = filePath.split("/");
19707
19902
  const name = parts2.pop() || filePath;
19708
19903
  const dir = parts2.length ? parts2.join("/") + "/" : "";
19709
- let badge = "M";
19904
+ let badge2 = "M";
19710
19905
  let badgeClass = "mod";
19711
19906
  if (code.includes("A")) {
19712
- badge = "A";
19907
+ badge2 = "A";
19713
19908
  badgeClass = "add";
19714
19909
  } else if (code.includes("D")) {
19715
- badge = "D";
19910
+ badge2 = "D";
19716
19911
  badgeClass = "del";
19717
19912
  } else if (code.includes("R")) {
19718
- badge = "R";
19913
+ badge2 = "R";
19719
19914
  badgeClass = "ren";
19720
19915
  } else if (code === "??" || code.includes("?")) {
19721
- badge = "U";
19916
+ badge2 = "U";
19722
19917
  badgeClass = "unt";
19723
19918
  } else if (code.includes("M")) {
19724
- badge = "M";
19919
+ badge2 = "M";
19725
19920
  badgeClass = "mod";
19726
19921
  }
19727
- return { code, badge, badgeClass, path: filePath, dir, name };
19922
+ return { code, badge: badge2, badgeClass, path: filePath, dir, name };
19728
19923
  });
19729
19924
  }
19730
19925
  function escapeHtml4(text) {
@@ -19935,7 +20130,7 @@
19935
20130
  const uninitView = gitPanelEl.querySelector(".lasso-git-uninit-view");
19936
20131
  const repoView = gitPanelEl.querySelector(".lasso-git-repo-view");
19937
20132
  const filesList = gitPanelEl.querySelector(".lasso-git-files-list");
19938
- const badge = gitPanelEl.querySelector(".lasso-git-changes-badge");
20133
+ const badge2 = gitPanelEl.querySelector(".lasso-git-changes-badge");
19939
20134
  const commitBtn = gitPanelEl.querySelector(".lasso-git-commit-btn");
19940
20135
  const commitBtnText = gitPanelEl.querySelector(".lasso-git-commit-btn-text");
19941
20136
  const pushBtn = gitPanelEl.querySelector(".lasso-git-push-btn");
@@ -19958,12 +20153,12 @@
19958
20153
  uninitView.hidden = next.isRepo;
19959
20154
  repoView.hidden = !next.isRepo;
19960
20155
  }
19961
- if (filesList && badge && next.isRepo) {
20156
+ if (filesList && badge2 && next.isRepo) {
19962
20157
  const rawStatus = next.status || [];
19963
20158
  const parsed = parseGitStatus(rawStatus);
19964
20159
  const count = parsed.length;
19965
- badge.textContent = count > 0 ? String(count) : "Clean";
19966
- badge.className = `lasso-git-changes-badge${count > 0 ? " has-changes" : " clean"}`;
20160
+ badge2.textContent = count > 0 ? String(count) : "Clean";
20161
+ badge2.className = `lasso-git-changes-badge${count > 0 ? " has-changes" : " clean"}`;
19967
20162
  if (count === 0) {
19968
20163
  filesList.innerHTML = `
19969
20164
  <div class="lasso-git-clean-state">
@@ -20148,6 +20343,11 @@
20148
20343
  </svg>
20149
20344
  </button>
20150
20345
 
20346
+ <button class="lasso-tool-btn agent-tasks-tool" type="button" aria-label="Agent tasks" title="Agent tasks">
20347
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M7 8h10M7 12h6M7 16h8"/></svg>
20348
+ <i class="lasso-agent-tasks-badge" hidden></i>
20349
+ </button>
20350
+
20151
20351
  <button class="lasso-tool-btn notepad-tool" type="button" aria-label="Notepad" title="Scratchpad & Notes">
20152
20352
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
20153
20353
  <path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8l-5-5z"/>
@@ -20223,6 +20423,7 @@
20223
20423
  const commentBtn = toolbar.querySelector(".comment-tool");
20224
20424
  const gitBtn = toolbar.querySelector(".git-tool");
20225
20425
  const todoBtn = toolbar.querySelector(".todo-tool");
20426
+ const agentTasksBtn = toolbar.querySelector(".agent-tasks-tool");
20226
20427
  const notepadBtn = toolbar.querySelector(".notepad-tool");
20227
20428
  const clipboardBtn = toolbar.querySelector(".clipboard-tool");
20228
20429
  const voiceBtn = toolbar.querySelector(".voice-tool");
@@ -20259,6 +20460,11 @@
20259
20460
  e.stopPropagation();
20260
20461
  toggleTodoPanel();
20261
20462
  });
20463
+ agentTasksBtn.addEventListener("click", (e) => {
20464
+ e.preventDefault();
20465
+ e.stopPropagation();
20466
+ toggleTaskPanel();
20467
+ });
20262
20468
  notepadBtn.addEventListener("click", (e) => {
20263
20469
  e.preventDefault();
20264
20470
  e.stopPropagation();
@@ -20354,6 +20560,9 @@
20354
20560
  setGeneratedCommitMessage(message.message || "", message.error);
20355
20561
  }
20356
20562
  if (message.type === "agent_status" && message.status && message.message) {
20563
+ const taskStatus = message.status === "error" ? "error" : message.status === "stopped" ? "stopped" : message.status;
20564
+ updateAgentTask(message.taskId, { status: taskStatus, message: message.message, detail: message.detail, changes: message.changes });
20565
+ if (message.taskId && message.taskId !== state.activeTaskId) return;
20357
20566
  setAgentStatus(message.status, message.message, message.detail);
20358
20567
  setDragCardStatus(message.status, message.message);
20359
20568
  if (message.status === "review" && message.changes?.length) {
@@ -20368,11 +20577,15 @@
20368
20577
  }
20369
20578
  }
20370
20579
  if (message.type === "assistant_message" && message.message) {
20580
+ updateAgentTask(message.taskId, { status: "complete", message: "Complete", response: message.message });
20581
+ if (message.taskId && message.taskId !== state.activeTaskId) return;
20371
20582
  void notifyAgent("Agent complete", message.message);
20372
20583
  appendChat("assistant", message.message);
20373
20584
  resetAgentState();
20374
20585
  }
20375
20586
  if (message.type === "applied" || message.type === "undone") {
20587
+ updateAgentTask(message.taskId, { status: "complete", message: message.message || "Done." });
20588
+ if (message.taskId && message.taskId !== state.activeTaskId) return;
20376
20589
  void notifyAgent(message.type === "applied" ? "Changes applied" : "Change undone", message.message || "Done.");
20377
20590
  appendChat("assistant", message.message || "Done.");
20378
20591
  releaseHeldLock();
@@ -20412,6 +20625,7 @@
20412
20625
  buildTodoPanel();
20413
20626
  buildNotepadPanel();
20414
20627
  buildClipboardPanel();
20628
+ buildTaskPanel();
20415
20629
  buildPrompt();
20416
20630
  buildDrag();
20417
20631
  initErrorListeners();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lasso-ai/cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
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",