@lasso-ai/cli 1.1.5 → 1.2.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.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ - **One-shot mode with global command bar**: Press ⌘K to open a command interface
13
+ anywhere in your app. Describe what you want to build (e.g., "Build a complete billing
14
+ system with invoices and payment tracking") and Lasso will plan, execute, and
15
+ validate the changes automatically. The agent runs through understand → plan →
16
+ execute → validate, with real-time progress visibility.
17
+ - **`lasso create` command**: Create brand-new Lasso projects with framework scaffolding.
18
+ Supports Next.js, React, Vue, Svelte, and Solid with TypeScript and package manager
19
+ selection. Optionally describe your app during creation to trigger one-shot mode
20
+ automatically.
21
+ - **`lasso doctor` command**: Diagnose configuration and integration problems.
22
+ Checks for `lasso.config.json`, API key configuration, framework detection, and
23
+ package.json presence.
24
+ - **Scope management**: One-shot mode supports project-level (broad changes across
25
+ entire codebase) and component-level (targeted changes) scopes to prevent agents
26
+ from wandering through unrelated files.
12
27
  - **Agent task workflow**: concurrent prompts now appear in In progress and
13
28
  Completed tabs. Review-ready tasks expose a Review action, and accepted changes
14
29
  move the task to Completed.
package/README.md CHANGED
@@ -78,6 +78,25 @@ Lasso works differently.
78
78
 
79
79
  ## Features
80
80
 
81
+ ### Two interaction modes
82
+
83
+ Lasso now supports two equally powerful ways to build:
84
+
85
+ **Visual Mode** (point-and-select)
86
+ - Select a component in your running app
87
+ - Describe the change you want
88
+ - Review the diff
89
+ - Accept to apply
90
+
91
+ **One-Shot Mode** (⌘K command bar)
92
+ - Press ⌘K anywhere to open the command bar
93
+ - Describe what you want to build (e.g., "Build a complete billing system")
94
+ - Lasso plans, executes, and validates automatically
95
+ - Real-time progress visibility as the agent works
96
+
97
+ These modes work together naturally: use one-shot to generate large features, then
98
+ visual mode to refine specific parts.
99
+
81
100
  ### Visual selection
82
101
 
83
102
  Turn on Lasso Mode and interact with your application normally.
@@ -87,6 +106,17 @@ Turn on Lasso Mode and interact with your application normally.
87
106
  - Hover to see what will be selected
88
107
  - Select empty space to insert new UI
89
108
 
109
+ ### One-shot agent
110
+
111
+ The one-shot agent follows a plan-execute-validate loop:
112
+
113
+ 1. **Understand**: Analyzes project structure, framework, and dependencies
114
+ 2. **Plan**: Generates execution steps (install, create, modify, delete, command)
115
+ 3. **Execute**: Runs each step with real-time progress updates
116
+ 4. **Validate**: Runs typecheck and build to verify changes
117
+
118
+ Progress is visible throughout: "Step 1/5: Creating auth configuration", "Step 2/5: Adding login page", etc.
119
+
90
120
  ### Overlay toolbars
91
121
 
92
122
  The overlay includes a primary editing toolbar and a compact secondary toolbar.
@@ -333,6 +363,33 @@ and set `LASSO_API_KEY` — see [Team collaboration](#team-collaboration-realtim
333
363
 
334
364
  Your existing configuration files are **not modified**.
335
365
 
366
+ ### 2a. Create a new project
367
+
368
+ Starting from scratch? Lasso can scaffold a new project for you:
369
+
370
+ ```bash
371
+ lasso create my-app
372
+ ```
373
+
374
+ You'll be prompted to choose:
375
+ - Framework (Next.js, React, Vue, Svelte, Solid)
376
+ - TypeScript preference
377
+ - Package manager (pnpm, npm, yarn)
378
+
379
+ Lasso then:
380
+ - Creates the application scaffold
381
+ - Installs dependencies
382
+ - Connects Lasso automatically
383
+ - Registers the project
384
+
385
+ Optionally describe your app during creation to trigger one-shot mode:
386
+
387
+ ```bash
388
+ lasso create my-app --prompt "A SaaS dashboard for managing customer invoices with billing and analytics"
389
+ ```
390
+
391
+ Or describe it interactively after the scaffold is complete.
392
+
336
393
  ### 3. Select something
337
394
 
338
395
  Enable **Lasso Mode** from the floating toolbar.
package/dist/cli/agent.js CHANGED
@@ -659,7 +659,7 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress, taskI
659
659
  });
