@lasso-ai/cli 2.1.13 → 2.1.15

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.
@@ -212,12 +212,7 @@ class ActionExecutor {
212
212
  };
213
213
  }
214
214
  // Safety check: prevent dangerous commands
215
- const dangerousPatterns = [
216
- /\brm\s+-rf\b/,
217
- /\bdd\s+if=/,
218
- />\s*\/dev\/null/,
219
- />\s*\/dev\/sd/,
220
- ];
215
+ const dangerousPatterns = [/\brm\s+-rf\b/, /\bdd\s+if=/, />\s*\/dev\/null/, />\s*\/dev\/sd/];
221
216
  for (const pattern of dangerousPatterns) {
222
217
  if (pattern.test(command)) {
223
218
  return {
@@ -366,9 +361,7 @@ class ActionExecutor {
366
361
  * boundary: every resolved path must stay inside projectPath.
367
362
  */
368
363
  resolvePath(filePath) {
369
- const resolved = node_path_1.default.isAbsolute(filePath)
370
- ? node_path_1.default.resolve(filePath)
371
- : node_path_1.default.resolve(this.projectPath, filePath);
364
+ const resolved = node_path_1.default.isAbsolute(filePath) ? node_path_1.default.resolve(filePath) : node_path_1.default.resolve(this.projectPath, filePath);
372
365
  const root = node_path_1.default.resolve(this.projectPath);
373
366
  if (resolved !== root && !resolved.startsWith(root + node_path_1.default.sep)) {
374
367
  throw new Error(`Path escapes workspace: ${filePath}`);
@@ -388,12 +381,7 @@ class ActionExecutor {
388
381
  for (const entry of entries) {
389
382
  const fullPath = node_path_1.default.join(currentDir, entry.name);
390
383
  // Skip common ignored directories
391
- if (entry.name === "node_modules" ||
392
- entry.name === ".git" ||
393
- entry.name === "dist" ||
394
- entry.name === "build" ||
395
- entry.name === ".next" ||
396
- entry.name === ".lasso") {
384
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist" || entry.name === "build" || entry.name === ".next" || entry.name === ".lasso") {
397
385
  continue;
398
386
  }
399
387
  if (entry.isDirectory()) {
@@ -67,9 +67,7 @@ class ClaudeCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
67
67
  try {
68
68
  const parsed = JSON.parse(stdout.slice(jsonStart));
69
69
  if (parsed && typeof parsed.loggedIn === "boolean") {
70
- return parsed.loggedIn
71
- ? { authenticated: true }
72
- : { authenticated: false, error: "Claude Code is not logged in.", fixInstructions: "Run 'claude' once and log in, or run 'claude auth login'." };
70
+ return parsed.loggedIn ? { authenticated: true } : { authenticated: false, error: "Claude Code is not logged in.", fixInstructions: "Run 'claude' once and log in, or run 'claude auth login'." };
73
71
  }
74
72
  }
75
73
  catch {
@@ -91,15 +89,7 @@ class ClaudeCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
91
89
  const cfg = this.config;
92
90
  const resumeId = this.resumeSessionId(context);
93
91
  const sessionId = node_crypto_1.default.randomUUID();
94
- const args = [
95
- "-p",
96
- this.buildTaskPrompt(context),
97
- "--output-format",
98
- "stream-json",
99
- "--verbose",
100
- "--permission-mode",
101
- cfg.permissionMode || "acceptEdits",
102
- ];
92
+ const args = ["-p", this.buildTaskPrompt(context), "--output-format", "stream-json", "--verbose", "--permission-mode", cfg.permissionMode || "acceptEdits"];
103
93
  if (this.modelId)
104
94
  args.push("--model", this.modelId);
105
95
  if (resumeId)
@@ -160,7 +150,10 @@ class ClaudeCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
160
150
  handleAssistant(event, sink) {
161
151
  const content = event.message?.content || [];
162
152
  if (event.error) {
163
- const text = content.map((c) => c.text || "").filter(Boolean).join(" ");
153
+ const text = content
154
+ .map((c) => c.text || "")
155
+ .filter(Boolean)
156
+ .join(" ");
164
157
  sink.state.failureMessage = text || `Claude Code error: ${event.error}`;
165
158
  if (/billing|auth|unauthorized|credit/i.test(event.error) || /credit balance|not logged in|unauthorized/i.test(sink.state.failureMessage)) {
166
159
  sink.state.authFailure = true;
@@ -199,11 +199,7 @@ class CodexCLIAdapter extends local_agent_adapter_1.LocalAgentAdapter {
199
199
  return error;
200
200
  if (typeof error === "object") {
201
201
  const value = error;
202
- const nested = [
203
- value.message,
204
- value.data?.message,
205
- value.cause?.message,
206
- ].find((item) => typeof item === "string" && item.trim().length > 0);
202
+ const nested = [value.message, value.data?.message, value.cause?.message].find((item) => typeof item === "string" && item.trim().length > 0);
207
203
  if (nested)
208
204
  return nested;
209
205
  try {
@@ -230,11 +230,7 @@ class LocalAgentAdapter {
230
230
  sink.emit("task_completed", { summary: state.summary });
231
231
  }
232
232
  else {
233
- const message = state.failureMessage ||
234
- inspected?.message ||
235
- (exitCode === 0
236
- ? `${this.provider} finished without reporting a successful result.`
237
- : `${this.provider} exited with code ${exitCode}${stderrBuffer ? `: ${stderrBuffer.trim().slice(-500)}` : ""}`);
233
+ const message = state.failureMessage || inspected?.message || (exitCode === 0 ? `${this.provider} finished without reporting a successful result.` : `${this.provider} exited with code ${exitCode}${stderrBuffer ? `: ${stderrBuffer.trim().slice(-500)}` : ""}`);
238
234
  if (!state.errors.some((e) => e.message === message)) {
239
235
  sink.error(message, { recoverable: false, authFailure: state.authFailure });
240
236
  }
@@ -277,14 +273,8 @@ class LocalAgentAdapter {
277
273
  }
278
274
  async switchBackend(newBackend, context) {
279
275
  const lostContext = this.supportsResume
280
- ? [
281
- `${this.provider} maintains its own internal session state that cannot be transferred to ${newBackend.provider}.`,
282
- "Tool call history and per-file context held by the previous agent are not portable.",
283
- ]
284
- : [
285
- `${this.provider} does not expose resumable session state for transfer.`,
286
- "Tool call history and per-file context held by the previous agent are not portable.",
287
- ];
276
+ ? [`${this.provider} maintains its own internal session state that cannot be transferred to ${newBackend.provider}.`, "Tool call history and per-file context held by the previous agent are not portable."]
277
+ : [`${this.provider} does not expose resumable session state for transfer.`, "Tool call history and per-file context held by the previous agent are not portable."];
288
278
  return {
289
279
  transferableContext: {
290
280
  conversation: context.conversation,
@@ -314,7 +304,10 @@ class LocalAgentAdapter {
314
304
  // Retry/continuation replies ("try again", "continue", …) must resume
315
305
  // the previous request instead of being treated as a literal task —
316
306
  // otherwise the agent greps for words like "again".
317
- const normalizedPrompt = context.userPrompt.trim().replace(/[.!]+$/, "").toLowerCase();
307
+ const normalizedPrompt = context.userPrompt
308
+ .trim()
309
+ .replace(/[.!]+$/, "")
310
+ .toLowerCase();
318
311
  const isContinuation = /^(continue|proceed|go on|go ahead|keep going|yes|yep|yup|sure|ok|okay|alright|do it|carry on|resume|next|try again|again|retry|re-try|do it again|run again|run it again|one more time|same again)$/.test(normalizedPrompt) ||
319
312
  /^(try|do|run|go)\s+(it\s+|that\s+)?again$|^one more time$|^retry(\s+it)?$/i.test(normalizedPrompt);
320
313
  if (isContinuation) {
@@ -57,9 +57,7 @@ class OpenCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
57
57
  timeout: 12000,
58
58
  windowsHide: true,
59
59
  }));
60
- const text = `${stdout}\n${stderr}`
61
- .replace(/\u001b\[[0-9;]*m/g, "")
62
- .replace(/\u001b\][^\u0007]*\u0007/g, "");
60
+ const text = `${stdout}\n${stderr}`.replace(/\u001b\[[0-9;]*m/g, "").replace(/\u001b\][^\u0007]*\u0007/g, "");
63
61
  // The credentials section lists a "●" bullet per configured credential.
64
62
  const credentialsStart = text.indexOf("Credentials");
65
63
  const environmentStart = text.indexOf("Environment");
@@ -95,9 +93,7 @@ class OpenCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
95
93
  if (resumeId)
96
94
  args.push("--session", resumeId);
97
95
  args.push(this.buildTaskPrompt(context));
98
- const env = context.mode === "conversation"
99
- ? { OPENCODE_PERMISSION: JSON.stringify({ "*": "deny" }) }
100
- : undefined;
96
+ const env = context.mode === "conversation" ? { OPENCODE_PERMISSION: JSON.stringify({ "*": "deny" }) } : undefined;
101
97
  return { bin: "opencode", args, cwd: this.config.projectPath, env };
102
98
  }
103
99
  handleLine(line, context, sink) {
@@ -172,12 +168,7 @@ class OpenCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
172
168
  return error;
173
169
  if (typeof error === "object") {
174
170
  const e = error;
175
- const nested = [
176
- e.message,
177
- e.data?.message,
178
- e.data?.error,
179
- e.cause?.message,
180
- ].find((item) => typeof item === "string" && item.trim().length > 0);
171
+ const nested = [e.message, e.data?.message, e.data?.error, e.cause?.message].find((item) => typeof item === "string" && item.trim().length > 0);
181
172
  if (nested)
182
173
  return nested;
183
174
  try {
@@ -185,9 +185,7 @@ class AgentStateMachine {
185
185
  * Check if the session can accept new actions
186
186
  */
187
187
  static canAcceptActions(session) {
188
- return (session.status === "running" &&
189
- session.phase !== "complete" &&
190
- session.phase !== "understand");
188
+ return session.status === "running" && session.phase !== "complete" && session.phase !== "understand";
191
189
  }
192
190
  /**
193
191
  * Check if the session should trigger verification
@@ -203,8 +201,7 @@ class AgentStateMachine {
203
201
  * Check if the session should trigger recovery
204
202
  */
205
203
  static shouldRecover(session) {
206
- return (session.phase === "verify" &&
207
- session.errors.filter((e) => e.phase === "verify").length > 0);
204
+ return session.phase === "verify" && session.errors.filter((e) => e.phase === "verify").length > 0;
208
205
  }
209
206
  /**
210
207
  * Check if the session can switch models
@@ -13,18 +13,14 @@ function diffToReview(rawChanges) {
13
13
  const applyable = rawChanges.filter((c) => !c.binary);
14
14
  const changes = applyable.map((change) => ({
15
15
  filePath: change.filePath,
16
- oldString: change.operation === "created" ? "" : change.oldContent ?? "",
17
- newString: change.operation === "deleted" ? "" : change.newContent ?? "",
16
+ oldString: change.operation === "created" ? "" : (change.oldContent ?? ""),
17
+ newString: change.operation === "deleted" ? "" : (change.newContent ?? ""),
18
18
  operation: change.operation,
19
19
  }));
20
20
  const actions = applyable.map((change) => ({
21
21
  id: crypto.randomUUID(),
22
22
  type: change.operation === "created" ? "create_file" : change.operation === "deleted" ? "delete_file" : "edit_file",
23
- data: change.operation === "created"
24
- ? { filePath: change.filePath, content: change.newContent }
25
- : change.operation === "deleted"
26
- ? { filePath: change.filePath }
27
- : { filePath: change.filePath, oldString: change.oldContent ?? "", newString: change.newContent ?? "" },
23
+ data: change.operation === "created" ? { filePath: change.filePath, content: change.newContent } : change.operation === "deleted" ? { filePath: change.filePath } : { filePath: change.filePath, oldString: change.oldContent ?? "", newString: change.newContent ?? "" },
28
24
  }));
29
25
  return { changes, binaryPaths, actions };
30
26
  }
@@ -49,12 +49,7 @@ const DEFAULT_IGNORE_DIRS = new Set([
49
49
  ".gradle",
50
50
  ".terraform",
51
51
  ]);
52
- const DEFAULT_IGNORE_FILES = new Set([
53
- "next-env.d.ts",
54
- "tsconfig.tsbuildinfo",
55
- ".DS_Store",
56
- "Thumbs.db",
57
- ]);
52
+ const DEFAULT_IGNORE_FILES = new Set(["next-env.d.ts", "tsconfig.tsbuildinfo", ".DS_Store", "Thumbs.db"]);
58
53
  function shouldIgnoreFile(name) {
59
54
  return DEFAULT_IGNORE_FILES.has(name) || name.endsWith(".tsbuildinfo");
60
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lasso-ai/cli",
3
- "version": "2.1.13",
3
+ "version": "2.1.15",
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",
@@ -46,9 +46,10 @@
46
46
  "typecheck:overlay": "tsc -p config/tsconfig.overlay.json --noEmit",
47
47
  "start": "node dist/cli/index.js",
48
48
  "prepublishOnly": "npm run build",
49
- "format": "prettier --write .",
49
+ "format": "prettier --write src config",
50
50
  "deploy": "pnpm build && npm publish --access public",
51
- "format:check": "prettier --check .",
51
+ "format:check": "prettier --check src config",
52
+ "lint": "npm run typecheck && npm run format:check",
52
53
  "test": "node --import tsx --test src/runtime/__tests__/*.test.ts"
53
54
  },
54
55
  "keywords": [