komodo-cli 2.0.6 → 2.1.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.
@@ -4791,7 +4791,14 @@ async function installNodePackages(envPath, packages, onProgress) {
4791
4791
  try {
4792
4792
  for (const spec of packageSpecs) {
4793
4793
  onProgress?.(`Installing ${spec}...`);
4794
- const args = packageManager === "yarn" ? ["add", spec] : ["install", spec];
4794
+ let args;
4795
+ if (packageManager === "yarn") {
4796
+ args = ["add", spec];
4797
+ } else if (packageManager === "pnpm") {
4798
+ args = ["add", spec];
4799
+ } else {
4800
+ args = ["install", "--legacy-peer-deps", spec];
4801
+ }
4795
4802
  await execa2(packageManager, args, { cwd: envPath });
4796
4803
  installedPackages.push(spec);
4797
4804
  }
@@ -4931,10 +4938,402 @@ async function listSnapshots(basePath, environmentId) {
4931
4938
  }
4932
4939
  return state.snapshots;
4933
4940
  }
4941
+ var DEFAULT_MODEL = "llama-3.3-70b";
4942
+ var DEFAULT_MAX_TOKENS = 2048;
4943
+ var DEFAULT_TEMPERATURE = 0.2;
4944
+ var CerebrasAI = class {
4945
+ apiKey;
4946
+ model;
4947
+ maxTokens;
4948
+ temperature;
4949
+ baseUrl = "https://api.cerebras.ai/v1";
4950
+ constructor(config) {
4951
+ this.apiKey = config.apiKey;
4952
+ this.model = config.model ?? DEFAULT_MODEL;
4953
+ this.maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;
4954
+ this.temperature = config.temperature ?? DEFAULT_TEMPERATURE;
4955
+ }
4956
+ /**
4957
+ * Make a chat completion request to Cerebras API
4958
+ */
4959
+ async chatCompletion(messages) {
4960
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
4961
+ method: "POST",
4962
+ headers: {
4963
+ "Content-Type": "application/json",
4964
+ "Authorization": `Bearer ${this.apiKey}`
4965
+ },
4966
+ body: JSON.stringify({
4967
+ model: this.model,
4968
+ messages,
4969
+ max_tokens: this.maxTokens,
4970
+ temperature: this.temperature,
4971
+ top_p: 1
4972
+ })
4973
+ });
4974
+ if (!response.ok) {
4975
+ const error = await response.text();
4976
+ throw new Error(`Cerebras API error: ${response.status} - ${error}`);
4977
+ }
4978
+ const data = await response.json();
4979
+ return data.choices[0]?.message?.content ?? "";
4980
+ }
4981
+ /**
4982
+ * Analyze user intent and return comprehensive package recommendations
4983
+ */
4984
+ async analyzeIntent(userIntent, hardware) {
4985
+ const systemPrompt = this.buildSystemPrompt(hardware);
4986
+ const userPrompt = this.buildUserPrompt(userIntent, hardware);
4987
+ const messages = [
4988
+ { role: "system", content: systemPrompt },
4989
+ { role: "user", content: userPrompt }
4990
+ ];
4991
+ const response = await this.chatCompletion(messages);
4992
+ return this.parseAIResponse(response);
4993
+ }
4994
+ /**
4995
+ * Build comprehensive system prompt for the AI
4996
+ */
4997
+ buildSystemPrompt(hardware) {
4998
+ return `You are Komodo AI, an expert software environment setup assistant. Your job is to understand what the user wants to build and recommend the exact packages and setup they need.
4999
+
5000
+ You are knowledgeable about:
5001
+ - Python packages (PyPI): ML/AI frameworks (PyTorch, TensorFlow, JAX, transformers, langchain), web frameworks (FastAPI, Django, Flask), data science (pandas, numpy, scikit-learn), and all common Python packages
5002
+ - Node.js packages (npm): React, Vue, Next.js, Express, NestJS, and all frontend/backend JavaScript libraries
5003
+ - Hardware optimization: You know which packages work best with different GPUs (NVIDIA CUDA, AMD ROCm, Apple Metal) and CPUs
5004
+ - Version compatibility: You know which package versions work well together
5005
+
5006
+ CRITICAL RULES:
5007
+ 1. Always respond with valid JSON only - no markdown, no explanations outside the JSON
5008
+ 2. Be thorough - include ALL necessary packages for a complete working setup
5009
+ 3. Consider the user's hardware when recommending packages
5010
+ 4. Include dev dependencies if relevant (pytest, eslint, prettier, etc.)
5011
+ 5. Recommend specific versions when compatibility matters
5012
+ 6. If the request is ambiguous, make reasonable assumptions and explain them
5013
+
5014
+ USER'S HARDWARE:
5015
+ - OS: ${hardware.os}
5016
+ - Architecture: ${hardware.arch}
5017
+ - GPU: ${hardware.gpu}${hardware.gpuName ? ` (${hardware.gpuName})` : ""}${hardware.cudaVersion ? `, CUDA ${hardware.cudaVersion}` : ""}
5018
+ - RAM: ${hardware.totalMemoryGb}GB
5019
+ - CPU Cores: ${hardware.cpuCores}
5020
+
5021
+ Based on this hardware, optimize package recommendations accordingly.`;
5022
+ }
5023
+ /**
5024
+ * Build the user prompt with context
5025
+ */
5026
+ buildUserPrompt(intent, hardware) {
5027
+ return `The user wants to: "${intent}"
5028
+
5029
+ Analyze this request and respond with a JSON object in this exact format:
5030
+ {
5031
+ "goal": "A short, clear name for what they're building (e.g., 'ML Training Pipeline', 'Next.js Blog')",
5032
+ "description": "A brief description of the project and what it will do",
5033
+ "runtime": "python" or "node",
5034
+ "packages": [
5035
+ {
5036
+ "name": "package-name",
5037
+ "version": "specific version or 'latest'",
5038
+ "reason": "Why this package is needed",
5039
+ "optional": false,
5040
+ "alternatives": ["alternative-package"] // optional field
5041
+ }
5042
+ ],
5043
+ "setupSteps": [
5044
+ "Step 1 description",
5045
+ "Step 2 description"
5046
+ ],
5047
+ "warnings": [
5048
+ "Any warnings about compatibility, memory requirements, etc."
5049
+ ],
5050
+ "tips": [
5051
+ "Helpful tips for using the setup"
5052
+ ],
5053
+ "confidence": 0.95
5054
+ }
5055
+
5056
+ IMPORTANT:
5057
+ - Include ALL packages needed for a complete, working setup
5058
+ - Order packages by importance (core packages first)
5059
+ - For Python ML on Apple Silicon, use standard torch (Metal support is built-in)
5060
+ - For Python ML on NVIDIA, recommend torch with CUDA
5061
+ - For web projects, include necessary build tools and dev dependencies
5062
+ - If the request mentions specific technologies, include those
5063
+ - If the request is vague, include a sensible default stack
5064
+
5065
+ Respond ONLY with the JSON object, nothing else.`;
5066
+ }
5067
+ /**
5068
+ * Parse the AI response into structured data
5069
+ */
5070
+ parseAIResponse(response) {
5071
+ let cleaned = response.trim();
5072
+ if (cleaned.startsWith("```json")) {
5073
+ cleaned = cleaned.slice(7);
5074
+ } else if (cleaned.startsWith("```")) {
5075
+ cleaned = cleaned.slice(3);
5076
+ }
5077
+ if (cleaned.endsWith("```")) {
5078
+ cleaned = cleaned.slice(0, -3);
5079
+ }
5080
+ cleaned = cleaned.trim();
5081
+ try {
5082
+ const parsed = JSON.parse(cleaned);
5083
+ return {
5084
+ goal: parsed.goal || "Project Setup",
5085
+ description: parsed.description || "",
5086
+ runtime: this.normalizeRuntime(parsed.runtime),
5087
+ packages: this.normalizePackages(parsed.packages || []),
5088
+ setupSteps: Array.isArray(parsed.setupSteps) ? parsed.setupSteps : [],
5089
+ warnings: Array.isArray(parsed.warnings) ? parsed.warnings : [],
5090
+ tips: Array.isArray(parsed.tips) ? parsed.tips : [],
5091
+ confidence: typeof parsed.confidence === "number" ? parsed.confidence : 0.8
5092
+ };
5093
+ } catch (error) {
5094
+ console.error("Failed to parse AI response:", error);
5095
+ console.error("Raw response:", response);
5096
+ return {
5097
+ goal: "Project Setup",
5098
+ description: "AI analysis failed - using fallback",
5099
+ runtime: "python",
5100
+ packages: [],
5101
+ setupSteps: [],
5102
+ warnings: ["AI response parsing failed - please try again with a clearer description"],
5103
+ tips: [],
5104
+ confidence: 0
5105
+ };
5106
+ }
5107
+ }
5108
+ /**
5109
+ * Normalize runtime string to valid Runtime type
5110
+ */
5111
+ normalizeRuntime(runtime) {
5112
+ const lower = (runtime || "").toLowerCase();
5113
+ if (lower === "node" || lower === "nodejs" || lower === "javascript" || lower === "js") {
5114
+ return "node";
5115
+ }
5116
+ return "python";
5117
+ }
5118
+ /**
5119
+ * Normalize and validate package recommendations
5120
+ */
5121
+ normalizePackages(packages) {
5122
+ if (!Array.isArray(packages)) return [];
5123
+ return packages.filter((pkg) => typeof pkg === "object" && pkg !== null).map((pkg) => ({
5124
+ name: String(pkg["name"] || ""),
5125
+ version: String(pkg["version"] || "latest"),
5126
+ reason: String(pkg["reason"] || "Required dependency"),
5127
+ optional: Boolean(pkg["optional"]),
5128
+ alternatives: Array.isArray(pkg["alternatives"]) ? pkg["alternatives"].map(String) : void 0
5129
+ })).filter((pkg) => pkg.name.length > 0);
5130
+ }
5131
+ /**
5132
+ * Convert AI recommendations to ResolvedPackage format for Komodo
5133
+ */
5134
+ convertToResolvedPackages(recommendations, runtime) {
5135
+ return recommendations.filter((pkg) => !pkg.optional).map((pkg) => ({
5136
+ name: pkg.name,
5137
+ version: pkg.version,
5138
+ runtime,
5139
+ reason: pkg.reason
5140
+ }));
5141
+ }
5142
+ };
5143
+ var KomodoChat = class {
5144
+ apiKey;
5145
+ model;
5146
+ baseUrl = "https://api.cerebras.ai/v1";
5147
+ conversationHistory = [];
5148
+ context = null;
5149
+ constructor(apiKey, model = "llama-3.3-70b") {
5150
+ this.apiKey = apiKey;
5151
+ this.model = model;
5152
+ }
5153
+ /**
5154
+ * Set the current environment context
5155
+ */
5156
+ setContext(context) {
5157
+ this.context = context;
5158
+ this.conversationHistory = [];
5159
+ }
5160
+ /**
5161
+ * Clear conversation history
5162
+ */
5163
+ clearHistory() {
5164
+ this.conversationHistory = [];
5165
+ }
5166
+ /**
5167
+ * Send a message and get AI response
5168
+ */
5169
+ async chat(userMessage) {
5170
+ if (!this.context) {
5171
+ return {
5172
+ message: "No environment context set. Please run 'komodo list' or set up a project first."
5173
+ };
5174
+ }
5175
+ const systemPrompt = this.buildSystemPrompt();
5176
+ this.conversationHistory.push({
5177
+ role: "user",
5178
+ content: userMessage
5179
+ });
5180
+ const messages = [
5181
+ { role: "system", content: systemPrompt },
5182
+ ...this.conversationHistory
5183
+ ];
5184
+ try {
5185
+ const response = await this.callAPI(messages);
5186
+ this.conversationHistory.push({
5187
+ role: "assistant",
5188
+ content: response
5189
+ });
5190
+ return this.parseResponse(response);
5191
+ } catch (error) {
5192
+ return {
5193
+ message: `Error communicating with AI: ${error instanceof Error ? error.message : "Unknown error"}`
5194
+ };
5195
+ }
5196
+ }
5197
+ /**
5198
+ * Get a quick analysis of the current environment
5199
+ */
5200
+ async analyzeEnvironment() {
5201
+ return this.chat("Analyze my current environment. What's installed, are there any issues, and what do you recommend?");
5202
+ }
5203
+ /**
5204
+ * Get suggestions for what to do next
5205
+ */
5206
+ async getSuggestions() {
5207
+ return this.chat("Based on what I have installed, what are some things I could build or add next?");
5208
+ }
5209
+ /**
5210
+ * Ask about a specific package
5211
+ */
5212
+ async askAboutPackage(packageName) {
5213
+ return this.chat(`Tell me about ${packageName}. Is it installed? Are there any issues with it? Should I update it?`);
5214
+ }
5215
+ /**
5216
+ * Get help fixing conflicts
5217
+ */
5218
+ async getConflictHelp() {
5219
+ return this.chat("I have some package conflicts. Can you explain what's wrong and how to fix them?");
5220
+ }
5221
+ buildSystemPrompt() {
5222
+ const ctx = this.context;
5223
+ const installedList = ctx.installedPackages.length > 0 ? ctx.installedPackages.map((p) => ` - ${p.name}@${p.version}`).join("\n") : " (none)";
5224
+ const conflictsList = ctx.conflicts.length > 0 ? ctx.conflicts.map((c) => ` - ${c.package1} vs ${c.package2}: ${c.reason}`).join("\n") : " (none)";
5225
+ const issuesList = ctx.healthIssues.length > 0 ? ctx.healthIssues.map((i) => ` - [${i.severity}] ${i.title}: ${i.description}`).join("\n") : " (none)";
5226
+ return `You are Komodo AI, a helpful assistant for managing software development environments. You help users understand their installed packages, resolve conflicts, and make smart decisions about their project dependencies.
5227
+
5228
+ CURRENT ENVIRONMENT:
5229
+ - Project: ${ctx.environmentName || "Unknown"}
5230
+ - Path: ${ctx.projectPath}
5231
+ - Runtime: ${ctx.runtime || "Not set"}
5232
+ - Hardware: ${ctx.hardware.os} ${ctx.hardware.arch}, ${ctx.hardware.gpu !== "none" ? ctx.hardware.gpuName || ctx.hardware.gpu : "CPU only"}, ${ctx.hardware.totalMemoryGb}GB RAM
5233
+
5234
+ INSTALLED PACKAGES (${ctx.installedPackages.length}):
5235
+ ${installedList}
5236
+
5237
+ CONFLICTS (${ctx.conflicts.length}):
5238
+ ${conflictsList}
5239
+
5240
+ HEALTH ISSUES (${ctx.healthIssues.length}):
5241
+ ${issuesList}
5242
+
5243
+ YOUR ROLE:
5244
+ 1. Answer questions about the installed packages and environment
5245
+ 2. Explain conflicts and suggest resolutions
5246
+ 3. Recommend packages to add, update, or remove
5247
+ 4. Provide guidance on best practices
5248
+ 5. Suggest what the user could build with their current setup
5249
+
5250
+ RESPONSE FORMAT:
5251
+ Always respond in a helpful, conversational tone. When suggesting actions, be specific about what commands to run or packages to install/remove.
5252
+
5253
+ If you recommend installing, removing, or updating packages, include a JSON block at the end of your response in this format:
5254
+ \`\`\`json
5255
+ {
5256
+ "actions": [
5257
+ {"type": "install", "packages": ["package1", "package2"]},
5258
+ {"type": "remove", "packages": ["old-package"]},
5259
+ {"type": "update", "packages": [{"name": "pkg", "to": "2.0.0"}]}
5260
+ ]
5261
+ }
5262
+ \`\`\`
5263
+
5264
+ Only include the JSON block if you're recommending specific package changes.`;
5265
+ }
5266
+ async callAPI(messages) {
5267
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
5268
+ method: "POST",
5269
+ headers: {
5270
+ "Content-Type": "application/json",
5271
+ "Authorization": `Bearer ${this.apiKey}`
5272
+ },
5273
+ body: JSON.stringify({
5274
+ model: this.model,
5275
+ messages,
5276
+ max_tokens: 2048,
5277
+ temperature: 0.3,
5278
+ top_p: 1
5279
+ })
5280
+ });
5281
+ if (!response.ok) {
5282
+ throw new Error(`API error: ${response.status}`);
5283
+ }
5284
+ const data = await response.json();
5285
+ return data.choices[0]?.message?.content ?? "";
5286
+ }
5287
+ parseResponse(response) {
5288
+ const result = {
5289
+ message: response
5290
+ };
5291
+ const jsonMatch = response.match(/```json\s*([\s\S]*?)\s*```/);
5292
+ if (jsonMatch && jsonMatch[1]) {
5293
+ try {
5294
+ const actions = JSON.parse(jsonMatch[1]);
5295
+ if (actions.actions) {
5296
+ result.actions = actions.actions;
5297
+ result.packages = {
5298
+ toInstall: [],
5299
+ toRemove: [],
5300
+ toUpdate: []
5301
+ };
5302
+ for (const action of actions.actions) {
5303
+ if (action.type === "install" && action.packages) {
5304
+ result.packages.toInstall.push(...action.packages);
5305
+ } else if (action.type === "remove" && action.packages) {
5306
+ result.packages.toRemove.push(...action.packages);
5307
+ } else if (action.type === "update" && action.packages) {
5308
+ result.packages.toUpdate.push(...action.packages);
5309
+ }
5310
+ }
5311
+ }
5312
+ result.message = response.replace(/```json\s*[\s\S]*?\s*```/, "").trim();
5313
+ } catch {
5314
+ }
5315
+ }
5316
+ const suggestionMatch = response.match(/(?:suggestions?|recommend|could|try)[:.]?\s*\n((?:\s*[-•*]\s*.+\n?)+)/i);
5317
+ if (suggestionMatch && suggestionMatch[1]) {
5318
+ result.suggestions = suggestionMatch[1].split("\n").map((s) => s.replace(/^\s*[-•*]\s*/, "").trim()).filter((s) => s.length > 0);
5319
+ }
5320
+ return result;
5321
+ }
5322
+ };
4934
5323
  var Komodo = class {
4935
5324
  hardware;
4936
- constructor() {
5325
+ ai = null;
5326
+ constructor(apiKey) {
4937
5327
  this.hardware = detectHardware();
5328
+ if (apiKey) {
5329
+ this.ai = new CerebrasAI({ apiKey });
5330
+ }
5331
+ }
5332
+ /**
5333
+ * Initialize or update the AI service with an API key
5334
+ */
5335
+ setApiKey(apiKey) {
5336
+ this.ai = new CerebrasAI({ apiKey });
4938
5337
  }
4939
5338
  getHardware() {
4940
5339
  return this.hardware;
@@ -4948,13 +5347,61 @@ var Komodo = class {
4948
5347
  resolve(intent) {
4949
5348
  return resolveIntent(intent, this.hardware);
4950
5349
  }
5350
+ /**
5351
+ * Use AI to analyze intent - returns detailed recommendations
5352
+ */
5353
+ async analyzeWithAI(intent, apiKey) {
5354
+ const ai = apiKey ? new CerebrasAI({ apiKey }) : this.ai;
5355
+ if (!ai) {
5356
+ throw new Error("AI not initialized. Provide an API key or call setApiKey() first.");
5357
+ }
5358
+ return ai.analyzeIntent(intent, this.hardware);
5359
+ }
4951
5360
  async install(options) {
4952
- const { intent, path: basePath = process.cwd(), dryRun, onProgress, onExplanation, onWarning } = options;
5361
+ const {
5362
+ intent,
5363
+ path: basePath = process.cwd(),
5364
+ dryRun,
5365
+ useAI = true,
5366
+ apiKey,
5367
+ onProgress,
5368
+ onExplanation,
5369
+ onWarning,
5370
+ onTip
5371
+ } = options;
4953
5372
  const targetPath = resolve(basePath);
4954
- onProgress?.("Parsing intent...");
4955
- const parsed = this.parseIntent(intent);
4956
- onProgress?.("Resolving packages for your hardware...");
4957
- const resolution = this.resolve(parsed);
5373
+ let aiAnalysis;
5374
+ let resolution;
5375
+ const effectiveApiKey = apiKey || process.env["CEREBRAS_API_KEY"];
5376
+ const shouldUseAI = useAI && effectiveApiKey;
5377
+ if (shouldUseAI) {
5378
+ try {
5379
+ onProgress?.("Understanding what you need...");
5380
+ const ai = new CerebrasAI({ apiKey: effectiveApiKey });
5381
+ aiAnalysis = await ai.analyzeIntent(intent, this.hardware);
5382
+ resolution = {
5383
+ packages: ai.convertToResolvedPackages(aiAnalysis.packages, aiAnalysis.runtime),
5384
+ runtime: aiAnalysis.runtime,
5385
+ pythonVersion: aiAnalysis.runtime === "python" ? "3.11" : void 0,
5386
+ nodeVersion: aiAnalysis.runtime === "node" ? "20" : void 0,
5387
+ warnings: aiAnalysis.warnings,
5388
+ explanations: [aiAnalysis.description, ...aiAnalysis.setupSteps]
5389
+ };
5390
+ for (const tip of aiAnalysis.tips) {
5391
+ onTip?.(tip);
5392
+ }
5393
+ onProgress?.(`Setting up: ${aiAnalysis.goal}`);
5394
+ } catch (error) {
5395
+ onProgress?.("AI analysis unavailable, using smart templates...");
5396
+ const parsed = this.parseIntent(intent);
5397
+ resolution = this.resolve(parsed);
5398
+ }
5399
+ } else {
5400
+ onProgress?.("Parsing intent...");
5401
+ const parsed = this.parseIntent(intent);
5402
+ onProgress?.("Resolving packages for your hardware...");
5403
+ resolution = this.resolve(parsed);
5404
+ }
4958
5405
  for (const explanation of resolution.explanations) {
4959
5406
  onExplanation?.(explanation);
4960
5407
  }
@@ -4966,23 +5413,26 @@ var Komodo = class {
4966
5413
  success: false,
4967
5414
  error: "Could not determine packages for this intent. Try being more specific.",
4968
5415
  resolution,
4969
- hardware: this.hardware
5416
+ hardware: this.hardware,
5417
+ aiAnalysis
4970
5418
  };
4971
5419
  }
4972
5420
  if (dryRun) {
4973
5421
  return {
4974
5422
  success: true,
4975
5423
  resolution,
4976
- hardware: this.hardware
5424
+ hardware: this.hardware,
5425
+ aiAnalysis
4977
5426
  };
4978
5427
  }
4979
5428
  if (!existsSync3(targetPath)) {
4980
5429
  await mkdir3(targetPath, { recursive: true });
4981
5430
  }
4982
5431
  const environmentId = randomUUID2();
5432
+ const environmentName = aiAnalysis?.goal ?? intent.slice(0, 50);
4983
5433
  const environment = {
4984
5434
  id: environmentId,
4985
- name: parsed.goal,
5435
+ name: environmentName,
4986
5436
  createdAt: /* @__PURE__ */ new Date(),
4987
5437
  runtime: resolution.runtime,
4988
5438
  path: targetPath,
@@ -5002,7 +5452,8 @@ var Komodo = class {
5002
5452
  success: false,
5003
5453
  error: envResult.error,
5004
5454
  resolution,
5005
- hardware: this.hardware
5455
+ hardware: this.hardware,
5456
+ aiAnalysis
5006
5457
  };
5007
5458
  }
5008
5459
  const installResult = await installPythonPackages(
@@ -5015,7 +5466,8 @@ var Komodo = class {
5015
5466
  success: false,
5016
5467
  error: installResult.error,
5017
5468
  resolution,
5018
- hardware: this.hardware
5469
+ hardware: this.hardware,
5470
+ aiAnalysis
5019
5471
  };
5020
5472
  }
5021
5473
  onProgress?.("Saving lockfile...");
@@ -5032,7 +5484,8 @@ var Komodo = class {
5032
5484
  success: false,
5033
5485
  error: envResult.error,
5034
5486
  resolution,
5035
- hardware: this.hardware
5487
+ hardware: this.hardware,
5488
+ aiAnalysis
5036
5489
  };
5037
5490
  }
5038
5491
  const installResult = await installNodePackages(
@@ -5045,7 +5498,8 @@ var Komodo = class {
5045
5498
  success: false,
5046
5499
  error: installResult.error,
5047
5500
  resolution,
5048
- hardware: this.hardware
5501
+ hardware: this.hardware,
5502
+ aiAnalysis
5049
5503
  };
5050
5504
  }
5051
5505
  onProgress?.("Saving lockfile...");
@@ -5057,19 +5511,21 @@ var Komodo = class {
5057
5511
  state.activeEnvironmentId = environment.id;
5058
5512
  await saveState(targetPath, state);
5059
5513
  onProgress?.("Creating snapshot for rollback...");
5060
- await createSnapshot(targetPath, environment, "Initial install");
5514
+ await createSnapshot(targetPath, environment, aiAnalysis?.goal ?? "Initial install");
5061
5515
  return {
5062
5516
  success: true,
5063
5517
  environment,
5064
5518
  resolution,
5065
- hardware: this.hardware
5519
+ hardware: this.hardware,
5520
+ aiAnalysis
5066
5521
  };
5067
5522
  } catch (error) {
5068
5523
  return {
5069
5524
  success: false,
5070
5525
  error: error instanceof Error ? error.message : "Installation failed",
5071
5526
  resolution,
5072
- hardware: this.hardware
5527
+ hardware: this.hardware,
5528
+ aiAnalysis
5073
5529
  };
5074
5530
  }
5075
5531
  }
@@ -5253,6 +5709,7 @@ export {
5253
5709
  __commonJS,
5254
5710
  __toESM,
5255
5711
  loadState,
5712
+ KomodoChat,
5256
5713
  Komodo,
5257
5714
  getInstalledPackages,
5258
5715
  detectConflicts,
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Komodo,
4
+ KomodoChat,
4
5
  analyzeHealth,
5
6
  detectConflicts,
6
7
  getInstalledPackages,
7
8
  loadState
8
- } from "./chunk-NTFP7K7V.js";
9
+ } from "./chunk-VHGNG7AW.js";
9
10
 
10
11
  // src/index.ts
11
12
  import { Command } from "commander";
@@ -16,7 +17,10 @@ import * as readline from "readline";
16
17
  import { createRequire } from "module";
17
18
  var require2 = createRequire(import.meta.url);
18
19
  var packageJson = require2("../package.json");
19
- var komodo = new Komodo();
20
+ var DEFAULT_API_KEY = "csk-m4vcnx94p854xmvnhxx38chwmxxwtffpnymk2ewexktk3962";
21
+ var API_KEY = process.env.CEREBRAS_API_KEY || DEFAULT_API_KEY;
22
+ var komodo = new Komodo(API_KEY);
23
+ var chat = new KomodoChat(API_KEY);
20
24
  var program = new Command();
21
25
  program.name("komodo").description("The simple way to set up your project").version(packageJson.version);
22
26
  var gradientColors = [
@@ -68,13 +72,90 @@ function formatGpu(hardware) {
68
72
  }
69
73
  return "CPU";
70
74
  }
75
+ async function updateChatContext(projectPath) {
76
+ const hardware = komodo.getHardware();
77
+ const state = await loadState(projectPath);
78
+ let installedPackages = [];
79
+ let conflicts = [];
80
+ let healthIssues = [];
81
+ let runtime;
82
+ let environmentName;
83
+ if (state.activeEnvironmentId) {
84
+ const activeEnv = state.environments.find((e) => e.id === state.activeEnvironmentId);
85
+ if (activeEnv) {
86
+ runtime = activeEnv.runtime;
87
+ environmentName = activeEnv.name;
88
+ installedPackages = await getInstalledPackages(projectPath, activeEnv.runtime);
89
+ conflicts = detectConflicts(installedPackages);
90
+ const health = analyzeHealth(installedPackages, conflicts);
91
+ healthIssues = health.issues;
92
+ }
93
+ }
94
+ const context = {
95
+ hardware,
96
+ runtime,
97
+ installedPackages,
98
+ conflicts,
99
+ healthIssues,
100
+ projectPath,
101
+ environmentName
102
+ };
103
+ chat.setContext(context);
104
+ return { installedPackages, conflicts, healthIssues, runtime, environmentName };
105
+ }
106
+ function isAIQuestion(input) {
107
+ const lowerInput = input.toLowerCase();
108
+ if (input.endsWith("?")) return true;
109
+ const questionWords = ["what", "why", "how", "when", "where", "which", "who", "can", "could", "should", "would", "is", "are", "do", "does", "tell me", "explain", "show me", "help me"];
110
+ if (questionWords.some((w) => lowerInput.startsWith(w))) return true;
111
+ const envKeywords = ["installed", "packages", "conflicts", "issues", "problems", "wrong", "fix", "update", "upgrade", "remove", "alternatives", "suggest", "recommend", "analyze", "status"];
112
+ if (envKeywords.some((k) => lowerInput.includes(k))) return true;
113
+ return false;
114
+ }
115
+ function displayAIResponse(response) {
116
+ console.log();
117
+ console.log(chalk.hex("#b4ffb4")(response.message));
118
+ if (response.suggestions && response.suggestions.length > 0) {
119
+ console.log();
120
+ console.log(chalk.hex("#87cefa")(" Suggestions:"));
121
+ response.suggestions.forEach((s) => {
122
+ console.log(chalk.hex("#87cefa")(` \u2022 ${s}`));
123
+ });
124
+ }
125
+ if (response.packages) {
126
+ if (response.packages.toInstall && response.packages.toInstall.length > 0) {
127
+ console.log();
128
+ console.log(chalk.hex("#5aff5a")(" Packages to install:"));
129
+ response.packages.toInstall.forEach((p) => {
130
+ console.log(chalk.hex("#5aff5a")(` + ${p}`));
131
+ });
132
+ }
133
+ if (response.packages.toRemove && response.packages.toRemove.length > 0) {
134
+ console.log();
135
+ console.log(chalk.yellow(" Packages to remove:"));
136
+ response.packages.toRemove.forEach((p) => {
137
+ console.log(chalk.yellow(` - ${p}`));
138
+ });
139
+ }
140
+ if (response.packages.toUpdate && response.packages.toUpdate.length > 0) {
141
+ console.log();
142
+ console.log(chalk.hex("#87cefa")(" Packages to update:"));
143
+ response.packages.toUpdate.forEach((p) => {
144
+ console.log(chalk.hex("#87cefa")(` \u2191 ${p.name}: ${p.from} \u2192 ${p.to}`));
145
+ });
146
+ }
147
+ }
148
+ console.log();
149
+ }
71
150
  async function startInteractiveMode(projectPath) {
72
151
  printBanner();
73
152
  const hardware = komodo.getHardware();
74
153
  console.log(chalk.hex("#b4ffb4").dim(` ${formatOs(hardware.os)} \xB7 ${formatGpu(hardware)} \xB7 ${hardware.totalMemoryGb}GB memory`));
75
154
  console.log();
76
- console.log(chalk.hex("#96ff96").dim(" Commands: ") + chalk.hex("#d2ffd2")("undo") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("list") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("check") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("ui") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("exit"));
155
+ console.log(chalk.hex("#96ff96").dim(" Commands: ") + chalk.hex("#d2ffd2")("undo") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("list") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("check") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("ask") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("exit"));
156
+ console.log(chalk.dim(" Or just ask anything in natural language!"));
77
157
  console.log();
158
+ await updateChatContext(projectPath);
78
159
  const rl = readline.createInterface({
79
160
  input: process.stdin,
80
161
  output: process.stdout
@@ -101,15 +182,18 @@ async function startInteractiveMode(projectPath) {
101
182
  }
102
183
  if (trimmed === "help" || trimmed === "?") {
103
184
  console.log();
104
- console.log(chalk.hex("#a5ffa5")(" Just type what you want to build:"));
185
+ console.log(chalk.hex("#a5ffa5")(" Just type what you want to build or ask anything:"));
105
186
  console.log(chalk.dim(' "build a website"'));
106
- console.log(chalk.dim(' "train an AI model"'));
107
- console.log(chalk.dim(' "analyze some data"'));
187
+ console.log(chalk.dim(' "what packages are installed?"'));
188
+ console.log(chalk.dim(' "are there any conflicts?"'));
189
+ console.log(chalk.dim(' "suggest alternatives to express"'));
108
190
  console.log();
109
191
  console.log(chalk.hex("#a5ffa5")(" Commands:"));
110
192
  console.log(` ${chalk.hex("#d2ffd2")("undo")} Undo last change`);
111
193
  console.log(` ${chalk.hex("#d2ffd2")("list")} See what's installed`);
112
194
  console.log(` ${chalk.hex("#d2ffd2")("check")} Check for problems`);
195
+ console.log(` ${chalk.hex("#d2ffd2")("ask")} Start AI conversation`);
196
+ console.log(` ${chalk.hex("#d2ffd2")("analyze")} AI analysis of environment`);
113
197
  console.log(` ${chalk.hex("#d2ffd2")("ui")} Open visual dashboard`);
114
198
  console.log(` ${chalk.hex("#d2ffd2")("history")} See past changes`);
115
199
  console.log(` ${chalk.hex("#d2ffd2")("clear")} Clear screen`);
@@ -120,6 +204,7 @@ async function startInteractiveMode(projectPath) {
120
204
  }
121
205
  if (trimmed === "undo") {
122
206
  await handleUndo(projectPath);
207
+ await updateChatContext(projectPath);
123
208
  prompt();
124
209
  return;
125
210
  }
@@ -143,6 +228,41 @@ async function startInteractiveMode(projectPath) {
143
228
  prompt();
144
229
  return;
145
230
  }
231
+ if (trimmed === "analyze" || trimmed === "analyse") {
232
+ const spinner = ora(chalk.hex("#b4ffb4")("Analyzing your environment...")).start();
233
+ await updateChatContext(projectPath);
234
+ const response = await chat.analyzeEnvironment();
235
+ spinner.stop();
236
+ displayAIResponse(response);
237
+ prompt();
238
+ return;
239
+ }
240
+ if (trimmed === "ask" || trimmed.startsWith("ask ")) {
241
+ const question = trimmed === "ask" ? "" : trimmed.slice(4).trim();
242
+ if (!question) {
243
+ console.log();
244
+ console.log(chalk.hex("#b4ffb4")(" Ask me anything about your environment!"));
245
+ console.log(chalk.dim(" Example: ask what should I install for a REST API?"));
246
+ console.log();
247
+ } else {
248
+ const spinner = ora(chalk.hex("#b4ffb4")("Thinking...")).start();
249
+ await updateChatContext(projectPath);
250
+ const response = await chat.chat(question);
251
+ spinner.stop();
252
+ displayAIResponse(response);
253
+ }
254
+ prompt();
255
+ return;
256
+ }
257
+ if (isAIQuestion(trimmed)) {
258
+ const spinner = ora(chalk.hex("#b4ffb4")("Thinking...")).start();
259
+ await updateChatContext(projectPath);
260
+ const response = await chat.chat(trimmed);
261
+ spinner.stop();
262
+ displayAIResponse(response);
263
+ prompt();
264
+ return;
265
+ }
146
266
  await handleInstall(trimmed, projectPath);
147
267
  prompt();
148
268
  });
@@ -154,10 +274,13 @@ async function handleInstall(intent, projectPath) {
154
274
  console.log();
155
275
  const explanations = [];
156
276
  const warnings = [];
277
+ const tips = [];
157
278
  const result = await komodo.install({
158
279
  intent,
159
280
  path: projectPath,
160
281
  dryRun: false,
282
+ useAI: true,
283
+ apiKey: API_KEY,
161
284
  onProgress: (message) => {
162
285
  const friendly = friendlyMessage(message);
163
286
  if (spinner.isSpinning) {
@@ -170,6 +293,9 @@ async function handleInstall(intent, projectPath) {
170
293
  },
171
294
  onWarning: (warning) => {
172
295
  warnings.push(friendlyWarning(warning));
296
+ },
297
+ onTip: (tip) => {
298
+ tips.push(tip);
173
299
  }
174
300
  });
175
301
  if (spinner.isSpinning) {
@@ -177,10 +303,18 @@ async function handleInstall(intent, projectPath) {
177
303
  }
178
304
  console.log();
179
305
  if (result.success) {
306
+ if (result.aiAnalysis) {
307
+ console.log(chalk.hex("#5aff5a").bold(` \u2713 ${result.aiAnalysis.goal}`));
308
+ if (result.aiAnalysis.description) {
309
+ console.log(chalk.dim(` ${result.aiAnalysis.description}`));
310
+ }
311
+ console.log();
312
+ }
180
313
  if (result.resolution && result.resolution.packages.length > 0) {
181
314
  console.log(chalk.hex("#a5ffa5")(" Set up:"));
182
315
  result.resolution.packages.forEach((pkg) => {
183
- console.log(chalk.hex("#5aff5a")(` \u2713 ${friendlyPackageName(pkg.name)}`));
316
+ const reason = pkg.reason ? chalk.dim(` - ${pkg.reason}`) : "";
317
+ console.log(chalk.hex("#5aff5a")(` \u2713 ${friendlyPackageName(pkg.name)}`) + reason);
184
318
  });
185
319
  console.log();
186
320
  }
@@ -191,6 +325,13 @@ async function handleInstall(intent, projectPath) {
191
325
  });
192
326
  console.log();
193
327
  }
328
+ if (tips.length > 0) {
329
+ console.log(chalk.hex("#87cefa")(" Tips:"));
330
+ tips.forEach((t) => {
331
+ console.log(chalk.hex("#87cefa")(` \u{1F4A1} ${t}`));
332
+ });
333
+ console.log();
334
+ }
194
335
  if (warnings.length > 0) {
195
336
  console.log(chalk.yellow(" Heads up:"));
196
337
  warnings.forEach((w) => {
@@ -318,7 +459,7 @@ async function handleUI(projectPath) {
318
459
  console.log();
319
460
  console.log(chalk.hex("#b4ffb4").dim(" Starting dashboard..."));
320
461
  try {
321
- const { startServer } = await import("./server-SEUGSMGX.js");
462
+ const { startServer } = await import("./server-2Z47U3WP.js");
322
463
  await startServer(projectPath, port);
323
464
  console.log(chalk.hex("#5aff5a")(` \u2713 Dashboard ready at ${chalk.bold(url)}`));
324
465
  console.log();
@@ -350,10 +491,13 @@ program.argument("[intent]", "What you want to build").option("-p, --path <path>
350
491
  }
351
492
  const explanations = [];
352
493
  const warnings = [];
494
+ const tips = [];
353
495
  const result = await komodo.install({
354
496
  intent,
355
497
  path: options.path,
356
498
  dryRun: options.preview,
499
+ useAI: true,
500
+ apiKey: API_KEY,
357
501
  onProgress: (message) => {
358
502
  const friendly = friendlyMessage(message);
359
503
  if (spinner.isSpinning) {
@@ -366,6 +510,9 @@ program.argument("[intent]", "What you want to build").option("-p, --path <path>
366
510
  },
367
511
  onWarning: (warning) => {
368
512
  warnings.push(friendlyWarning(warning));
513
+ },
514
+ onTip: (tip) => {
515
+ tips.push(tip);
369
516
  }
370
517
  });
371
518
  if (spinner.isSpinning) {
@@ -373,10 +520,32 @@ program.argument("[intent]", "What you want to build").option("-p, --path <path>
373
520
  }
374
521
  console.log();
375
522
  if (result.success) {
523
+ if (result.aiAnalysis) {
524
+ console.log(chalk.green.bold(` \u2713 ${result.aiAnalysis.goal}`));
525
+ if (result.aiAnalysis.description) {
526
+ console.log(chalk.dim(` ${result.aiAnalysis.description}`));
527
+ }
528
+ console.log();
529
+ }
376
530
  if (result.resolution && result.resolution.packages.length > 0) {
377
531
  console.log(chalk.bold(" Set up:"));
378
532
  result.resolution.packages.forEach((pkg) => {
379
- console.log(chalk.green(` \u2713 ${friendlyPackageName(pkg.name)}`));
533
+ const reason = pkg.reason ? chalk.dim(` - ${pkg.reason}`) : "";
534
+ console.log(chalk.green(` \u2713 ${friendlyPackageName(pkg.name)}`) + reason);
535
+ });
536
+ console.log();
537
+ }
538
+ if (result.aiAnalysis && result.aiAnalysis.setupSteps.length > 0) {
539
+ console.log(chalk.dim(" Next steps:"));
540
+ result.aiAnalysis.setupSteps.forEach((step, i) => {
541
+ console.log(chalk.dim(` ${i + 1}. ${step}`));
542
+ });
543
+ console.log();
544
+ }
545
+ if (tips.length > 0) {
546
+ console.log(chalk.hex("#87cefa")(" Tips:"));
547
+ tips.forEach((t) => {
548
+ console.log(chalk.hex("#87cefa")(` \u{1F4A1} ${t}`));
380
549
  });
381
550
  console.log();
382
551
  }
@@ -437,7 +606,7 @@ program.command("ui").description("Open the visual dashboard").option("-p, --pat
437
606
  const url = `http://localhost:${port}`;
438
607
  console.log(chalk.dim(" Starting dashboard..."));
439
608
  try {
440
- const { startServer } = await import("./server-SEUGSMGX.js");
609
+ const { startServer } = await import("./server-2Z47U3WP.js");
441
610
  await startServer(options.path, port);
442
611
  console.log();
443
612
  console.log(chalk.green(` \u2713 Dashboard ready at ${chalk.bold(url)}`));
@@ -7,7 +7,7 @@ import {
7
7
  detectConflicts,
8
8
  getInstalledPackages,
9
9
  loadState
10
- } from "./chunk-NTFP7K7V.js";
10
+ } from "./chunk-VHGNG7AW.js";
11
11
 
12
12
  // ../../node_modules/.pnpm/reusify@1.1.0/node_modules/reusify/reusify.js
13
13
  var require_reusify = __commonJS({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "komodo-cli",
3
- "version": "2.0.6",
3
+ "version": "2.1.1",
4
4
  "description": "The simple way to set up your project. Just say what you want to build.",
5
5
  "type": "module",
6
6
  "bin": {