660
660
  }
661
661
  async function proposeWithLocalAgent(cwd, instruction, context, config, signal, onProgress, taskId, onPrompt) {
662
- const outputContract = `Return ONLY valid JSON in this exact shape: {"summary":"short explanation","changes":[{"filePath":"relative/path","oldString":"exact existing text","newString":"replacement text"}]}. Treat the supplied source context as read-only. Before returning, verify every oldString against that context. Use project-relative paths only. Do not edit files, run write commands, commit, or produce markdown fences.`;
662
+ const outputContract = `CRITICAL: You MUST return ONLY valid JSON with actual file changes. NEVER explain, describe, or analyze code without proposing edits. ALWAYS return JSON with at least one change when given a modification instruction. Format: {"summary":"brief action taken","changes":[{"filePath":"relative/path","oldString":"exact existing text","newString":"replacement text"}]}. Treat the supplied source context as read-only. Before returning, verify every oldString against that context. Use project-relative paths only. Do not edit files, run write commands, commit, or produce markdown fences. If you cannot find the exact text to change, search the provided context more carefully - do not give up and explain instead.`;
663
663
  const prompt = `${instruction}\n\n${outputContract}\n\nLasso has already assembled this source context:\n${context || "No matching source context was found."}`;
664
664
  const local = localCommand(config.provider, config.model, prompt);
665
665
  const command = local.command;
@@ -677,11 +677,11 @@ async function proposeChanges(cwd, input, config, signal, onProgress, onPrompt)
677
677
  const dragHint = input.context?.drag
678
678
  ? `\n\nDRAG REPOSITIONING TASK:\nThe user dragged this element by dx: ${input.context.drag.delta?.dx ?? 0}px, dy: ${input.context.drag.delta?.dy ?? 0}px to target coordinates (left: ${input.context.drag.targetRect?.left ?? 0}px, top: ${input.context.drag.targetRect?.top ?? 0}px). Modify the source code (CSS classes, Tailwind classes, flex/grid alignment, margin offsets, or positioning properties) so the element is accurately rendered at this target position.`
679
679
  : "";
680
- const instruction = `Selection context:\n${JSON.stringify(input.element, null, 2)}\n${JSON.stringify(visualContext, null, 2)}\n\nConversation history:\n${history}\n\nPrevious change history for this selection:\n${priorChanges}\n\nRelevant source context:\n${context || "No matching source context was found. Ask for a more specific selection rather than inventing a file."}${dragHint}\n\nReturn ONLY JSON: {"summary":"short explanation","changes":[{"filePath":"relative/path","oldString":"exact text","newString":"replacement text"}]}`;
680
+ const instruction = `Selection context:\n${JSON.stringify(input.element, null, 2)}\n${JSON.stringify(visualContext, null, 2)}\n\nConversation history:\n${history}\n\nPrevious change history for this selection:\n${priorChanges}\n\nRelevant source context:\n${context || "No matching source context was found. Ask for a more specific selection rather than inventing a file."}${dragHint}\n\nCRITICAL: You MUST return ONLY JSON with actual file changes. Do NOT explain what you see or describe the code. Always propose concrete edits when given an instruction. Return JSON format: {"summary":"brief action taken","changes":[{"filePath":"relative/path","oldString":"exact existing text","newString":"replacement text"}]}`;
681
681
  if (config.provider === "claude-code" || config.provider === "codex" || config.provider === "opencode") {
682
682
  return proposeWithLocalAgent(cwd, instruction, context, config, signal, onProgress, input.taskId, onPrompt);
683
683
  }
684
- const system = "You are Lasso, a careful source-code editing agent. Return only valid JSON. Each oldString must occur exactly once in its file. Never rewrite whole files. Keep changes focused on the request.";
684
+ const system = "You are Lasso, a source-code editing agent. Your ONLY job is to propose concrete file changes. NEVER explain, describe, or analyze code without proposing edits. ALWAYS return valid JSON with at least one change when the user requests a modification. Each oldString must occur exactly once in its file. Never rewrite whole files. Keep changes focused and minimal. If you cannot find the exact text to change, look harder at the provided context - do not give up and explain instead.";
685
685
  const model = config.model || (config.provider === "google" ? "gemini-2.5-flash" : config.provider === "openai" ? "gpt-4.1-mini" : config.provider === "ollama" ? "llama3.2" : "claude-sonnet-4-20250514");
686
686
  const image = input.context?.screenshots?.element || input.context?.screenshots?.full;
687
687
  const imageData = image?.replace(/^data:image\/[^;]+;base64,/, "");
@@ -131,6 +131,12 @@ export type BridgeMessage = {
131
131
  audio: string;
132
132
  mimeType?: string;
133
133
  language?: string;
134
+ } | {
135
+ type: "oneshot";
136
+ prompt: string;
137
+ scope: "project" | "component";
138
+ model?: string;
139
+ provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
134
140
  };
135
141
  type ModelOption = {
136
142
  id: string;
@@ -598,6 +598,49 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
598
598
  }));
599
599
  }
600
600
  }
601
+ else if (msg.type === "oneshot") {
602
+ console.log("[lasso] one-shot request:", msg.prompt, msg.scope);
603
+ if (!lassoKeyConfigured && !agentConfig) {
604
+ socket.send(JSON.stringify({ type: "agent_status", status: "error", message: "Set your Lasso API key or configure an AI provider before using one-shot mode." }));
605
+ return;
606
+ }
607
+ const localProvider = msg.provider === "cli";
608
+ const cliProvider = msg.model?.startsWith("claude-code:") ? "claude-code" : msg.model?.startsWith("opencode:") ? "opencode" : "codex";
609
+ const selectedConfig = localProvider
610
+ ? { provider: cliProvider, model: msg.model || "claude-code:sonnet" }
611
+ : { ...agentConfig, provider: (msg.provider || agentConfig.provider), model: msg.model || agentConfig.model };
612
+ const taskId = `oneshot-${Date.now()}`;
613
+ const controller = new AbortController();
614
+ taskControllers.set(taskId, controller);
615
+ socket.send(JSON.stringify({ type: "agent_status", taskId, status: "thinking", message: "Planning your request..." }));
616
+ // Import the one-shot agent function
617
+ const { runOneShotAgent } = await import("./oneshot.js");
618
+ runOneShotAgent(cwd, msg.prompt, msg.scope, selectedConfig, controller.signal, (status, message, detail) => {
619
+ if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
620
+ socket.send(JSON.stringify({ type: "agent_status", taskId, status, message, detail }));
621
+ }
622
+ }, (prompt) => {
623
+ if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
624
+ socket.send(JSON.stringify({ type: "agent_prompt", taskId, prompt }));
625
+ }
626
+ }).then((result) => {
627
+ if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
628
+ if (result.ok) {
629
+ socket.send(JSON.stringify({ type: "agent_status", taskId, status: "review", message: result.summary, changes: result.changes }));
630
+ }
631
+ else {
632
+ socket.send(JSON.stringify({ type: "agent_status", taskId, status: "error", message: result.error || "One-shot agent failed" }));
633
+ }
634
+ }
635
+ }).catch((error) => {
636
+ if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
637
+ socket.send(JSON.stringify({ type: "agent_status", taskId, status: "error", message: error instanceof Error ? error.message : "One-shot agent encountered an error" }));
638
+ }
639
+ }).finally(() => {
640
+ if (taskControllers.get(taskId) === controller)
641
+ taskControllers.delete(taskId);
642
+ });
643
+ }
601
644
  else if (msg.type === "apply") {
602
645
  try {
603
646
  const snapshots = [];
@@ -0,0 +1,16 @@
1
+ export type Framework = "next" | "react" | "vue" | "svelte" | "solid";
2
+ export type PackageManager = "pnpm" | "npm" | "yarn";
3
+ interface CreateOptions {
4
+ name?: string;
5
+ framework?: Framework;
6
+ typescript?: boolean;
7
+ packageManager?: PackageManager;
8
+ prompt?: string;
9
+ }
10
+ interface CreateResult {
11
+ ok: boolean;
12
+ error?: string;
13
+ projectPath?: string;
14
+ }
15
+ export declare function createProject(options: CreateOptions): Promise<CreateResult>;
16
+ export {};
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createProject = createProject;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const node_child_process_1 = require("node:child_process");
10
+ const node_util_1 = require("node:util");
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const prompts_1 = __importDefault(require("prompts"));
13
+ const project_1 = require("./project");
14
+ const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
15
+ async function detectPackageManager() {
16
+ const hasCommand = (cmd) => {
17
+ try {
18
+ (0, node_child_process_1.execSync)(cmd, { stdio: "ignore" });
19
+ return true;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ };
25
+ if (hasCommand("pnpm --version"))
26
+ return "pnpm";
27
+ if (hasCommand("yarn --version"))
28
+ return "yarn";
29
+ return "npm";
30
+ }
31
+ function getFrameworkCommand(framework, typescript, packageManager) {
32
+ const tsFlag = typescript ? "--typescript" : "--js";
33
+ const baseCommands = {
34
+ next: `npx create-next-app@latest`,
35
+ react: `npx create-react-app@latest`,
36
+ vue: `npx create-vite@latest`,
37
+ svelte: `npx create-svelte@latest`,
38
+ solid: `npx degit solidjs/templates/js`,
39
+ };
40
+ let command = baseCommands[framework];
41
+ if (framework === "next") {
42
+ command += ` ${tsFlag} --tailwind --eslint --app --src-dir --import-alias "@/*" --no-git`;
43
+ }
44
+ else if (framework === "react") {
45
+ command += ` ${typescript ? "--template typescript" : ""}`;
46
+ }
47
+ else if (framework === "vue") {
48
+ command += ` ${typescript ? "--template vue-ts" : "--template vanilla"}`;
49
+ }
50
+ else if (framework === "svelte") {
51
+ command += ` ${typescript ? "--template skeleton" : "--template skeleton"}`;
52
+ }
53
+ return command;
54
+ }
55
+ async function runCreateCommand(projectName, framework, typescript, packageManager, cwd) {
56
+ const projectPath = node_path_1.default.join(cwd, projectName);
57
+ if (node_fs_1.default.existsSync(projectPath)) {
58
+ return { ok: false, error: `Directory "${projectName}" already exists.` };
59
+ }
60
+ console.log(chalk_1.default.dim(`Creating ${chalk_1.default.cyan(projectName)} with ${chalk_1.default.cyan(framework)}...`));
61
+ try {
62
+ let command;
63
+ let args;
64
+ if (framework === "next") {
65
+ const tsFlag = typescript ? "--ts" : "--js";
66
+ const pmFlag = packageManager === "pnpm" ? "--use-pnpm" : packageManager === "yarn" ? "--use-yarn" : "--use-npm";
67
+ command = "npx";
68
+ args = ["create-next-app@latest", projectName, tsFlag, "--tailwind", "--eslint", "--app", "--src-dir", "--import-alias", "@/*", "--no-git", pmFlag];
69
+ }
70
+ else if (framework === "react") {
71
+ command = "npx";
72
+ args = ["create-react-app@latest", projectName, ...(typescript ? ["--template", "typescript"] : [])];
73
+ }
74
+ else if (framework === "vue") {
75
+ command = "npx";
76
+ args = ["create-vue@latest", projectName, ...(typescript ? ["--typescript"] : []), "--no-git", "--no-install"];
77
+ }
78
+ else if (framework === "svelte") {
79
+ command = "npx";
80
+ args = ["create-svelte@latest", projectName, ...(typescript ? ["--types", "typescript"] : ["--types", "checkjs"]), "--no-eslint", "--no-prettier", "--no-playwright", "--no-vitest", "--template", "skeleton"];
81
+ }
82
+ else if (framework === "solid") {
83
+ command = "npx";
84
+ args = ["degit", `solidjs/templates/${typescript ? "ts" : "js"}`, projectName];
85
+ }
86
+ else {
87
+ return { ok: false, error: `Unsupported framework: ${framework}` };
88
+ }
89
+ await new Promise((resolve, reject) => {
90
+ const proc = (0, node_child_process_1.spawn)(command, args, { cwd, stdio: "inherit" });
91
+ proc.on("close", (code) => {
92
+ if (code === 0)
93
+ resolve();
94
+ else
95
+ reject(new Error(`Command exited with code ${code}`));
96
+ });
97
+ });
98
+ if (framework === "vue" || framework === "svelte") {
99
+ console.log(chalk_1.default.dim(`Installing dependencies with ${chalk_1.default.cyan(packageManager)}...`));
100
+ const installCommand = packageManager === "pnpm" ? "pnpm" : packageManager === "yarn" ? "yarn" : "npm";
101
+ const installArgs = packageManager === "pnpm" ? ["install"] : packageManager === "yarn" ? [] : ["install"];
102
+ await new Promise((resolve, reject) => {
103
+ const proc = (0, node_child_process_1.spawn)(installCommand, installArgs, { cwd: projectPath, stdio: "inherit" });
104
+ proc.on("close", (code) => {
105
+ if (code === 0)
106
+ resolve();
107
+ else
108
+ reject(new Error(`Install command exited with code ${code}`));
109
+ });
110
+ });
111
+ }
112
+ return { ok: true, projectPath };
113
+ }
114
+ catch (error) {
115
+ const reason = error instanceof Error ? error.message : "unknown error";
116
+ return { ok: false, error: `Failed to create project: ${reason}` };
117
+ }
118
+ }
119
+ async function createProject(options) {
120
+ const cwd = process.cwd();
121
+ let { name, framework, typescript, packageManager } = options;
122
+ if (!name) {
123
+ const response = await (0, prompts_1.default)({
124
+ type: "text",
125
+ name: "name",
126
+ message: "Project name:",
127
+ initial: "my-app",
128
+ validate: (value) => {
129
+ if (!value.trim())
130
+ return "Project name is required";
131
+ if (!/^[a-z0-9-]+$/.test(value))
132
+ return "Project name must contain only lowercase letters, numbers, and hyphens";
133
+ return true;
134
+ },
135
+ });
136
+ name = response.name;
137
+ }
138
+ if (!name) {
139
+ return { ok: false, error: "Project name is required" };
140
+ }
141
+ if (!framework) {
142
+ const response = await (0, prompts_1.default)({
143
+ type: "select",
144
+ name: "framework",
145
+ message: "Choose a framework:",
146
+ choices: [
147
+ { title: "Next.js", value: "next" },
148
+ { title: "React", value: "react" },
149
+ { title: "Vue", value: "vue" },
150
+ { title: "Svelte", value: "svelte" },
151
+ { title: "Solid", value: "solid" },
152
+ ],
153
+ });
154
+ framework = response.framework;
155
+ }
156
+ if (!framework) {
157
+ return { ok: false, error: "Framework selection is required" };
158
+ }
159
+ if (typescript === undefined) {
160
+ const response = await (0, prompts_1.default)({
161
+ type: "select",
162
+ name: "typescript",
163
+ message: "Use TypeScript?",
164
+ choices: [
165
+ { title: "Yes", value: true },
166
+ { title: "No", value: false },
167
+ ],
168
+ });
169
+ typescript = response.typescript;
170
+ }
171
+ if (typescript === undefined) {
172
+ typescript = true;
173
+ }
174
+ if (!packageManager) {
175
+ const detected = await detectPackageManager();
176
+ const choices = [
177
+ { title: "pnpm", value: "pnpm" },
178
+ { title: "npm", value: "npm" },
179
+ { title: "yarn", value: "yarn" },
180
+ ];
181
+ const response = await (0, prompts_1.default)({
182
+ type: "select",
183
+ name: "packageManager",
184
+ message: "Package manager:",
185
+ choices,
186
+ initial: Math.max(0, choices.findIndex((c) => c.value === detected)),
187
+ });
188
+ packageManager = response.packageManager;
189
+ }
190
+ if (!packageManager) {
191
+ packageManager = "npm";
192
+ }
193
+ const createResult = await runCreateCommand(name, framework, typescript, packageManager, cwd);
194
+ if (!createResult.ok) {
195
+ return createResult;
196
+ }
197
+ const projectPath = createResult.projectPath;
198
+ console.log(chalk_1.default.green("✓") + " Created application");
199
+ console.log(chalk_1.default.green("✓") + " Installed dependencies");
200
+ const fileEnv = (0, project_1.readProjectEnv)(projectPath);
201
+ const initResult = await (0, project_1.initProject)(projectPath, fileEnv);
202
+ if (!initResult.ok) {
203
+ console.error(chalk_1.default.yellow("!") + ` Could not initialize Lasso: ${initResult.error}`);
204
+ console.log(chalk_1.default.dim(`Run ${chalk_1.default.cyan("lasso init")} in the project directory to complete setup.`));
205
+ return { ok: true, projectPath };
206
+ }
207
+ console.log(chalk_1.default.green("✓") + " Connected Lasso");
208
+ console.log(chalk_1.default.green("✓") + " Registered project");
209
+ // Ask if user wants to describe their app for one-shot building
210
+ if (!options.prompt) {
211
+ const describeResponse = await (0, prompts_1.default)({
212
+ type: "select",
213
+ name: "describe",
214
+ message: "Do you want to describe your app for Lasso to build initial features?",
215
+ choices: [
216
+ { title: "Yes, describe my app", value: true },
217
+ { title: "No, I'll start from scratch", value: false },
218
+ ],
219
+ });
220
+ if (describeResponse.describe) {
221
+ const promptResponse = await (0, prompts_1.default)({
222
+ type: "text",
223
+ name: "appPrompt",
224
+ message: "Describe what you're building (e.g., A SaaS dashboard for managing customer invoices with billing and analytics):",
225
+ validate: (value) => {
226
+ if (!value.trim())
227
+ return "Please describe your app";
228
+ return true;
229
+ },
230
+ });
231
+ if (promptResponse.appPrompt) {
232
+ options.prompt = promptResponse.appPrompt;
233
+ }
234
+ }
235
+ }
236
+ if (options.prompt) {
237
+ console.log("");
238
+ console.log(chalk_1.default.dim(`✦ Lasso will build: ${chalk_1.default.cyan(options.prompt)}`));
239
+ console.log(chalk_1.default.dim(` Open your app and press ${chalk_1.default.cyan("⌘K")} to track progress.`));
240
+ }
241
+ console.log("");
242
+ console.log(chalk_1.default.bold("Your app is ready!"));
243
+ console.log("");
244
+ console.log(chalk_1.default.dim(`Next steps:`));
245
+ console.log(chalk_1.default.dim(` cd ${chalk_1.default.cyan(name)}`));
246
+ console.log(chalk_1.default.dim(` ${chalk_1.default.cyan("lasso dev")}`));
247
+ if (options.prompt) {
248
+ console.log(chalk_1.default.dim(` Then use ${chalk_1.default.cyan("⌘K")} to trigger one-shot mode`));
249
+ }
250
+ console.log("");
251
+ console.log(chalk_1.default.dim(`Your app will be available at ${chalk_1.default.cyan("http://localhost:3000")}`));
252
+ return { ok: true, projectPath };
253
+ }
package/dist/cli/index.js CHANGED
@@ -6,6 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const node_os_1 = __importDefault(require("node:os"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
+ const node_fs_1 = __importDefault(require("node:fs"));
9
10
  const chalk_1 = __importDefault(require("chalk"));
10
11
  const commander_1 = require("commander");
11
12
  const bridge_1 = require("./bridge");
@@ -20,6 +21,7 @@ const install_1 = require("./host/install");
20
21
  const client_1 = require("./host/client");
21
22
  const registry_1 = require("./host/registry");
22
23
  const next_config_1 = require("./next-config");
24
+ const create_1 = require("./create");
23
25
  const package_json_1 = __importDefault(require("../../package.json"));
24
26
  const VERSION = package_json_1.default.version;
25
27
  const program = new commander_1.Command();
@@ -81,6 +83,17 @@ program.command("init")
81
83
  console.log(chalk_1.default.dim(` ${project_1.LASSO_CONFIG_FILE} — commit it so teammates share this project.`));
82
84
  });
83
85
  // prettier-ignore
86
+ program.command("create [name]")
87
+ .description("Create a brand-new Lasso project with a framework scaffold")
88
+ .option("--prompt <prompt>", "Describe your app for one-shot building")
89
+ .action(async (name, options) => {
90
+ const result = await (0, create_1.createProject)({ name, prompt: options.prompt });
91
+ if (!result.ok) {
92
+ console.error(chalk_1.default.red("✗") + ` ${result.error}`);
93
+ process.exit(1);
94
+ }
95
+ });
96
+ // prettier-ignore
84
97
  const daemon = program.command("daemon [action]")
85
98
  .description("Start/stop Lasso Host — the local .lasso domain server (start|status|stop|restart|install|uninstall)")
86
99
  .usage("[action]")
@@ -353,6 +366,57 @@ auth.command("logout").description("Remove the Lasso credential from this machin
353
366
  console.log(chalk_1.default.green("✓") + " Signed out. Revoke the key from the dashboard if you no longer need it.");
354
367
  });
355
368
  // prettier-ignore
369
+ program.command("doctor")
370
+ .description("Diagnose configuration and integration problems")
371
+ .action(async () => {
372
+ const cwd = process.cwd();
373
+ console.log(chalk_1.default.bold("Lasso Doctor"));
374
+ console.log("");
375
+ // Check for lasso.config.json
376
+ const configPath = node_path_1.default.join(cwd, project_1.LASSO_CONFIG_FILE);
377
+ if (node_fs_1.default.existsSync(configPath)) {
378
+ console.log(chalk_1.default.green("✓") + ` ${project_1.LASSO_CONFIG_FILE} exists`);
379
+ try {
380
+ const config = JSON.parse(node_fs_1.default.readFileSync(configPath, "utf8"));
381
+ console.log(chalk_1.default.dim(` Project ID: ${config.id}`));
382
+ if (config.domain)
383
+ console.log(chalk_1.default.dim(` Domain: ${config.domain}`));
384
+ }
385
+ catch {
386
+ console.log(chalk_1.default.red("✗") + ` ${project_1.LASSO_CONFIG_FILE} is invalid`);
387
+ }
388
+ }
389
+ else {
390
+ console.log(chalk_1.default.yellow("!") + ` ${project_1.LASSO_CONFIG_FILE} not found. Run ${chalk_1.default.cyan("lasso init")} to create it.`);
391
+ }
392
+ // Check for API key
393
+ const credentials = (0, auth_1.loadCredentials)();
394
+ if (credentials) {
395
+ console.log(chalk_1.default.green("✓") + " Lasso API key configured");
396
+ }
397
+ else {
398
+ console.log(chalk_1.default.yellow("!") + " No Lasso API key found. Run " + chalk_1.default.cyan("lasso auth login"));
399
+ }
400
+ // Check framework
401
+ const framework = (0, framework_1.detectFramework)(cwd);
402
+ if (framework !== "unknown") {
403
+ console.log(chalk_1.default.green("✓") + ` Detected framework: ${framework}`);
404
+ }
405
+ else {
406
+ console.log(chalk_1.default.yellow("!") + " Could not detect a supported framework");
407
+ }
408
+ // Check package.json
409
+ const pkgPath = node_path_1.default.join(cwd, "package.json");
410
+ if (node_fs_1.default.existsSync(pkgPath)) {
411
+ console.log(chalk_1.default.green("✓") + " package.json exists");
412
+ }
413
+ else {
414
+ console.log(chalk_1.default.red("✗") + " package.json not found");
415
+ }
416
+ console.log("");
417
+ console.log(chalk_1.default.dim("Run ") + chalk_1.default.cyan("lasso dev") + chalk_1.default.dim(" to start development with Lasso."));
418
+ });
419
+ // prettier-ignore
356
420
  program.command("dev", { isDefault: true }).description("Start your dev server with the Lasso overlay attached").action(async () => {
357
421
  const cwd = process.cwd();
358
422
  const framework = (0, framework_1.detectFramework)(cwd);
@@ -0,0 +1,17 @@
1
+ import type { AgentConfig, SourceChange } from "./agent";
2
+ export interface OneShotResult {
3
+ ok: boolean;
4
+ summary?: string;
5
+ changes?: SourceChange[];
6
+ error?: string;
7
+ }
8
+ export interface ProgressCallback {
9
+ (status: "thinking" | "working" | "review" | "error" | "stopped", message: string, detail?: string): void;
10
+ }
11
+ export interface PromptCallback {
12
+ (prompt: {
13
+ question: string;
14
+ options?: string[];
15
+ }): void;
16
+ }
17
+ export declare function runOneShotAgent(cwd: string, prompt: string, scope: "project" | "component", config: AgentConfig, signal: AbortSignal, onProgress: ProgressCallback, onPrompt: PromptCallback): Promise<OneShotResult>;