tf-ai 0.1.6 → 0.1.7

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/README.md CHANGED
@@ -78,6 +78,15 @@ PS> fuck
78
78
  [Enter] Run [e] Edit [Esc] Cancel
79
79
  ```
80
80
 
81
+ #### Flags
82
+
83
+ | Flag | Description |
84
+ |------|-------------|
85
+ | `fuck` | Analyze the last command (default - doesn't re-run it) |
86
+ | `fuck -r` | Re-run the last command to capture its output, then analyze |
87
+
88
+ > **Tip**: Use `fuck -r` when the error output is important for diagnosis. The default mode is faster and works well for typos and common mistakes.
89
+
81
90
  ### 3. Direct Usage
82
91
 
83
92
  You can also use it manually to analyze specific errors:
package/dist/index.js CHANGED
@@ -323,8 +323,7 @@ Add this to your PowerShell profile ($PROFILE):
323
323
 
324
324
  function fuck {
325
325
  param(
326
- [Alias("r")][switch]$Rerun,
327
- [Alias("s")][switch]$Skip
326
+ [Alias("r")][switch]$Rerun
328
327
  )
329
328
 
330
329
  # Get last 3 commands from history (for context)
@@ -354,39 +353,20 @@ function fuck {
354
353
 
355
354
  $output = ""
356
355
 
357
- # Handle flags: -r/--Rerun to auto-rerun, -s/--Skip to skip
356
+ Write-Host "Last command: " -NoNewline
357
+ Write-Host $lastCmd -ForegroundColor Cyan
358
+
359
+ # Use -r/--Rerun flag to re-run the command for output capture
358
360
  if ($Rerun) {
359
- Write-Host "Last command: " -NoNewline
360
- Write-Host $lastCmd -ForegroundColor Cyan
361
361
  Write-Host "Re-running to capture output..." -ForegroundColor Gray
362
362
  $output = try {
363
363
  Invoke-Expression $lastCmd 2>&1 | Out-String
364
364
  } catch {
365
365
  $_.Exception.Message
366
366
  }
367
- } elseif ($Skip) {
368
- Write-Host "Last command: " -NoNewline
369
- Write-Host $lastCmd -ForegroundColor Cyan
370
- Write-Host "Skipping re-execution (no output capture)" -ForegroundColor Gray
371
- $output = "(output not captured - skipped via flag)"
372
367
  } else {
373
- # Interactive mode: ask for confirmation
374
- Write-Host "Last command: " -NoNewline
375
- Write-Host $lastCmd -ForegroundColor Cyan
376
- $confirm = Read-Host "Re-run to capture output? (y/n)"
377
-
378
- if ($confirm -eq "y" -or $confirm -eq "Y") {
379
- $output = try {
380
- Invoke-Expression $lastCmd 2>&1 | Out-String
381
- } catch {
382
- $_.Exception.Message
383
- }
384
- } elseif ($confirm -eq "n" -or $confirm -eq "N") {
385
- $output = "(output not captured - user skipped re-execution)"
386
- } else {
387
- Write-Host "Cancelled" -ForegroundColor Yellow
388
- return
389
- }
368
+ # Default: skip re-execution (no output capture)
369
+ $output = "(output not captured)"
390
370
  }
391
371
 
392
372
  # Call tf-ai with captured command, output, and history
@@ -398,11 +378,9 @@ function fuck {
398
378
  }
399
379
 
400
380
  # Usage:
401
- # fuck - Interactive mode (prompts for confirmation)
402
- # fuck -r - Auto re-run last command to capture output
403
- # fuck -Rerun - Same as -r
404
- # fuck -s - Skip re-execution, analyze without output
405
- # fuck -Skip - Same as -s
381
+ # fuck - Analyze last command (without re-running it)
382
+ # fuck -r - Re-run last command to capture output, then analyze
383
+ # fuck -Rerun - Same as -r
406
384
 
407
385
  Then reload your profile:
408
386
  . $PROFILE
@@ -456,7 +434,6 @@ var BashAdapter = class {
456
434
 
457
435
  function fuck() {
458
436
  local rerun=false
459
- local skip=false
460
437
 
461
438
  # Parse arguments
462
439
  while [[ $# -gt 0 ]]; do
@@ -465,13 +442,9 @@ function fuck() {
465
442
  rerun=true
466
443
  shift
467
444
  ;;
468
- -s|--skip)
469
- skip=true
470
- shift
471
- ;;
472
445
  *)
473
446
  echo "Unknown option: $1"
474
- echo "Usage: fuck [-r|--rerun] [-s|--skip]"
447
+ echo "Usage: fuck [-r|--rerun]"
475
448
  return 1
476
449
  ;;
477
450
  esac
@@ -520,36 +493,16 @@ function fuck() {
520
493
 
521
494
  local TF_OUTPUT=""
522
495
 
523
- # Handle flags: -r/--rerun to auto-rerun, -s/--skip to skip
496
+ echo -n "Last command: "
497
+ echo -e "\\033[36m$last_cmd\\033[0m"
498
+
499
+ # Use -r/--rerun flag to re-run the command for output capture
524
500
  if [ "$rerun" = true ]; then
525
- echo -n "Last command: "
526
- echo -e "\\033[36m$last_cmd\\033[0m"
527
501
  echo -e "\\033[90mRe-running to capture output...\\033[0m"
528
502
  TF_OUTPUT=$($last_cmd 2>&1)
529
- elif [ "$skip" = true ]; then
530
- echo -n "Last command: "
531
- echo -e "\\033[36m$last_cmd\\033[0m"
532
- echo -e "\\033[90mSkipping re-execution (no output capture)\\033[0m"
533
- TF_OUTPUT="(output not captured - skipped via flag)"
534
503
  else
535
- # Interactive mode: ask for confirmation
536
- echo -n "Last command: "
537
- echo -e "\\033[36m$last_cmd\\033[0m"
538
- echo -n "Re-run to capture output? (y/n): "
539
- read -r confirm
540
-
541
- case "$confirm" in
542
- y|Y)
543
- TF_OUTPUT=$($last_cmd 2>&1)
544
- ;;
545
- n|N)
546
- TF_OUTPUT="(output not captured - user skipped re-execution)"
547
- ;;
548
- *)
549
- echo "Cancelled"
550
- return
551
- ;;
552
- esac
504
+ # Default: skip re-execution (no output capture)
505
+ TF_OUTPUT="(output not captured)"
553
506
  fi
554
507
 
555
508
  # Call tf-ai with captured command, output, and history
@@ -561,11 +514,9 @@ function fuck() {
561
514
  }
562
515
 
563
516
  # Usage:
564
- # fuck - Interactive mode (prompts for confirmation)
565
- # fuck -r - Auto re-run last command to capture output
566
- # fuck --rerun - Same as -r
567
- # fuck -s - Skip re-execution, analyze without output
568
- # fuck --skip - Same as -s
517
+ # fuck - Analyze last command (without re-running it)
518
+ # fuck -r - Re-run last command to capture output, then analyze
519
+ # fuck --rerun - Same as -r
569
520
  `.trim();
570
521
  }
571
522
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/ai/client.ts","../src/ai/prompts.ts","../src/ai/schema.ts","../src/shell/context.ts","../src/shell/powershell.ts","../src/shell/bash.ts","../src/shell/index.ts","../src/ui/output.ts","../src/ui/theme.ts","../src/ui/confirm.ts","../src/ui/animation.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfig, validateConfig, CONFIG_FILE } from \"./config.js\";\r\nimport { analyzeCommandStream } from \"./ai/index.js\";\r\nimport { getShell, detectEnvironment, type CommandContext } from \"./shell/index.js\";\r\nimport {\r\n printHeader,\r\n printSuggestion,\r\n printError,\r\n printWarning,\r\n printCommand,\r\n printVerbose,\r\n printStreamingExplanation,\r\n finalizeStreaming,\r\n confirmCommand,\r\n editCommand,\r\n runCommand,\r\n createHackingAnimation,\r\n} from \"./ui/index.js\";\r\nimport chalk from \"chalk\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"tf-ai\")\r\n .description(\"AI-powered terminal assistant that explains errors and suggests fixes\")\r\n .version(\"1.0.0\");\r\n\r\nprogram\r\n .option(\"-c, --command <command>\", \"The command that failed\")\r\n .option(\"-o, --output <output>\", \"The output/error from the failed command\")\r\n .option(\"-e, --explain\", \"Only explain the error, don't suggest commands\")\r\n .option(\"-m, --model <model>\", \"Model to use (e.g., claude-sonnet-4-20250514, gpt-4o)\")\r\n .option(\"-y, --yes\", \"Auto-run the suggested command without confirmation\")\r\n .option(\"-v, --verbose\", \"Show verbose output\")\r\n .option(\"--setup\", \"Show shell setup instructions\")\r\n .option(\"--history <json>\", \"JSON array of previous commands for context\")\r\n .action(async (options: {\r\n command?: string;\r\n output?: string;\r\n explain?: boolean;\r\n model?: string;\r\n yes?: boolean;\r\n verbose?: boolean;\r\n setup?: boolean;\r\n history?: string;\r\n }) => {\r\n // Show setup instructions\r\n if (options.setup) {\r\n console.log(getShell().getSetupInstructions());\r\n return;\r\n }\r\n\r\n // Load and validate config\r\n const configOverrides: Parameters<typeof loadConfig>[0] = {};\r\n if (options.model !== undefined) configOverrides.model = options.model;\r\n if (options.verbose !== undefined) configOverrides.verbose = options.verbose;\r\n if (options.yes !== undefined) configOverrides.confirmBeforeRun = !options.yes;\r\n \r\n const config = loadConfig(configOverrides);\r\n\r\n printVerbose(`Config: ${JSON.stringify(config, null, 2)}`, config.verbose);\r\n\r\n const validation = validateConfig(config);\r\n if (!validation.valid) {\r\n printError(validation.error!);\r\n console.log(`\\nCreate a config file at ${CONFIG_FILE} or set the appropriate environment variable.`);\r\n console.log('\\nExample config.json:');\r\n console.log(JSON.stringify({\r\n model: \"claude-sonnet-4-20250514\",\r\n apiKey: \"your-api-key-here\",\r\n }, null, 2));\r\n process.exit(1);\r\n }\r\n\r\n // Get command to analyze\r\n let command = options.command;\r\n let output = options.output ?? \"\";\r\n\r\n if (!command) {\r\n printVerbose(\"No command provided, checking shell history...\", config.verbose);\r\n const lastCmd = await getShell().getLastCommand();\r\n \r\n if (lastCmd) {\r\n command = lastCmd.command;\r\n output = lastCmd.output;\r\n printCommand(\"Last command\", command);\r\n } else {\r\n printWarning(\"No command provided and couldn't get last command from history.\");\r\n console.log(\"\\nUsage:\");\r\n console.log(\" tf-ai --command 'git pussh' --output 'error message'\");\r\n console.log(\"\\nOr set up the shell integration:\");\r\n console.log(\" tf-ai --setup\");\r\n process.exit(1);\r\n }\r\n } \r\n // Removed \"Analyzing: command\" print\r\n\r\n if (output && config.verbose) {\r\n console.log(\"\\nOutput:\");\r\n console.log(output);\r\n }\r\n\r\n console.log(); // Spacing\r\n const animation = createHackingAnimation(\"Analyzing what happened...\");\r\n animation.start();\r\n \r\n // Detect environment context\r\n const envContext = detectEnvironment();\r\n if (config.verbose) {\r\n printVerbose(`Environment: ${JSON.stringify(envContext, null, 2)}`, true);\r\n }\r\n \r\n // Parse command history if provided\r\n let history: CommandContext[] = [];\r\n if (options.history) {\r\n try {\r\n const parsed = JSON.parse(options.history);\r\n if (Array.isArray(parsed)) {\r\n history = parsed.map((item: { command: string; hasOutput?: boolean }) => ({\r\n command: item.command,\r\n output: \"\",\r\n hasOutput: item.hasOutput ?? false,\r\n }));\r\n printVerbose(`Command history: ${history.length} previous commands`, config.verbose);\r\n }\r\n } catch (e) {\r\n printVerbose(`Failed to parse history JSON: ${e}`, config.verbose);\r\n }\r\n }\r\n \r\n try {\r\n let isFirstChunk = true;\r\n const result = await analyzeCommandStream(command, output, config, envContext, {\r\n onExplanationUpdate: (text) => {\r\n if (isFirstChunk) {\r\n animation.stop();\r\n isFirstChunk = false;\r\n }\r\n // Stream using the polished UI function\r\n printStreamingExplanation(text);\r\n },\r\n }, history);\r\n \r\n if (isFirstChunk) {\r\n animation.stop();\r\n }\r\n \r\n finalizeStreaming();\r\n console.log();\r\n\r\n // Handle suggestion\r\n if (result.suggestion && !options.explain) {\r\n printSuggestion(result.suggestion);\r\n\r\n // Ask for confirmation\r\n if (config.confirmBeforeRun) {\r\n const confirmation = await confirmCommand(result.suggestion.command);\r\n\r\n if (confirmation.action === \"run\") {\r\n await runCommand(result.suggestion.command);\r\n } else if (confirmation.action === \"edit\") {\r\n const edited = await editCommand(result.suggestion.command);\r\n if (edited) {\r\n await runCommand(edited);\r\n }\r\n }\r\n } else {\r\n // Auto-run (-y flag)\r\n await runCommand(result.suggestion.command);\r\n }\r\n }\r\n } catch (error) {\r\n animation.stop(); // Stop animation on error\r\n finalizeStreaming(); // Reset streaming state\r\n \r\n const err = error as Error;\r\n printError(`Failed to analyze command: ${err.message}`);\r\n \r\n if (config.verbose && err.stack) {\r\n console.log(err.stack);\r\n }\r\n \r\n process.exit(1);\r\n }\r\n });\r\n\r\nprogram.parse();\r\n","import { existsSync, readFileSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { join } from \"node:path\";\r\n\r\nexport interface Config {\r\n /** Model identifier, e.g. \"anthropic/claude-sonnet-4-20250514\" or \"openai/gpt-4o\" */\r\n model: string;\r\n /** Provider to use: anthropic, openai, or google */\r\n provider: \"anthropic\" | \"openai\" | \"google\";\r\n /** API key for the selected provider */\r\n apiKey: string;\r\n /** Whether to ask for confirmation before running suggested commands */\r\n confirmBeforeRun: boolean;\r\n /** Show verbose/debug output */\r\n verbose: boolean;\r\n}\r\n\r\ninterface ConfigFile {\r\n model?: string;\r\n provider?: \"anthropic\" | \"openai\" | \"google\";\r\n apiKey?: string;\r\n confirmBeforeRun?: boolean;\r\n verbose?: boolean;\r\n}\r\n\r\nconst CONFIG_DIR = join(homedir(), \".tf-ai\");\r\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\r\n\r\nfunction loadConfigFile(): ConfigFile {\r\n if (!existsSync(CONFIG_FILE)) {\r\n return {};\r\n }\r\n \r\n try {\r\n const content = readFileSync(CONFIG_FILE, \"utf-8\");\r\n return JSON.parse(content) as ConfigFile;\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\nfunction detectProvider(model: string): \"anthropic\" | \"openai\" | \"google\" {\r\n if (model.startsWith(\"anthropic/\") || model.includes(\"claude\")) {\r\n return \"anthropic\";\r\n }\r\n if (model.startsWith(\"openai/\") || model.includes(\"gpt\")) {\r\n return \"openai\";\r\n }\r\n if (model.startsWith(\"google/\") || model.includes(\"gemini\")) {\r\n return \"google\";\r\n }\r\n // Default to anthropic\r\n return \"anthropic\";\r\n}\r\n\r\nfunction getApiKey(provider: \"anthropic\" | \"openai\" | \"google\", configKey?: string): string {\r\n // Config file key takes precedence\r\n if (configKey) {\r\n return configKey;\r\n }\r\n \r\n // Then check environment variables\r\n const envVars = {\r\n anthropic: \"ANTHROPIC_API_KEY\",\r\n openai: \"OPENAI_API_KEY\",\r\n google: \"GOOGLE_API_KEY\",\r\n } as const;\r\n \r\n const envVarName = envVars[provider];\r\n const envKey = process.env[envVarName];\r\n if (envKey) {\r\n return envKey;\r\n }\r\n \r\n // Also check generic key\r\n return process.env[\"TF_AI_API_KEY\"] ?? \"\";\r\n}\r\n\r\nexport function loadConfig(overrides?: Partial<Config>): Config {\r\n const file = loadConfigFile();\r\n \r\n // Determine model (priority: override > env > file > default)\r\n const model =\r\n overrides?.model ??\r\n process.env[\"TF_AI_MODEL\"] ??\r\n file.model ??\r\n \"claude-sonnet-4-5-20250929\";\r\n \r\n // Detect provider from model string\r\n const provider = \r\n overrides?.provider ?? \r\n file.provider ?? \r\n detectProvider(model);\r\n \r\n // Get API key\r\n const apiKey = getApiKey(provider, file.apiKey);\r\n \r\n return {\r\n model,\r\n provider,\r\n apiKey,\r\n confirmBeforeRun: overrides?.confirmBeforeRun ?? file.confirmBeforeRun ?? true,\r\n verbose: overrides?.verbose ?? file.verbose ?? false,\r\n };\r\n}\r\n\r\nexport function validateConfig(config: Config): { valid: boolean; error?: string } {\r\n if (!config.apiKey) {\r\n const envVar = {\r\n anthropic: \"ANTHROPIC_API_KEY\",\r\n openai: \"OPENAI_API_KEY\",\r\n google: \"GOOGLE_API_KEY\",\r\n }[config.provider];\r\n \r\n return {\r\n valid: false,\r\n error: `No API key found. Set ${envVar} environment variable or add \"apiKey\" to ${CONFIG_FILE}`,\r\n };\r\n }\r\n \r\n return { valid: true };\r\n}\r\n\r\nexport { CONFIG_DIR, CONFIG_FILE };\r\n","import { streamText, Output } from \"ai\";\r\nimport { anthropic } from \"@ai-sdk/anthropic\";\r\nimport { openai } from \"@ai-sdk/openai\";\r\nimport { google } from \"@ai-sdk/google\";\r\nimport type { Config } from \"../config.js\";\r\nimport { SYSTEM_PROMPT, formatUserMessage, formatUserMessageWithHistory } from \"./prompts.js\";\r\nimport { analysisResponseSchema, type AnalysisResponse } from \"./schema.js\";\r\nimport type { CommandContext } from \"../shell/types.js\";\r\n\r\nexport interface CommandSuggestion {\r\n command: string;\r\n explanation: string;\r\n}\r\n\r\nexport interface AnalysisResult {\r\n explanation: string;\r\n suggestion: CommandSuggestion | undefined;\r\n}\r\n\r\nfunction getModel(config: Config) {\r\n const modelName = config.model.includes(\"/\") \r\n ? config.model.split(\"/\")[1]! \r\n : config.model;\r\n \r\n switch (config.provider) {\r\n case \"anthropic\":\r\n return anthropic(modelName);\r\n case \"openai\":\r\n return openai(modelName);\r\n case \"google\":\r\n return google(modelName);\r\n default:\r\n return anthropic(modelName);\r\n }\r\n}\r\n\r\nimport type { EnvironmentContext } from \"../shell/context.js\";\r\n\r\nexport interface StreamCallbacks {\r\n onExplanationUpdate?: (text: string) => void;\r\n onComplete?: (result: AnalysisResult) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\n/**\r\n * Analyzes a command using streaming structured output.\r\n * Calls onExplanationUpdate as the explanation streams in.\r\n * @param history - Optional array of previous commands (without output) for context\r\n */\r\nexport async function analyzeCommandStream(\r\n command: string,\r\n output: string,\r\n config: Config,\r\n environment: EnvironmentContext,\r\n callbacks: StreamCallbacks = {},\r\n history: CommandContext[] = []\r\n): Promise<AnalysisResult> {\r\n const model = getModel(config);\r\n \r\n // Use history-aware formatter if history is provided\r\n const prompt = history.length > 0\r\n ? formatUserMessageWithHistory(command, output, history, environment)\r\n : formatUserMessage(command, output, environment);\r\n \r\n const { partialOutputStream } = streamText({\r\n model,\r\n system: SYSTEM_PROMPT,\r\n prompt,\r\n output: Output.object({ schema: analysisResponseSchema }),\r\n });\r\n\r\n let lastExplanation = \"\";\r\n let finalResponse: Partial<AnalysisResponse> = {};\r\n\r\n try {\r\n for await (const partial of partialOutputStream) {\r\n finalResponse = partial;\r\n \r\n // Stream explanation updates\r\n if (partial.explanation && partial.explanation !== lastExplanation) {\r\n lastExplanation = partial.explanation;\r\n callbacks.onExplanationUpdate?.(partial.explanation);\r\n }\r\n }\r\n } catch (error) {\r\n callbacks.onError?.(error as Error);\r\n throw error;\r\n }\r\n\r\n // Build final result\r\n const result: AnalysisResult = {\r\n explanation: finalResponse.explanation ?? \"\",\r\n suggestion: finalResponse.suggestedCommand \r\n ? {\r\n command: finalResponse.suggestedCommand,\r\n explanation: \"\", // Explanation is in the main field\r\n }\r\n : undefined,\r\n };\r\n\r\n callbacks.onComplete?.(result);\r\n return result;\r\n}\r\n\r\n/**\r\n * Non-streaming version for simpler use cases.\r\n */\r\nexport async function analyzeCommand(\r\n command: string,\r\n output: string,\r\n config: Config,\r\n environment: EnvironmentContext,\r\n history: CommandContext[] = []\r\n): Promise<AnalysisResult> {\r\n return analyzeCommandStream(command, output, config, environment, {}, history);\r\n}\r\n\r\n","import type { EnvironmentContext } from \"../shell/context.js\";\r\nimport type { CommandContext } from \"../shell/types.js\";\r\n\r\nexport const SYSTEM_PROMPT = `You are an expert terminal assistant that helps developers understand command output and troubleshoot issues.\r\n\r\nGiven a command and its output (and optionally recent command history for context), analyze the situation and provide helpful insight. The output could be:\r\n- An error message (syntax error, permission denied, command not found, etc.)\r\n- A warning or unexpected behavior\r\n- A build failure, test failure, or deployment issue\r\n- API/network errors\r\n- Confusing or unclear output that needs explanation\r\n- Or even successful output that the user wants to understand better\r\n\r\nYour response should:\r\n1. **Explain** what happened\r\n2. **Diagnose** the root cause if there's an issue, or at least speculate\r\n3. **Suggest a command** (optional) - only if there's a clear action the user can take\r\n\r\nGuidelines:\r\n- Be concise but thorough (if the problem is complex/deep, you have more leeway and the output can be longer!)\r\n- If you suggest a command, explain why / what it will do\r\n- If you're unsure or lacking enough context, simply say so\r\n- Apart from deliberate paragraphs, do not needlessly put newlines between sentences.\r\n- Tailor suggestions to the user's shell and OS\r\n- When command history is provided, use it to understand the user's workflow and intent`;\r\n\r\n\r\nexport function formatUserMessage(\r\n command: string, \r\n output: string,\r\n context: EnvironmentContext\r\n): string {\r\n const projectInfo = context.projectType ? `\\n- Project Type: ${context.projectType}` : '';\r\n \r\n return `Command: ${command}\r\n\r\nOutput:\r\n${output || \"(no output)\"}\r\n\r\nEnvironment:\r\n- Shell: ${context.shell}\r\n- OS: ${context.os}\r\n- Working Directory: ${context.cwd}${projectInfo}\r\n\r\nAnalyze this command and its output. Explain what happened and suggest a follow-up action if appropriate.`;\r\n}\r\n\r\n/**\r\n * Format user message with command history for better context.\r\n * The history array should be ordered oldest-first, with the most recent command last.\r\n */\r\nexport function formatUserMessageWithHistory(\r\n currentCommand: string,\r\n currentOutput: string,\r\n history: CommandContext[],\r\n context: EnvironmentContext\r\n): string {\r\n const projectInfo = context.projectType ? `\\n- Project Type: ${context.projectType}` : '';\r\n \r\n let historySection = \"\";\r\n if (history.length > 0) {\r\n historySection = \"Recent Command History (oldest first):\\n\";\r\n history.forEach((cmd, index) => {\r\n historySection += `${index + 1}. ${cmd.command}\\n`;\r\n });\r\n historySection += \"\\n\";\r\n }\r\n \r\n return `${historySection}Current Command: ${currentCommand}\r\n\r\nOutput:\r\n${currentOutput || \"(no output)\"}\r\n\r\nEnvironment:\r\n- Shell: ${context.shell}\r\n- OS: ${context.os}\r\n- Working Directory: ${context.cwd}${projectInfo}\r\n\r\nAnalyze this command and its output. Use the command history for context about what the user was trying to accomplish. Explain what happened and suggest a follow-up action if appropriate.`;\r\n}\r\n\r\n","import { z } from \"zod\";\r\n\r\n// Response schema for the AI analysis\r\nexport const analysisResponseSchema = z.object({\r\n explanation: z\r\n .string()\r\n .describe(\r\n \"Clear, helpful explanation of what happened. Interpret the output, diagnose issues, and provide context.\",\r\n ),\r\n suggestedCommand: z\r\n .string()\r\n .nullable()\r\n .describe(\r\n \"A follow-up command the user could run, if applicable. Could be a fix, a next step, or a diagnostic command. Pass null if no command is suggested\",\r\n ),\r\n});\r\n\r\nexport type AnalysisResponse = z.infer<typeof analysisResponseSchema>;\r\n","import { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\nimport { platform } from \"node:os\";\r\n\r\nexport interface EnvironmentContext {\r\n shell: \"powershell\" | \"bash\" | \"zsh\" | \"cmd\" | \"fish\" | \"unknown\";\r\n os: \"windows\" | \"macos\" | \"linux\";\r\n cwd: string;\r\n projectType?: \"git\" | \"nodejs\" | \"python\" | \"docker\";\r\n}\r\n\r\nfunction detectShell(): EnvironmentContext[\"shell\"] {\r\n // Check SHELL env var (Unix-like systems) - Check this FIRST to avoid false positives on Linux\r\n const shell = process.env[\"SHELL\"];\r\n if (shell) {\r\n if (shell.includes(\"bash\")) return \"bash\";\r\n if (shell.includes(\"zsh\")) return \"zsh\";\r\n if (shell.includes(\"fish\")) return \"fish\";\r\n }\r\n\r\n // Check for PowerShell-specific env var\r\n if (process.env[\"PSModulePath\"]) {\r\n return \"powershell\";\r\n }\r\n \r\n // Check for CMD on Windows\r\n if (process.env[\"ComSpec\"]?.includes(\"cmd.exe\")) {\r\n return \"cmd\";\r\n }\r\n \r\n return \"unknown\";\r\n}\r\n\r\nfunction detectOS(): EnvironmentContext[\"os\"] {\r\n const os = platform();\r\n \r\n switch (os) {\r\n case \"win32\":\r\n return \"windows\";\r\n case \"darwin\":\r\n return \"macos\";\r\n case \"linux\":\r\n return \"linux\";\r\n default:\r\n // Fallback to linux for other Unix-like systems\r\n return \"linux\";\r\n }\r\n}\r\n\r\nfunction detectProjectType(cwd: string): EnvironmentContext[\"projectType\"] {\r\n // Check in order of specificity\r\n if (existsSync(join(cwd, \".git\"))) {\r\n return \"git\";\r\n }\r\n \r\n if (existsSync(join(cwd, \"package.json\"))) {\r\n return \"nodejs\";\r\n }\r\n \r\n if (existsSync(join(cwd, \"requirements.txt\")) || existsSync(join(cwd, \"pyproject.toml\"))) {\r\n return \"python\";\r\n }\r\n \r\n if (existsSync(join(cwd, \"Dockerfile\"))) {\r\n return \"docker\";\r\n }\r\n \r\n return undefined;\r\n}\r\n\r\nexport function detectEnvironment(): EnvironmentContext {\r\n const cwd = process.cwd();\r\n const projectType = detectProjectType(cwd);\r\n \r\n return {\r\n shell: detectShell(),\r\n os: detectOS(),\r\n cwd,\r\n ...(projectType ? { projectType } : {}),\r\n };\r\n}\r\n","import { exec } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { ShellAdapter, CommandContext } from \"./types.js\";\r\n\r\nconst execAsync = promisify(exec);\r\n\r\nexport class PowerShellAdapter implements ShellAdapter {\r\n async getLastCommand(): Promise<CommandContext | null> {\r\n // This is called when the user runs tf-ai without explicit --command\r\n // We try to get the last command from PowerShell history\r\n try {\r\n const { stdout } = await execAsync(\r\n 'powershell -Command \"(Get-History -Count 1).CommandLine\"',\r\n { encoding: \"utf-8\" }\r\n );\r\n \r\n const command = stdout.trim();\r\n if (!command) {\r\n return null;\r\n }\r\n \r\n // We can't reliably get the output of the previous command after the fact\r\n // The user should use the shell function which captures output in real-time\r\n return {\r\n command,\r\n output: \"(Output not captured - use the 'fuck' shell function for full functionality)\",\r\n hasOutput: false,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n async getCommandHistory(count: number): Promise<string[]> {\r\n try {\r\n const { stdout } = await execAsync(\r\n `powershell -Command \"(Get-History -Count ${count}).CommandLine\"`,\r\n { encoding: \"utf-8\" }\r\n );\r\n \r\n const commands = stdout\r\n .split(/\\r?\\n/)\r\n .map(cmd => cmd.trim())\r\n .filter(cmd => cmd.length > 0);\r\n \r\n return commands;\r\n } catch {\r\n return [];\r\n }\r\n }\r\n\r\n async executeCommand(command: string): Promise<{ output: string; exitCode: number }> {\r\n try {\r\n const { stdout, stderr } = await execAsync(\r\n `powershell -Command \"${command.replace(/\"/g, '\\\\\"')}\"`,\r\n { encoding: \"utf-8\" }\r\n );\r\n return {\r\n output: stdout + stderr,\r\n exitCode: 0,\r\n };\r\n } catch (error) {\r\n const execError = error as { stdout?: string; stderr?: string; code?: number };\r\n return {\r\n output: (execError.stdout ?? \"\") + (execError.stderr ?? \"\"),\r\n exitCode: execError.code ?? 1,\r\n };\r\n }\r\n }\r\n\r\n getSetupInstructions(): string {\r\n return `\r\nAdd this to your PowerShell profile ($PROFILE):\r\n\r\nfunction fuck {\r\n param(\r\n [Alias(\"r\")][switch]$Rerun,\r\n [Alias(\"s\")][switch]$Skip\r\n )\r\n \r\n # Get last 3 commands from history (for context)\r\n $historyItems = Get-History -Count 3\r\n $historyCommands = @()\r\n foreach ($item in $historyItems) {\r\n $historyCommands += $item.CommandLine\r\n }\r\n \r\n if ($historyCommands.Count -eq 0) {\r\n Write-Host \"No command in history\" -ForegroundColor Red\r\n return\r\n }\r\n \r\n # The last command is the one we'll capture output for\r\n $lastCmd = $historyCommands[-1]\r\n \r\n # Build history JSON (all commands except the last, which will have output)\r\n $historyJson = @()\r\n for ($i = 0; $i -lt $historyCommands.Count - 1; $i++) {\r\n $historyJson += @{\r\n command = $historyCommands[$i]\r\n hasOutput = $false\r\n }\r\n }\r\n $historyArg = ($historyJson | ConvertTo-Json -Compress) -replace '\"', '\\\\\\\\\"'\r\n \r\n $output = \"\"\r\n \r\n # Handle flags: -r/--Rerun to auto-rerun, -s/--Skip to skip\r\n if ($Rerun) {\r\n Write-Host \"Last command: \" -NoNewline\r\n Write-Host $lastCmd -ForegroundColor Cyan\r\n Write-Host \"Re-running to capture output...\" -ForegroundColor Gray\r\n $output = try { \r\n Invoke-Expression $lastCmd 2>&1 | Out-String \r\n } catch { \r\n $_.Exception.Message \r\n }\r\n } elseif ($Skip) {\r\n Write-Host \"Last command: \" -NoNewline\r\n Write-Host $lastCmd -ForegroundColor Cyan\r\n Write-Host \"Skipping re-execution (no output capture)\" -ForegroundColor Gray\r\n $output = \"(output not captured - skipped via flag)\"\r\n } else {\r\n # Interactive mode: ask for confirmation\r\n Write-Host \"Last command: \" -NoNewline\r\n Write-Host $lastCmd -ForegroundColor Cyan\r\n $confirm = Read-Host \"Re-run to capture output? (y/n)\"\r\n \r\n if ($confirm -eq \"y\" -or $confirm -eq \"Y\") {\r\n $output = try { \r\n Invoke-Expression $lastCmd 2>&1 | Out-String \r\n } catch { \r\n $_.Exception.Message \r\n }\r\n } elseif ($confirm -eq \"n\" -or $confirm -eq \"N\") {\r\n $output = \"(output not captured - user skipped re-execution)\"\r\n } else {\r\n Write-Host \"Cancelled\" -ForegroundColor Yellow\r\n return\r\n }\r\n }\r\n \r\n # Call tf-ai with captured command, output, and history\r\n if ($historyJson.Count -gt 0) {\r\n tf-ai --command $lastCmd --output $output --history \"$historyArg\"\r\n } else {\r\n tf-ai --command $lastCmd --output $output\r\n }\r\n}\r\n\r\n# Usage:\r\n# fuck - Interactive mode (prompts for confirmation)\r\n# fuck -r - Auto re-run last command to capture output\r\n# fuck -Rerun - Same as -r\r\n# fuck -s - Skip re-execution, analyze without output\r\n# fuck -Skip - Same as -s\r\n\r\nThen reload your profile:\r\n. $PROFILE\r\n`.trim();\r\n }\r\n}\r\n\r\nexport const powershell = new PowerShellAdapter();\r\n\r\n","import { exec } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { ShellAdapter, CommandContext } from \"./types.js\";\r\n\r\nconst execAsync = promisify(exec);\r\n\r\nexport class BashAdapter implements ShellAdapter {\r\n async getLastCommand(): Promise<CommandContext | null> {\r\n // fast, non-interactive history lookup not easily possible in pure node without help from shell\r\n // The user really should use the shell alias/function for full functionality.\r\n // However, we can try to read ~/.bash_history if forced, but that's flaky (timestamps etc).\r\n \r\n // Instead, we rely on the shell wrapper passing the command.\r\n // If called directly without wrapper:\r\n return null; \r\n }\r\n\r\n async getCommandHistory(count: number): Promise<string[]> {\r\n try {\r\n // Use fc to get last N commands (works in bash and zsh)\r\n const { stdout } = await execAsync(\r\n `bash -c 'fc -ln -${count} | sed \"s/^[[:space:]]*//\"'`,\r\n { encoding: \"utf-8\" }\r\n );\r\n \r\n const commands = stdout\r\n .split(/\\r?\\n/)\r\n .map(cmd => cmd.trim())\r\n .filter(cmd => cmd.length > 0);\r\n \r\n return commands;\r\n } catch {\r\n return [];\r\n }\r\n }\r\n\r\n async executeCommand(command: string): Promise<{ output: string; exitCode: number }> {\r\n try {\r\n // Use bash -c to execute. \r\n // We escape single quotes in the command by replacing ' with '\\''\r\n const escapedCommand = command.replace(/'/g, \"'\\\\''\");\r\n const { stdout, stderr } = await execAsync(\r\n `bash -c '${escapedCommand}'`,\r\n { encoding: \"utf-8\" }\r\n );\r\n return {\r\n output: stdout + stderr,\r\n exitCode: 0,\r\n };\r\n } catch (error) {\r\n const execError = error as { stdout?: string; stderr?: string; code?: number };\r\n return {\r\n output: (execError.stdout ?? \"\") + (execError.stderr ?? \"\"),\r\n exitCode: execError.code ?? 1,\r\n };\r\n }\r\n }\r\n\r\n getSetupInstructions(): string {\r\n return `\r\n# Add this to your .bashrc or .zshrc:\r\n\r\nfunction fuck() {\r\n local rerun=false\r\n local skip=false\r\n \r\n # Parse arguments\r\n while [[ $# -gt 0 ]]; do\r\n case \"$1\" in\r\n -r|--rerun)\r\n rerun=true\r\n shift\r\n ;;\r\n -s|--skip)\r\n skip=true\r\n shift\r\n ;;\r\n *)\r\n echo \"Unknown option: $1\"\r\n echo \"Usage: fuck [-r|--rerun] [-s|--skip]\"\r\n return 1\r\n ;;\r\n esac\r\n done\r\n \r\n # Get last 3 commands from history (for context)\r\n # fc -ln -3 gets the last 3 commands, sed removes leading whitespace\r\n local history_cmds\r\n history_cmds=$(fc -ln -3 2>/dev/null | sed 's/^[[:space:]]*//')\r\n \r\n if [ -z \"$history_cmds\" ]; then\r\n echo \"No command in history\"\r\n return\r\n fi\r\n \r\n # Convert to array (one command per line)\r\n local -a cmds\r\n while IFS= read -r line; do\r\n [ -n \"$line\" ] && cmds+=(\"$line\")\r\n done <<< \"$history_cmds\"\r\n \r\n local cmd_count=\\${#cmds[@]}\r\n if [ \"$cmd_count\" -eq 0 ]; then\r\n echo \"No command in history\"\r\n return\r\n fi\r\n \r\n # The last command is the one we'll capture output for\r\n local last_cmd=\"\\${cmds[$((cmd_count-1))]}\"\r\n \r\n # Build history JSON (all commands except the last)\r\n local history_json=\"[\"\r\n local first=true\r\n for ((i=0; i<cmd_count-1; i++)); do\r\n local cmd=\"\\${cmds[$i]}\"\r\n # Escape quotes and backslashes for JSON\r\n cmd=\\$(printf '%s' \"$cmd\" | sed 's/\\\\\\\\/\\\\\\\\\\\\\\\\/g; s/\"/\\\\\\\\\"/g')\r\n if [ \"$first\" = true ]; then\r\n first=false\r\n else\r\n history_json+=\",\"\r\n fi\r\n history_json+=\"{\\\\\\\"command\\\\\\\":\\\\\\\"$cmd\\\\\\\",\\\\\\\"hasOutput\\\\\\\":false}\"\r\n done\r\n history_json+=\"]\"\r\n \r\n local TF_OUTPUT=\"\"\r\n \r\n # Handle flags: -r/--rerun to auto-rerun, -s/--skip to skip\r\n if [ \"$rerun\" = true ]; then\r\n echo -n \"Last command: \"\r\n echo -e \"\\\\033[36m$last_cmd\\\\033[0m\"\r\n echo -e \"\\\\033[90mRe-running to capture output...\\\\033[0m\"\r\n TF_OUTPUT=$($last_cmd 2>&1)\r\n elif [ \"$skip\" = true ]; then\r\n echo -n \"Last command: \"\r\n echo -e \"\\\\033[36m$last_cmd\\\\033[0m\"\r\n echo -e \"\\\\033[90mSkipping re-execution (no output capture)\\\\033[0m\"\r\n TF_OUTPUT=\"(output not captured - skipped via flag)\"\r\n else\r\n # Interactive mode: ask for confirmation\r\n echo -n \"Last command: \"\r\n echo -e \"\\\\033[36m$last_cmd\\\\033[0m\"\r\n echo -n \"Re-run to capture output? (y/n): \"\r\n read -r confirm\r\n \r\n case \"$confirm\" in\r\n y|Y)\r\n TF_OUTPUT=$($last_cmd 2>&1)\r\n ;;\r\n n|N)\r\n TF_OUTPUT=\"(output not captured - user skipped re-execution)\"\r\n ;;\r\n *)\r\n echo \"Cancelled\"\r\n return\r\n ;;\r\n esac\r\n fi\r\n \r\n # Call tf-ai with captured command, output, and history\r\n if [ \"$cmd_count\" -gt 1 ]; then\r\n tf-ai --command \"$last_cmd\" --output \"$TF_OUTPUT\" --history \"$history_json\"\r\n else\r\n tf-ai --command \"$last_cmd\" --output \"$TF_OUTPUT\"\r\n fi\r\n}\r\n\r\n# Usage:\r\n# fuck - Interactive mode (prompts for confirmation)\r\n# fuck -r - Auto re-run last command to capture output\r\n# fuck --rerun - Same as -r\r\n# fuck -s - Skip re-execution, analyze without output\r\n# fuck --skip - Same as -s\r\n`.trim();\r\n }\r\n}\r\n\r\nexport const bash = new BashAdapter();\r\n","import { detectEnvironment } from \"./context.js\";\r\nimport { PowerShellAdapter, powershell } from \"./powershell.js\";\r\nimport { BashAdapter, bash } from \"./bash.js\";\r\nimport type { ShellAdapter } from \"./types.js\";\r\n\r\nexport { PowerShellAdapter, powershell } from \"./powershell.js\";\r\nexport { BashAdapter, bash } from \"./bash.js\";\r\nexport { detectEnvironment, type EnvironmentContext } from \"./context.js\";\r\nexport type { ShellAdapter, CommandContext } from \"./types.js\";\r\n\r\nlet activeShell: ShellAdapter | null = null;\r\n\r\nexport function getShell(): ShellAdapter {\r\n if (activeShell) {\r\n return activeShell;\r\n }\r\n \r\n const env = detectEnvironment();\r\n \r\n switch (env.shell) {\r\n case \"powershell\":\r\n activeShell = powershell;\r\n break;\r\n case \"bash\":\r\n case \"zsh\":\r\n // Bash adapter works for zsh too for now as they both share 'fc'\r\n activeShell = bash;\r\n break;\r\n case \"cmd\":\r\n // Fallback to powershell for now on Windows if CMD is detected but we want robust features\r\n // Or we could implement CmdAdapter later. For now, defaulting to powershell is safer \r\n // as our powershell adapter uses 'powershell -Command' which works from CMD too.\r\n activeShell = powershell;\r\n break;\r\n default:\r\n // Default fallback based on OS\r\n if (env.os === \"windows\") {\r\n activeShell = powershell;\r\n } else {\r\n activeShell = bash;\r\n }\r\n }\r\n \r\n return activeShell!;\r\n}\r\n","import chalk from \"chalk\";\r\nimport type { CommandSuggestion, AnalysisResult } from \"../ai/index.js\";\r\nimport { theme } from \"./theme.js\";\r\n\r\nconst ICONS = {\r\n robot: \"🤖\",\r\n lightbulb: \"\", // note: remember to add space after it if adding icon\r\n command: \"➜\",\r\n warning: \"⚠️\",\r\n error: \"❌\",\r\n success: \"✓\",\r\n thinking: \"🧠\",\r\n};\r\n\r\nexport function printHeader(): void {\r\n // Clean header using theme\r\n console.log(theme.header(`\\n${ICONS.robot} tf-ai\\n`));\r\n}\r\n\r\nexport function printExplanation(result: AnalysisResult): void {\r\n if (result.explanation) {\r\n console.log(theme.explanation(result.explanation));\r\n console.log();\r\n }\r\n}\r\n\r\nlet lastPrintedLength = 0;\r\n\r\n/**\r\n * For streaming: prints only the new part of the explanation\r\n */\r\nexport function printStreamingExplanation(fullText: string): void {\r\n if (!fullText) return;\r\n\r\n // Calculate the new chunk to print\r\n const newText = fullText.slice(lastPrintedLength);\r\n\r\n if (newText) {\r\n // Use theme explanation color\r\n process.stdout.write(theme.explanation(newText));\r\n lastPrintedLength = fullText.length;\r\n }\r\n}\r\n\r\n/**\r\n * Finalize streaming output with newline and reset state\r\n */\r\nexport function finalizeStreaming(): void {\r\n console.log(); // New line after streaming completes\r\n lastPrintedLength = 0; // Reset for next run\r\n}\r\n\r\nexport function printSuggestion(suggestion: CommandSuggestion): void {\r\n console.log(theme.suggestionLabel(`${ICONS.lightbulb}Suggested command:`));\r\n console.log();\r\n // Using theme.command color\r\n console.log(theme.command(` ${ICONS.command} ${suggestion.command} `));\r\n console.log();\r\n}\r\n\r\nexport function printError(message: string): void {\r\n console.error(theme.error(`${ICONS.error} ${message}`));\r\n}\r\n\r\nexport function printWarning(message: string): void {\r\n console.log(theme.warning(`${ICONS.warning} ${message}`));\r\n}\r\n\r\nexport function printSuccess(message: string): void {\r\n console.log(theme.success(`${ICONS.success} ${message}`));\r\n}\r\n\r\nexport function printCommand(label: string, command: string): void {\r\n console.log(theme.muted(`${label}: `) + theme.text(command));\r\n}\r\n\r\nexport function printVerbose(message: string, verbose: boolean): void {\r\n if (verbose) {\r\n console.log(theme.muted(`[debug] ${message}`));\r\n }\r\n}\r\n","import chalk from \"chalk\";\r\n\r\n/**\r\n * Anthropic-Inspired Palette\r\n * Sophisticated, warm, and professional.\r\n */\r\nexport const COLORS = {\r\n stone: \"#F5F2E8\", // Warmest White / Base\r\n warmGray: \"#9E9A95\", // Muted text / Labels\r\n text: \"#E3DCD2\", // Main reading text (Warm Off-White)\r\n coral: \"#D97757\", // Primary Accent (Anthropic-like orange/coral) - Highlights\r\n maple: \"#C8A682\", // Secondary Accent - Interactions\r\n olive: \"#8E9C6D\", // Success\r\n sienna: \"#C1554D\", // Error\r\n charcoal: \"#2D2B29\", // Dark background text if needed\r\n};\r\n\r\n/**\r\n * Semantic Theme Definitions\r\n */\r\nexport const theme = {\r\n // --- Global Roles ---\r\n primary: chalk.hex(COLORS.coral),\r\n secondary: chalk.hex(COLORS.maple),\r\n success: chalk.hex(COLORS.olive),\r\n error: chalk.hex(COLORS.sienna),\r\n warning: chalk.hex(COLORS.maple), // Maple works well for warning too\r\n muted: chalk.hex(COLORS.warmGray),\r\n text: chalk.hex(COLORS.text),\r\n \r\n // --- Component Specifics ---\r\n \r\n // Header / Branding\r\n header: chalk.hex(COLORS.coral).bold,\r\n \r\n // The suggested command to run\r\n command: chalk.hex(COLORS.stone).bold, // Bright, distinct\r\n \r\n // The AI's explanation text\r\n explanation: chalk.hex(COLORS.text),\r\n \r\n // \"Suggested fix:\" label - subtle yet clear\r\n suggestionLabel: chalk.hex(COLORS.maple).bold,\r\n \r\n // Confidence levels\r\n confidenceHigh: chalk.hex(COLORS.olive),\r\n confidenceMedium: chalk.hex(COLORS.maple),\r\n confidenceLow: chalk.hex(COLORS.sienna),\r\n \r\n // --- Animation Colors ---\r\n // Glitch characters\r\n glitch1: chalk.hex(COLORS.coral),\r\n glitch2: chalk.hex(COLORS.maple),\r\n // Base text for \"Analyzing...\"\r\n glitchBase: chalk.hex(COLORS.warmGray),\r\n};\r\n","import * as readline from \"node:readline\";\r\nimport chalk from \"chalk\";\r\nimport { exec } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport { theme } from \"./theme.js\";\r\nimport { getShell } from \"../shell/index.js\";\r\n\r\nconst execAsync = promisify(exec);\r\n\r\nexport interface ConfirmResult {\r\n action: \"run\" | \"cancel\" | \"edit\";\r\n editedCommand?: string;\r\n}\r\n\r\nexport async function confirmCommand(command: string): Promise<ConfirmResult> {\r\n return new Promise((resolve) => {\r\n // Compact menu on one line\r\n process.stdout.write(\r\n theme.secondary(\"[Enter]\") + theme.muted(\" Run \") +\r\n theme.secondary(\"[e]\") + theme.muted(\" Edit \") +\r\n theme.secondary(\"[Esc/q]\") + theme.muted(\" Cancel\")\r\n );\r\n\r\n // Set up raw mode to capture single keypresses\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(true);\r\n }\r\n process.stdin.resume();\r\n process.stdin.setEncoding(\"utf8\");\r\n\r\n const cleanup = () => {\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(false);\r\n }\r\n process.stdin.pause();\r\n process.stdin.removeAllListeners(\"data\");\r\n };\r\n\r\n process.stdin.once(\"data\", (key: string) => {\r\n cleanup();\r\n\r\n // Enter key\r\n if (key === \"\\r\" || key === \"\\n\") {\r\n resolve({ action: \"run\" });\r\n return;\r\n }\r\n\r\n // Escape or q\r\n if (key === \"\\u001b\" || key === \"q\" || key === \"\\u0003\") {\r\n console.log(\"\\n\" + theme.warning(\"Cancelled\"));\r\n resolve({ action: \"cancel\" });\r\n return;\r\n }\r\n\r\n // Edit\r\n if (key === \"e\" || key === \"E\") {\r\n resolve({ action: \"edit\" });\r\n return;\r\n }\r\n\r\n // Unknown key - treat as cancel\r\n console.log(\"\\n\" + theme.warning(\"Cancelled\"));\r\n resolve({ action: \"cancel\" });\r\n });\r\n });\r\n}\r\n\r\nexport async function editCommand(originalCommand: string): Promise<string | null> {\r\n return new Promise((resolve) => {\r\n const rl = readline.createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n console.log(theme.secondary(\"\\nEdit command (press Enter to confirm, Ctrl+C to cancel):\"));\r\n \r\n rl.question(theme.muted(\"> \"), (answer) => {\r\n rl.close();\r\n const command = answer.trim();\r\n resolve(command || originalCommand);\r\n });\r\n\r\n rl.on(\"close\", () => {\r\n if (!rl.terminal) {\r\n resolve(null);\r\n }\r\n });\r\n });\r\n}\r\n\r\nexport async function runCommand(command: string): Promise<void> {\r\n console.log(theme.secondary(`\\nRunning: ${command}\\n`));\r\n console.log(theme.muted(\"─\".repeat(40)));\r\n \r\n try {\r\n // Run the command and stream output\r\n const { output, exitCode } = await getShell().executeCommand(command);\r\n \r\n // Parse stdout/stderr roughly from the combined output if possible, \r\n // but our adapters return combined output mostly.\r\n // For now we just print the output.\r\n if (output) console.log(output);\r\n \r\n console.log(theme.muted(\"─\".repeat(40)));\r\n if (exitCode === 0) {\r\n console.log(theme.success(\"✓ Command completed\"));\r\n } else {\r\n console.log(theme.error(`✗ Command failed with exit code ${exitCode}`));\r\n }\r\n } catch (error) {\r\n console.log(theme.muted(\"─\".repeat(40)));\r\n console.log(theme.error(`✗ Command failed: ${error}`));\r\n }\r\n}\r\n","import { theme } from \"./theme.js\";\n\n/**\n * Creates a \"hacking\" style animated text where random characters glitch\n * Refined for subtlety and elegance\n */\nexport function createHackingAnimation(text: string): {\n start: () => void;\n stop: () => void;\n} {\n const glitchChars = ['#', '$', '%', '*', '?', '~', '^', '+', '=', ':'];\n let interval: NodeJS.Timeout | null = null;\n let isRunning = false;\n \n const originalText = text;\n const textArray = text.split('');\n \n function glitchText(): string {\n return textArray.map((char, i) => {\n // Skip spaces\n if (char === ' ') return ' ';\n \n // Random chance to glitch (much lower probability for subtlety: 5%)\n if (Math.random() < 0.05) {\n // Use theme colors for glitches\n return Math.random() > 0.5 \n ? theme.glitch1(glitchChars[Math.floor(Math.random() * glitchChars.length)]!)\n : theme.glitch2(glitchChars[Math.floor(Math.random() * glitchChars.length)]!);\n }\n \n // Base text color\n return theme.glitchBase(char);\n }).join('');\n }\n \n function render() {\n if (!isRunning) return;\n \n // Clear line and print glitched text\n process.stdout.write('\\r' + glitchText());\n }\n \n return {\n start() {\n if (isRunning) return;\n isRunning = true;\n \n // Print initial state\n process.stdout.write(theme.glitchBase(originalText));\n \n // Start glitching at slower intervals (120ms) for elegance\n interval = setInterval(render, 120);\n },\n \n stop() {\n if (!isRunning) return;\n isRunning = false;\n \n if (interval) {\n clearInterval(interval);\n interval = null;\n }\n \n // Clear the line\n process.stdout.write('\\r' + ' '.repeat(originalText.length) + '\\r');\n },\n };\n}\n"],"mappings":"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","names":["existsSync","join","exec","promisify","execAsync","exec","promisify","execAsync","promisify","exec"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/ai/client.ts","../src/ai/prompts.ts","../src/ai/schema.ts","../src/shell/context.ts","../src/shell/powershell.ts","../src/shell/bash.ts","../src/shell/index.ts","../src/ui/output.ts","../src/ui/theme.ts","../src/ui/confirm.ts","../src/ui/animation.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfig, validateConfig, CONFIG_FILE } from \"./config.js\";\r\nimport { analyzeCommandStream } from \"./ai/index.js\";\r\nimport { getShell, detectEnvironment, type CommandContext } from \"./shell/index.js\";\r\nimport {\r\n printHeader,\r\n printSuggestion,\r\n printError,\r\n printWarning,\r\n printCommand,\r\n printVerbose,\r\n printStreamingExplanation,\r\n finalizeStreaming,\r\n confirmCommand,\r\n editCommand,\r\n runCommand,\r\n createHackingAnimation,\r\n} from \"./ui/index.js\";\r\nimport chalk from \"chalk\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"tf-ai\")\r\n .description(\"AI-powered terminal assistant that explains errors and suggests fixes\")\r\n .version(\"1.0.0\");\r\n\r\nprogram\r\n .option(\"-c, --command <command>\", \"The command that failed\")\r\n .option(\"-o, --output <output>\", \"The output/error from the failed command\")\r\n .option(\"-e, --explain\", \"Only explain the error, don't suggest commands\")\r\n .option(\"-m, --model <model>\", \"Model to use (e.g., claude-sonnet-4-20250514, gpt-4o)\")\r\n .option(\"-y, --yes\", \"Auto-run the suggested command without confirmation\")\r\n .option(\"-v, --verbose\", \"Show verbose output\")\r\n .option(\"--setup\", \"Show shell setup instructions\")\r\n .option(\"--history <json>\", \"JSON array of previous commands for context\")\r\n .action(async (options: {\r\n command?: string;\r\n output?: string;\r\n explain?: boolean;\r\n model?: string;\r\n yes?: boolean;\r\n verbose?: boolean;\r\n setup?: boolean;\r\n history?: string;\r\n }) => {\r\n // Show setup instructions\r\n if (options.setup) {\r\n console.log(getShell().getSetupInstructions());\r\n return;\r\n }\r\n\r\n // Load and validate config\r\n const configOverrides: Parameters<typeof loadConfig>[0] = {};\r\n if (options.model !== undefined) configOverrides.model = options.model;\r\n if (options.verbose !== undefined) configOverrides.verbose = options.verbose;\r\n if (options.yes !== undefined) configOverrides.confirmBeforeRun = !options.yes;\r\n \r\n const config = loadConfig(configOverrides);\r\n\r\n printVerbose(`Config: ${JSON.stringify(config, null, 2)}`, config.verbose);\r\n\r\n const validation = validateConfig(config);\r\n if (!validation.valid) {\r\n printError(validation.error!);\r\n console.log(`\\nCreate a config file at ${CONFIG_FILE} or set the appropriate environment variable.`);\r\n console.log('\\nExample config.json:');\r\n console.log(JSON.stringify({\r\n model: \"claude-sonnet-4-20250514\",\r\n apiKey: \"your-api-key-here\",\r\n }, null, 2));\r\n process.exit(1);\r\n }\r\n\r\n // Get command to analyze\r\n let command = options.command;\r\n let output = options.output ?? \"\";\r\n\r\n if (!command) {\r\n printVerbose(\"No command provided, checking shell history...\", config.verbose);\r\n const lastCmd = await getShell().getLastCommand();\r\n \r\n if (lastCmd) {\r\n command = lastCmd.command;\r\n output = lastCmd.output;\r\n printCommand(\"Last command\", command);\r\n } else {\r\n printWarning(\"No command provided and couldn't get last command from history.\");\r\n console.log(\"\\nUsage:\");\r\n console.log(\" tf-ai --command 'git pussh' --output 'error message'\");\r\n console.log(\"\\nOr set up the shell integration:\");\r\n console.log(\" tf-ai --setup\");\r\n process.exit(1);\r\n }\r\n } \r\n // Removed \"Analyzing: command\" print\r\n\r\n if (output && config.verbose) {\r\n console.log(\"\\nOutput:\");\r\n console.log(output);\r\n }\r\n\r\n console.log(); // Spacing\r\n const animation = createHackingAnimation(\"Analyzing what happened...\");\r\n animation.start();\r\n \r\n // Detect environment context\r\n const envContext = detectEnvironment();\r\n if (config.verbose) {\r\n printVerbose(`Environment: ${JSON.stringify(envContext, null, 2)}`, true);\r\n }\r\n \r\n // Parse command history if provided\r\n let history: CommandContext[] = [];\r\n if (options.history) {\r\n try {\r\n const parsed = JSON.parse(options.history);\r\n if (Array.isArray(parsed)) {\r\n history = parsed.map((item: { command: string; hasOutput?: boolean }) => ({\r\n command: item.command,\r\n output: \"\",\r\n hasOutput: item.hasOutput ?? false,\r\n }));\r\n printVerbose(`Command history: ${history.length} previous commands`, config.verbose);\r\n }\r\n } catch (e) {\r\n printVerbose(`Failed to parse history JSON: ${e}`, config.verbose);\r\n }\r\n }\r\n \r\n try {\r\n let isFirstChunk = true;\r\n const result = await analyzeCommandStream(command, output, config, envContext, {\r\n onExplanationUpdate: (text) => {\r\n if (isFirstChunk) {\r\n animation.stop();\r\n isFirstChunk = false;\r\n }\r\n // Stream using the polished UI function\r\n printStreamingExplanation(text);\r\n },\r\n }, history);\r\n \r\n if (isFirstChunk) {\r\n animation.stop();\r\n }\r\n \r\n finalizeStreaming();\r\n console.log();\r\n\r\n // Handle suggestion\r\n if (result.suggestion && !options.explain) {\r\n printSuggestion(result.suggestion);\r\n\r\n // Ask for confirmation\r\n if (config.confirmBeforeRun) {\r\n const confirmation = await confirmCommand(result.suggestion.command);\r\n\r\n if (confirmation.action === \"run\") {\r\n await runCommand(result.suggestion.command);\r\n } else if (confirmation.action === \"edit\") {\r\n const edited = await editCommand(result.suggestion.command);\r\n if (edited) {\r\n await runCommand(edited);\r\n }\r\n }\r\n } else {\r\n // Auto-run (-y flag)\r\n await runCommand(result.suggestion.command);\r\n }\r\n }\r\n } catch (error) {\r\n animation.stop(); // Stop animation on error\r\n finalizeStreaming(); // Reset streaming state\r\n \r\n const err = error as Error;\r\n printError(`Failed to analyze command: ${err.message}`);\r\n \r\n if (config.verbose && err.stack) {\r\n console.log(err.stack);\r\n }\r\n \r\n process.exit(1);\r\n }\r\n });\r\n\r\nprogram.parse();\r\n","import { existsSync, readFileSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { join } from \"node:path\";\r\n\r\nexport interface Config {\r\n /** Model identifier, e.g. \"anthropic/claude-sonnet-4-20250514\" or \"openai/gpt-4o\" */\r\n model: string;\r\n /** Provider to use: anthropic, openai, or google */\r\n provider: \"anthropic\" | \"openai\" | \"google\";\r\n /** API key for the selected provider */\r\n apiKey: string;\r\n /** Whether to ask for confirmation before running suggested commands */\r\n confirmBeforeRun: boolean;\r\n /** Show verbose/debug output */\r\n verbose: boolean;\r\n}\r\n\r\ninterface ConfigFile {\r\n model?: string;\r\n provider?: \"anthropic\" | \"openai\" | \"google\";\r\n apiKey?: string;\r\n confirmBeforeRun?: boolean;\r\n verbose?: boolean;\r\n}\r\n\r\nconst CONFIG_DIR = join(homedir(), \".tf-ai\");\r\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\r\n\r\nfunction loadConfigFile(): ConfigFile {\r\n if (!existsSync(CONFIG_FILE)) {\r\n return {};\r\n }\r\n \r\n try {\r\n const content = readFileSync(CONFIG_FILE, \"utf-8\");\r\n return JSON.parse(content) as ConfigFile;\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\nfunction detectProvider(model: string): \"anthropic\" | \"openai\" | \"google\" {\r\n if (model.startsWith(\"anthropic/\") || model.includes(\"claude\")) {\r\n return \"anthropic\";\r\n }\r\n if (model.startsWith(\"openai/\") || model.includes(\"gpt\")) {\r\n return \"openai\";\r\n }\r\n if (model.startsWith(\"google/\") || model.includes(\"gemini\")) {\r\n return \"google\";\r\n }\r\n // Default to anthropic\r\n return \"anthropic\";\r\n}\r\n\r\nfunction getApiKey(provider: \"anthropic\" | \"openai\" | \"google\", configKey?: string): string {\r\n // Config file key takes precedence\r\n if (configKey) {\r\n return configKey;\r\n }\r\n \r\n // Then check environment variables\r\n const envVars = {\r\n anthropic: \"ANTHROPIC_API_KEY\",\r\n openai: \"OPENAI_API_KEY\",\r\n google: \"GOOGLE_API_KEY\",\r\n } as const;\r\n \r\n const envVarName = envVars[provider];\r\n const envKey = process.env[envVarName];\r\n if (envKey) {\r\n return envKey;\r\n }\r\n \r\n // Also check generic key\r\n return process.env[\"TF_AI_API_KEY\"] ?? \"\";\r\n}\r\n\r\nexport function loadConfig(overrides?: Partial<Config>): Config {\r\n const file = loadConfigFile();\r\n \r\n // Determine model (priority: override > env > file > default)\r\n const model =\r\n overrides?.model ??\r\n process.env[\"TF_AI_MODEL\"] ??\r\n file.model ??\r\n \"claude-sonnet-4-5-20250929\";\r\n \r\n // Detect provider from model string\r\n const provider = \r\n overrides?.provider ?? \r\n file.provider ?? \r\n detectProvider(model);\r\n \r\n // Get API key\r\n const apiKey = getApiKey(provider, file.apiKey);\r\n \r\n return {\r\n model,\r\n provider,\r\n apiKey,\r\n confirmBeforeRun: overrides?.confirmBeforeRun ?? file.confirmBeforeRun ?? true,\r\n verbose: overrides?.verbose ?? file.verbose ?? false,\r\n };\r\n}\r\n\r\nexport function validateConfig(config: Config): { valid: boolean; error?: string } {\r\n if (!config.apiKey) {\r\n const envVar = {\r\n anthropic: \"ANTHROPIC_API_KEY\",\r\n openai: \"OPENAI_API_KEY\",\r\n google: \"GOOGLE_API_KEY\",\r\n }[config.provider];\r\n \r\n return {\r\n valid: false,\r\n error: `No API key found. Set ${envVar} environment variable or add \"apiKey\" to ${CONFIG_FILE}`,\r\n };\r\n }\r\n \r\n return { valid: true };\r\n}\r\n\r\nexport { CONFIG_DIR, CONFIG_FILE };\r\n","import { streamText, Output } from \"ai\";\r\nimport { anthropic } from \"@ai-sdk/anthropic\";\r\nimport { openai } from \"@ai-sdk/openai\";\r\nimport { google } from \"@ai-sdk/google\";\r\nimport type { Config } from \"../config.js\";\r\nimport { SYSTEM_PROMPT, formatUserMessage, formatUserMessageWithHistory } from \"./prompts.js\";\r\nimport { analysisResponseSchema, type AnalysisResponse } from \"./schema.js\";\r\nimport type { CommandContext } from \"../shell/types.js\";\r\n\r\nexport interface CommandSuggestion {\r\n command: string;\r\n explanation: string;\r\n}\r\n\r\nexport interface AnalysisResult {\r\n explanation: string;\r\n suggestion: CommandSuggestion | undefined;\r\n}\r\n\r\nfunction getModel(config: Config) {\r\n const modelName = config.model.includes(\"/\") \r\n ? config.model.split(\"/\")[1]! \r\n : config.model;\r\n \r\n switch (config.provider) {\r\n case \"anthropic\":\r\n return anthropic(modelName);\r\n case \"openai\":\r\n return openai(modelName);\r\n case \"google\":\r\n return google(modelName);\r\n default:\r\n return anthropic(modelName);\r\n }\r\n}\r\n\r\nimport type { EnvironmentContext } from \"../shell/context.js\";\r\n\r\nexport interface StreamCallbacks {\r\n onExplanationUpdate?: (text: string) => void;\r\n onComplete?: (result: AnalysisResult) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\n/**\r\n * Analyzes a command using streaming structured output.\r\n * Calls onExplanationUpdate as the explanation streams in.\r\n * @param history - Optional array of previous commands (without output) for context\r\n */\r\nexport async function analyzeCommandStream(\r\n command: string,\r\n output: string,\r\n config: Config,\r\n environment: EnvironmentContext,\r\n callbacks: StreamCallbacks = {},\r\n history: CommandContext[] = []\r\n): Promise<AnalysisResult> {\r\n const model = getModel(config);\r\n \r\n // Use history-aware formatter if history is provided\r\n const prompt = history.length > 0\r\n ? formatUserMessageWithHistory(command, output, history, environment)\r\n : formatUserMessage(command, output, environment);\r\n \r\n const { partialOutputStream } = streamText({\r\n model,\r\n system: SYSTEM_PROMPT,\r\n prompt,\r\n output: Output.object({ schema: analysisResponseSchema }),\r\n });\r\n\r\n let lastExplanation = \"\";\r\n let finalResponse: Partial<AnalysisResponse> = {};\r\n\r\n try {\r\n for await (const partial of partialOutputStream) {\r\n finalResponse = partial;\r\n \r\n // Stream explanation updates\r\n if (partial.explanation && partial.explanation !== lastExplanation) {\r\n lastExplanation = partial.explanation;\r\n callbacks.onExplanationUpdate?.(partial.explanation);\r\n }\r\n }\r\n } catch (error) {\r\n callbacks.onError?.(error as Error);\r\n throw error;\r\n }\r\n\r\n // Build final result\r\n const result: AnalysisResult = {\r\n explanation: finalResponse.explanation ?? \"\",\r\n suggestion: finalResponse.suggestedCommand \r\n ? {\r\n command: finalResponse.suggestedCommand,\r\n explanation: \"\", // Explanation is in the main field\r\n }\r\n : undefined,\r\n };\r\n\r\n callbacks.onComplete?.(result);\r\n return result;\r\n}\r\n\r\n/**\r\n * Non-streaming version for simpler use cases.\r\n */\r\nexport async function analyzeCommand(\r\n command: string,\r\n output: string,\r\n config: Config,\r\n environment: EnvironmentContext,\r\n history: CommandContext[] = []\r\n): Promise<AnalysisResult> {\r\n return analyzeCommandStream(command, output, config, environment, {}, history);\r\n}\r\n\r\n","import type { EnvironmentContext } from \"../shell/context.js\";\r\nimport type { CommandContext } from \"../shell/types.js\";\r\n\r\nexport const SYSTEM_PROMPT = `You are an expert terminal assistant that helps developers understand command output and troubleshoot issues.\r\n\r\nGiven a command and its output (and optionally recent command history for context), analyze the situation and provide helpful insight. The output could be:\r\n- An error message (syntax error, permission denied, command not found, etc.)\r\n- A warning or unexpected behavior\r\n- A build failure, test failure, or deployment issue\r\n- API/network errors\r\n- Confusing or unclear output that needs explanation\r\n- Or even successful output that the user wants to understand better\r\n\r\nYour response should:\r\n1. **Explain** what happened\r\n2. **Diagnose** the root cause if there's an issue, or at least speculate\r\n3. **Suggest a command** (optional) - only if there's a clear action the user can take\r\n\r\nGuidelines:\r\n- Be concise but thorough (if the problem is complex/deep, you have more leeway and the output can be longer!)\r\n- If you suggest a command, explain why / what it will do\r\n- If you're unsure or lacking enough context, simply say so\r\n- Apart from deliberate paragraphs, do not needlessly put newlines between sentences.\r\n- Tailor suggestions to the user's shell and OS\r\n- When command history is provided, use it to understand the user's workflow and intent`;\r\n\r\n\r\nexport function formatUserMessage(\r\n command: string, \r\n output: string,\r\n context: EnvironmentContext\r\n): string {\r\n const projectInfo = context.projectType ? `\\n- Project Type: ${context.projectType}` : '';\r\n \r\n return `Command: ${command}\r\n\r\nOutput:\r\n${output || \"(no output)\"}\r\n\r\nEnvironment:\r\n- Shell: ${context.shell}\r\n- OS: ${context.os}\r\n- Working Directory: ${context.cwd}${projectInfo}\r\n\r\nAnalyze this command and its output. Explain what happened and suggest a follow-up action if appropriate.`;\r\n}\r\n\r\n/**\r\n * Format user message with command history for better context.\r\n * The history array should be ordered oldest-first, with the most recent command last.\r\n */\r\nexport function formatUserMessageWithHistory(\r\n currentCommand: string,\r\n currentOutput: string,\r\n history: CommandContext[],\r\n context: EnvironmentContext\r\n): string {\r\n const projectInfo = context.projectType ? `\\n- Project Type: ${context.projectType}` : '';\r\n \r\n let historySection = \"\";\r\n if (history.length > 0) {\r\n historySection = \"Recent Command History (oldest first):\\n\";\r\n history.forEach((cmd, index) => {\r\n historySection += `${index + 1}. ${cmd.command}\\n`;\r\n });\r\n historySection += \"\\n\";\r\n }\r\n \r\n return `${historySection}Current Command: ${currentCommand}\r\n\r\nOutput:\r\n${currentOutput || \"(no output)\"}\r\n\r\nEnvironment:\r\n- Shell: ${context.shell}\r\n- OS: ${context.os}\r\n- Working Directory: ${context.cwd}${projectInfo}\r\n\r\nAnalyze this command and its output. Use the command history for context about what the user was trying to accomplish. Explain what happened and suggest a follow-up action if appropriate.`;\r\n}\r\n\r\n","import { z } from \"zod\";\r\n\r\n// Response schema for the AI analysis\r\nexport const analysisResponseSchema = z.object({\r\n explanation: z\r\n .string()\r\n .describe(\r\n \"Clear, helpful explanation of what happened. Interpret the output, diagnose issues, and provide context.\",\r\n ),\r\n suggestedCommand: z\r\n .string()\r\n .nullable()\r\n .describe(\r\n \"A follow-up command the user could run, if applicable. Could be a fix, a next step, or a diagnostic command. Pass null if no command is suggested\",\r\n ),\r\n});\r\n\r\nexport type AnalysisResponse = z.infer<typeof analysisResponseSchema>;\r\n","import { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\nimport { platform } from \"node:os\";\r\n\r\nexport interface EnvironmentContext {\r\n shell: \"powershell\" | \"bash\" | \"zsh\" | \"cmd\" | \"fish\" | \"unknown\";\r\n os: \"windows\" | \"macos\" | \"linux\";\r\n cwd: string;\r\n projectType?: \"git\" | \"nodejs\" | \"python\" | \"docker\";\r\n}\r\n\r\nfunction detectShell(): EnvironmentContext[\"shell\"] {\r\n // Check SHELL env var (Unix-like systems) - Check this FIRST to avoid false positives on Linux\r\n const shell = process.env[\"SHELL\"];\r\n if (shell) {\r\n if (shell.includes(\"bash\")) return \"bash\";\r\n if (shell.includes(\"zsh\")) return \"zsh\";\r\n if (shell.includes(\"fish\")) return \"fish\";\r\n }\r\n\r\n // Check for PowerShell-specific env var\r\n if (process.env[\"PSModulePath\"]) {\r\n return \"powershell\";\r\n }\r\n \r\n // Check for CMD on Windows\r\n if (process.env[\"ComSpec\"]?.includes(\"cmd.exe\")) {\r\n return \"cmd\";\r\n }\r\n \r\n return \"unknown\";\r\n}\r\n\r\nfunction detectOS(): EnvironmentContext[\"os\"] {\r\n const os = platform();\r\n \r\n switch (os) {\r\n case \"win32\":\r\n return \"windows\";\r\n case \"darwin\":\r\n return \"macos\";\r\n case \"linux\":\r\n return \"linux\";\r\n default:\r\n // Fallback to linux for other Unix-like systems\r\n return \"linux\";\r\n }\r\n}\r\n\r\nfunction detectProjectType(cwd: string): EnvironmentContext[\"projectType\"] {\r\n // Check in order of specificity\r\n if (existsSync(join(cwd, \".git\"))) {\r\n return \"git\";\r\n }\r\n \r\n if (existsSync(join(cwd, \"package.json\"))) {\r\n return \"nodejs\";\r\n }\r\n \r\n if (existsSync(join(cwd, \"requirements.txt\")) || existsSync(join(cwd, \"pyproject.toml\"))) {\r\n return \"python\";\r\n }\r\n \r\n if (existsSync(join(cwd, \"Dockerfile\"))) {\r\n return \"docker\";\r\n }\r\n \r\n return undefined;\r\n}\r\n\r\nexport function detectEnvironment(): EnvironmentContext {\r\n const cwd = process.cwd();\r\n const projectType = detectProjectType(cwd);\r\n \r\n return {\r\n shell: detectShell(),\r\n os: detectOS(),\r\n cwd,\r\n ...(projectType ? { projectType } : {}),\r\n };\r\n}\r\n","import { exec } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { ShellAdapter, CommandContext } from \"./types.js\";\r\n\r\nconst execAsync = promisify(exec);\r\n\r\nexport class PowerShellAdapter implements ShellAdapter {\r\n async getLastCommand(): Promise<CommandContext | null> {\r\n // This is called when the user runs tf-ai without explicit --command\r\n // We try to get the last command from PowerShell history\r\n try {\r\n const { stdout } = await execAsync(\r\n 'powershell -Command \"(Get-History -Count 1).CommandLine\"',\r\n { encoding: \"utf-8\" }\r\n );\r\n \r\n const command = stdout.trim();\r\n if (!command) {\r\n return null;\r\n }\r\n \r\n // We can't reliably get the output of the previous command after the fact\r\n // The user should use the shell function which captures output in real-time\r\n return {\r\n command,\r\n output: \"(Output not captured - use the 'fuck' shell function for full functionality)\",\r\n hasOutput: false,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n async getCommandHistory(count: number): Promise<string[]> {\r\n try {\r\n const { stdout } = await execAsync(\r\n `powershell -Command \"(Get-History -Count ${count}).CommandLine\"`,\r\n { encoding: \"utf-8\" }\r\n );\r\n \r\n const commands = stdout\r\n .split(/\\r?\\n/)\r\n .map(cmd => cmd.trim())\r\n .filter(cmd => cmd.length > 0);\r\n \r\n return commands;\r\n } catch {\r\n return [];\r\n }\r\n }\r\n\r\n async executeCommand(command: string): Promise<{ output: string; exitCode: number }> {\r\n try {\r\n const { stdout, stderr } = await execAsync(\r\n `powershell -Command \"${command.replace(/\"/g, '\\\\\"')}\"`,\r\n { encoding: \"utf-8\" }\r\n );\r\n return {\r\n output: stdout + stderr,\r\n exitCode: 0,\r\n };\r\n } catch (error) {\r\n const execError = error as { stdout?: string; stderr?: string; code?: number };\r\n return {\r\n output: (execError.stdout ?? \"\") + (execError.stderr ?? \"\"),\r\n exitCode: execError.code ?? 1,\r\n };\r\n }\r\n }\r\n\r\n getSetupInstructions(): string {\r\n return `\r\nAdd this to your PowerShell profile ($PROFILE):\r\n\r\nfunction fuck {\r\n param(\r\n [Alias(\"r\")][switch]$Rerun\r\n )\r\n \r\n # Get last 3 commands from history (for context)\r\n $historyItems = Get-History -Count 3\r\n $historyCommands = @()\r\n foreach ($item in $historyItems) {\r\n $historyCommands += $item.CommandLine\r\n }\r\n \r\n if ($historyCommands.Count -eq 0) {\r\n Write-Host \"No command in history\" -ForegroundColor Red\r\n return\r\n }\r\n \r\n # The last command is the one we'll capture output for\r\n $lastCmd = $historyCommands[-1]\r\n \r\n # Build history JSON (all commands except the last, which will have output)\r\n $historyJson = @()\r\n for ($i = 0; $i -lt $historyCommands.Count - 1; $i++) {\r\n $historyJson += @{\r\n command = $historyCommands[$i]\r\n hasOutput = $false\r\n }\r\n }\r\n $historyArg = ($historyJson | ConvertTo-Json -Compress) -replace '\"', '\\\\\\\\\"'\r\n \r\n $output = \"\"\r\n \r\n Write-Host \"Last command: \" -NoNewline\r\n Write-Host $lastCmd -ForegroundColor Cyan\r\n \r\n # Use -r/--Rerun flag to re-run the command for output capture\r\n if ($Rerun) {\r\n Write-Host \"Re-running to capture output...\" -ForegroundColor Gray\r\n $output = try { \r\n Invoke-Expression $lastCmd 2>&1 | Out-String \r\n } catch { \r\n $_.Exception.Message \r\n }\r\n } else {\r\n # Default: skip re-execution (no output capture)\r\n $output = \"(output not captured)\"\r\n }\r\n \r\n # Call tf-ai with captured command, output, and history\r\n if ($historyJson.Count -gt 0) {\r\n tf-ai --command $lastCmd --output $output --history \"$historyArg\"\r\n } else {\r\n tf-ai --command $lastCmd --output $output\r\n }\r\n}\r\n\r\n# Usage:\r\n# fuck - Analyze last command (without re-running it)\r\n# fuck -r - Re-run last command to capture output, then analyze\r\n# fuck -Rerun - Same as -r\r\n\r\nThen reload your profile:\r\n. $PROFILE\r\n`.trim();\r\n }\r\n}\r\n\r\nexport const powershell = new PowerShellAdapter();\r\n\r\n","import { exec } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { ShellAdapter, CommandContext } from \"./types.js\";\r\n\r\nconst execAsync = promisify(exec);\r\n\r\nexport class BashAdapter implements ShellAdapter {\r\n async getLastCommand(): Promise<CommandContext | null> {\r\n // fast, non-interactive history lookup not easily possible in pure node without help from shell\r\n // The user really should use the shell alias/function for full functionality.\r\n // However, we can try to read ~/.bash_history if forced, but that's flaky (timestamps etc).\r\n \r\n // Instead, we rely on the shell wrapper passing the command.\r\n // If called directly without wrapper:\r\n return null; \r\n }\r\n\r\n async getCommandHistory(count: number): Promise<string[]> {\r\n try {\r\n // Use fc to get last N commands (works in bash and zsh)\r\n const { stdout } = await execAsync(\r\n `bash -c 'fc -ln -${count} | sed \"s/^[[:space:]]*//\"'`,\r\n { encoding: \"utf-8\" }\r\n );\r\n \r\n const commands = stdout\r\n .split(/\\r?\\n/)\r\n .map(cmd => cmd.trim())\r\n .filter(cmd => cmd.length > 0);\r\n \r\n return commands;\r\n } catch {\r\n return [];\r\n }\r\n }\r\n\r\n async executeCommand(command: string): Promise<{ output: string; exitCode: number }> {\r\n try {\r\n // Use bash -c to execute. \r\n // We escape single quotes in the command by replacing ' with '\\''\r\n const escapedCommand = command.replace(/'/g, \"'\\\\''\");\r\n const { stdout, stderr } = await execAsync(\r\n `bash -c '${escapedCommand}'`,\r\n { encoding: \"utf-8\" }\r\n );\r\n return {\r\n output: stdout + stderr,\r\n exitCode: 0,\r\n };\r\n } catch (error) {\r\n const execError = error as { stdout?: string; stderr?: string; code?: number };\r\n return {\r\n output: (execError.stdout ?? \"\") + (execError.stderr ?? \"\"),\r\n exitCode: execError.code ?? 1,\r\n };\r\n }\r\n }\r\n\r\n getSetupInstructions(): string {\r\n return `\r\n# Add this to your .bashrc or .zshrc:\r\n\r\nfunction fuck() {\r\n local rerun=false\r\n \r\n # Parse arguments\r\n while [[ $# -gt 0 ]]; do\r\n case \"$1\" in\r\n -r|--rerun)\r\n rerun=true\r\n shift\r\n ;;\r\n *)\r\n echo \"Unknown option: $1\"\r\n echo \"Usage: fuck [-r|--rerun]\"\r\n return 1\r\n ;;\r\n esac\r\n done\r\n \r\n # Get last 3 commands from history (for context)\r\n # fc -ln -3 gets the last 3 commands, sed removes leading whitespace\r\n local history_cmds\r\n history_cmds=$(fc -ln -3 2>/dev/null | sed 's/^[[:space:]]*//')\r\n \r\n if [ -z \"$history_cmds\" ]; then\r\n echo \"No command in history\"\r\n return\r\n fi\r\n \r\n # Convert to array (one command per line)\r\n local -a cmds\r\n while IFS= read -r line; do\r\n [ -n \"$line\" ] && cmds+=(\"$line\")\r\n done <<< \"$history_cmds\"\r\n \r\n local cmd_count=\\${#cmds[@]}\r\n if [ \"$cmd_count\" -eq 0 ]; then\r\n echo \"No command in history\"\r\n return\r\n fi\r\n \r\n # The last command is the one we'll capture output for\r\n local last_cmd=\"\\${cmds[$((cmd_count-1))]}\"\r\n \r\n # Build history JSON (all commands except the last)\r\n local history_json=\"[\"\r\n local first=true\r\n for ((i=0; i<cmd_count-1; i++)); do\r\n local cmd=\"\\${cmds[$i]}\"\r\n # Escape quotes and backslashes for JSON\r\n cmd=\\$(printf '%s' \"$cmd\" | sed 's/\\\\\\\\/\\\\\\\\\\\\\\\\/g; s/\"/\\\\\\\\\"/g')\r\n if [ \"$first\" = true ]; then\r\n first=false\r\n else\r\n history_json+=\",\"\r\n fi\r\n history_json+=\"{\\\\\\\"command\\\\\\\":\\\\\\\"$cmd\\\\\\\",\\\\\\\"hasOutput\\\\\\\":false}\"\r\n done\r\n history_json+=\"]\"\r\n \r\n local TF_OUTPUT=\"\"\r\n \r\n echo -n \"Last command: \"\r\n echo -e \"\\\\033[36m$last_cmd\\\\033[0m\"\r\n \r\n # Use -r/--rerun flag to re-run the command for output capture\r\n if [ \"$rerun\" = true ]; then\r\n echo -e \"\\\\033[90mRe-running to capture output...\\\\033[0m\"\r\n TF_OUTPUT=$($last_cmd 2>&1)\r\n else\r\n # Default: skip re-execution (no output capture)\r\n TF_OUTPUT=\"(output not captured)\"\r\n fi\r\n \r\n # Call tf-ai with captured command, output, and history\r\n if [ \"$cmd_count\" -gt 1 ]; then\r\n tf-ai --command \"$last_cmd\" --output \"$TF_OUTPUT\" --history \"$history_json\"\r\n else\r\n tf-ai --command \"$last_cmd\" --output \"$TF_OUTPUT\"\r\n fi\r\n}\r\n\r\n# Usage:\r\n# fuck - Analyze last command (without re-running it)\r\n# fuck -r - Re-run last command to capture output, then analyze\r\n# fuck --rerun - Same as -r\r\n`.trim();\r\n }\r\n}\r\n\r\nexport const bash = new BashAdapter();\r\n","import { detectEnvironment } from \"./context.js\";\r\nimport { PowerShellAdapter, powershell } from \"./powershell.js\";\r\nimport { BashAdapter, bash } from \"./bash.js\";\r\nimport type { ShellAdapter } from \"./types.js\";\r\n\r\nexport { PowerShellAdapter, powershell } from \"./powershell.js\";\r\nexport { BashAdapter, bash } from \"./bash.js\";\r\nexport { detectEnvironment, type EnvironmentContext } from \"./context.js\";\r\nexport type { ShellAdapter, CommandContext } from \"./types.js\";\r\n\r\nlet activeShell: ShellAdapter | null = null;\r\n\r\nexport function getShell(): ShellAdapter {\r\n if (activeShell) {\r\n return activeShell;\r\n }\r\n \r\n const env = detectEnvironment();\r\n \r\n switch (env.shell) {\r\n case \"powershell\":\r\n activeShell = powershell;\r\n break;\r\n case \"bash\":\r\n case \"zsh\":\r\n // Bash adapter works for zsh too for now as they both share 'fc'\r\n activeShell = bash;\r\n break;\r\n case \"cmd\":\r\n // Fallback to powershell for now on Windows if CMD is detected but we want robust features\r\n // Or we could implement CmdAdapter later. For now, defaulting to powershell is safer \r\n // as our powershell adapter uses 'powershell -Command' which works from CMD too.\r\n activeShell = powershell;\r\n break;\r\n default:\r\n // Default fallback based on OS\r\n if (env.os === \"windows\") {\r\n activeShell = powershell;\r\n } else {\r\n activeShell = bash;\r\n }\r\n }\r\n \r\n return activeShell!;\r\n}\r\n","import chalk from \"chalk\";\r\nimport type { CommandSuggestion, AnalysisResult } from \"../ai/index.js\";\r\nimport { theme } from \"./theme.js\";\r\n\r\nconst ICONS = {\r\n robot: \"🤖\",\r\n lightbulb: \"\", // note: remember to add space after it if adding icon\r\n command: \"➜\",\r\n warning: \"⚠️\",\r\n error: \"❌\",\r\n success: \"✓\",\r\n thinking: \"🧠\",\r\n};\r\n\r\nexport function printHeader(): void {\r\n // Clean header using theme\r\n console.log(theme.header(`\\n${ICONS.robot} tf-ai\\n`));\r\n}\r\n\r\nexport function printExplanation(result: AnalysisResult): void {\r\n if (result.explanation) {\r\n console.log(theme.explanation(result.explanation));\r\n console.log();\r\n }\r\n}\r\n\r\nlet lastPrintedLength = 0;\r\n\r\n/**\r\n * For streaming: prints only the new part of the explanation\r\n */\r\nexport function printStreamingExplanation(fullText: string): void {\r\n if (!fullText) return;\r\n\r\n // Calculate the new chunk to print\r\n const newText = fullText.slice(lastPrintedLength);\r\n\r\n if (newText) {\r\n // Use theme explanation color\r\n process.stdout.write(theme.explanation(newText));\r\n lastPrintedLength = fullText.length;\r\n }\r\n}\r\n\r\n/**\r\n * Finalize streaming output with newline and reset state\r\n */\r\nexport function finalizeStreaming(): void {\r\n console.log(); // New line after streaming completes\r\n lastPrintedLength = 0; // Reset for next run\r\n}\r\n\r\nexport function printSuggestion(suggestion: CommandSuggestion): void {\r\n console.log(theme.suggestionLabel(`${ICONS.lightbulb}Suggested command:`));\r\n console.log();\r\n // Using theme.command color\r\n console.log(theme.command(` ${ICONS.command} ${suggestion.command} `));\r\n console.log();\r\n}\r\n\r\nexport function printError(message: string): void {\r\n console.error(theme.error(`${ICONS.error} ${message}`));\r\n}\r\n\r\nexport function printWarning(message: string): void {\r\n console.log(theme.warning(`${ICONS.warning} ${message}`));\r\n}\r\n\r\nexport function printSuccess(message: string): void {\r\n console.log(theme.success(`${ICONS.success} ${message}`));\r\n}\r\n\r\nexport function printCommand(label: string, command: string): void {\r\n console.log(theme.muted(`${label}: `) + theme.text(command));\r\n}\r\n\r\nexport function printVerbose(message: string, verbose: boolean): void {\r\n if (verbose) {\r\n console.log(theme.muted(`[debug] ${message}`));\r\n }\r\n}\r\n","import chalk from \"chalk\";\r\n\r\n/**\r\n * Anthropic-Inspired Palette\r\n * Sophisticated, warm, and professional.\r\n */\r\nexport const COLORS = {\r\n stone: \"#F5F2E8\", // Warmest White / Base\r\n warmGray: \"#9E9A95\", // Muted text / Labels\r\n text: \"#E3DCD2\", // Main reading text (Warm Off-White)\r\n coral: \"#D97757\", // Primary Accent (Anthropic-like orange/coral) - Highlights\r\n maple: \"#C8A682\", // Secondary Accent - Interactions\r\n olive: \"#8E9C6D\", // Success\r\n sienna: \"#C1554D\", // Error\r\n charcoal: \"#2D2B29\", // Dark background text if needed\r\n};\r\n\r\n/**\r\n * Semantic Theme Definitions\r\n */\r\nexport const theme = {\r\n // --- Global Roles ---\r\n primary: chalk.hex(COLORS.coral),\r\n secondary: chalk.hex(COLORS.maple),\r\n success: chalk.hex(COLORS.olive),\r\n error: chalk.hex(COLORS.sienna),\r\n warning: chalk.hex(COLORS.maple), // Maple works well for warning too\r\n muted: chalk.hex(COLORS.warmGray),\r\n text: chalk.hex(COLORS.text),\r\n \r\n // --- Component Specifics ---\r\n \r\n // Header / Branding\r\n header: chalk.hex(COLORS.coral).bold,\r\n \r\n // The suggested command to run\r\n command: chalk.hex(COLORS.stone).bold, // Bright, distinct\r\n \r\n // The AI's explanation text\r\n explanation: chalk.hex(COLORS.text),\r\n \r\n // \"Suggested fix:\" label - subtle yet clear\r\n suggestionLabel: chalk.hex(COLORS.maple).bold,\r\n \r\n // Confidence levels\r\n confidenceHigh: chalk.hex(COLORS.olive),\r\n confidenceMedium: chalk.hex(COLORS.maple),\r\n confidenceLow: chalk.hex(COLORS.sienna),\r\n \r\n // --- Animation Colors ---\r\n // Glitch characters\r\n glitch1: chalk.hex(COLORS.coral),\r\n glitch2: chalk.hex(COLORS.maple),\r\n // Base text for \"Analyzing...\"\r\n glitchBase: chalk.hex(COLORS.warmGray),\r\n};\r\n","import * as readline from \"node:readline\";\r\nimport chalk from \"chalk\";\r\nimport { exec } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport { theme } from \"./theme.js\";\r\nimport { getShell } from \"../shell/index.js\";\r\n\r\nconst execAsync = promisify(exec);\r\n\r\nexport interface ConfirmResult {\r\n action: \"run\" | \"cancel\" | \"edit\";\r\n editedCommand?: string;\r\n}\r\n\r\nexport async function confirmCommand(command: string): Promise<ConfirmResult> {\r\n return new Promise((resolve) => {\r\n // Compact menu on one line\r\n process.stdout.write(\r\n theme.secondary(\"[Enter]\") + theme.muted(\" Run \") +\r\n theme.secondary(\"[e]\") + theme.muted(\" Edit \") +\r\n theme.secondary(\"[Esc/q]\") + theme.muted(\" Cancel\")\r\n );\r\n\r\n // Set up raw mode to capture single keypresses\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(true);\r\n }\r\n process.stdin.resume();\r\n process.stdin.setEncoding(\"utf8\");\r\n\r\n const cleanup = () => {\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(false);\r\n }\r\n process.stdin.pause();\r\n process.stdin.removeAllListeners(\"data\");\r\n };\r\n\r\n process.stdin.once(\"data\", (key: string) => {\r\n cleanup();\r\n\r\n // Enter key\r\n if (key === \"\\r\" || key === \"\\n\") {\r\n resolve({ action: \"run\" });\r\n return;\r\n }\r\n\r\n // Escape or q\r\n if (key === \"\\u001b\" || key === \"q\" || key === \"\\u0003\") {\r\n console.log(\"\\n\" + theme.warning(\"Cancelled\"));\r\n resolve({ action: \"cancel\" });\r\n return;\r\n }\r\n\r\n // Edit\r\n if (key === \"e\" || key === \"E\") {\r\n resolve({ action: \"edit\" });\r\n return;\r\n }\r\n\r\n // Unknown key - treat as cancel\r\n console.log(\"\\n\" + theme.warning(\"Cancelled\"));\r\n resolve({ action: \"cancel\" });\r\n });\r\n });\r\n}\r\n\r\nexport async function editCommand(originalCommand: string): Promise<string | null> {\r\n return new Promise((resolve) => {\r\n const rl = readline.createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n console.log(theme.secondary(\"\\nEdit command (press Enter to confirm, Ctrl+C to cancel):\"));\r\n \r\n rl.question(theme.muted(\"> \"), (answer) => {\r\n rl.close();\r\n const command = answer.trim();\r\n resolve(command || originalCommand);\r\n });\r\n\r\n rl.on(\"close\", () => {\r\n if (!rl.terminal) {\r\n resolve(null);\r\n }\r\n });\r\n });\r\n}\r\n\r\nexport async function runCommand(command: string): Promise<void> {\r\n console.log(theme.secondary(`\\nRunning: ${command}\\n`));\r\n console.log(theme.muted(\"─\".repeat(40)));\r\n \r\n try {\r\n // Run the command and stream output\r\n const { output, exitCode } = await getShell().executeCommand(command);\r\n \r\n // Parse stdout/stderr roughly from the combined output if possible, \r\n // but our adapters return combined output mostly.\r\n // For now we just print the output.\r\n if (output) console.log(output);\r\n \r\n console.log(theme.muted(\"─\".repeat(40)));\r\n if (exitCode === 0) {\r\n console.log(theme.success(\"✓ Command completed\"));\r\n } else {\r\n console.log(theme.error(`✗ Command failed with exit code ${exitCode}`));\r\n }\r\n } catch (error) {\r\n console.log(theme.muted(\"─\".repeat(40)));\r\n console.log(theme.error(`✗ Command failed: ${error}`));\r\n }\r\n}\r\n","import { theme } from \"./theme.js\";\n\n/**\n * Creates a \"hacking\" style animated text where random characters glitch\n * Refined for subtlety and elegance\n */\nexport function createHackingAnimation(text: string): {\n start: () => void;\n stop: () => void;\n} {\n const glitchChars = ['#', '$', '%', '*', '?', '~', '^', '+', '=', ':'];\n let interval: NodeJS.Timeout | null = null;\n let isRunning = false;\n \n const originalText = text;\n const textArray = text.split('');\n \n function glitchText(): string {\n return textArray.map((char, i) => {\n // Skip spaces\n if (char === ' ') return ' ';\n \n // Random chance to glitch (much lower probability for subtlety: 5%)\n if (Math.random() < 0.05) {\n // Use theme colors for glitches\n return Math.random() > 0.5 \n ? theme.glitch1(glitchChars[Math.floor(Math.random() * glitchChars.length)]!)\n : theme.glitch2(glitchChars[Math.floor(Math.random() * glitchChars.length)]!);\n }\n \n // Base text color\n return theme.glitchBase(char);\n }).join('');\n }\n \n function render() {\n if (!isRunning) return;\n \n // Clear line and print glitched text\n process.stdout.write('\\r' + glitchText());\n }\n \n return {\n start() {\n if (isRunning) return;\n isRunning = true;\n \n // Print initial state\n process.stdout.write(theme.glitchBase(originalText));\n \n // Start glitching at slower intervals (120ms) for elegance\n interval = setInterval(render, 120);\n },\n \n stop() {\n if (!isRunning) return;\n isRunning = false;\n \n if (interval) {\n clearInterval(interval);\n interval = null;\n }\n \n // Clear the line\n process.stdout.write('\\r' + ' '.repeat(originalText.length) + '\\r');\n },\n };\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,YAAY,oBAAoB;AACzC,SAAS,eAAe;AACxB,SAAS,YAAY;AAuBrB,IAAM,aAAa,KAAK,QAAQ,GAAG,QAAQ;AAC3C,IAAM,cAAc,KAAK,YAAY,aAAa;AAElD,SAAS,iBAA6B;AACpC,MAAI,CAAC,WAAW,WAAW,GAAG;AAC5B,WAAO,CAAC;AAAA,EACV;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,aAAa,OAAO;AACjD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,eAAe,OAAkD;AACxE,MAAI,MAAM,WAAW,YAAY,KAAK,MAAM,SAAS,QAAQ,GAAG;AAC9D,WAAO;AAAA,EACT;AACA,MAAI,MAAM,WAAW,SAAS,KAAK,MAAM,SAAS,KAAK,GAAG;AACxD,WAAO;AAAA,EACT;AACA,MAAI,MAAM,WAAW,SAAS,KAAK,MAAM,SAAS,QAAQ,GAAG;AAC3D,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,UAAU,UAA6C,WAA4B;AAE1F,MAAI,WAAW;AACb,WAAO;AAAA,EACT;AAGA,QAAM,UAAU;AAAA,IACd,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAEA,QAAM,aAAa,QAAQ,QAAQ;AACnC,QAAM,SAAS,QAAQ,IAAI,UAAU;AACrC,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAGA,SAAO,QAAQ,IAAI,eAAe,KAAK;AACzC;AAEO,SAAS,WAAW,WAAqC;AAC9D,QAAM,OAAO,eAAe;AAG5B,QAAM,QACJ,WAAW,SACX,QAAQ,IAAI,aAAa,KACzB,KAAK,SACL;AAGF,QAAM,WACJ,WAAW,YACX,KAAK,YACL,eAAe,KAAK;AAGtB,QAAM,SAAS,UAAU,UAAU,KAAK,MAAM;AAE9C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB,WAAW,oBAAoB,KAAK,oBAAoB;AAAA,IAC1E,SAAS,WAAW,WAAW,KAAK,WAAW;AAAA,EACjD;AACF;AAEO,SAAS,eAAe,QAAoD;AACjF,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,SAAS;AAAA,MACb,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,EAAE,OAAO,QAAQ;AAEjB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,yBAAyB,MAAM,4CAA4C,WAAW;AAAA,IAC/F;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;;;ACzHA,SAAS,YAAY,cAAc;AACnC,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,cAAc;;;ACAhB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBtB,SAAS,kBACd,SACA,QACA,SACQ;AACR,QAAM,cAAc,QAAQ,cAAc;AAAA,kBAAqB,QAAQ,WAAW,KAAK;AAEvF,SAAO,YAAY,OAAO;AAAA;AAAA;AAAA,EAG1B,UAAU,aAAa;AAAA;AAAA;AAAA,WAGd,QAAQ,KAAK;AAAA,QAChB,QAAQ,EAAE;AAAA,uBACK,QAAQ,GAAG,GAAG,WAAW;AAAA;AAAA;AAGhD;AAMO,SAAS,6BACd,gBACA,eACA,SACA,SACQ;AACR,QAAM,cAAc,QAAQ,cAAc;AAAA,kBAAqB,QAAQ,WAAW,KAAK;AAEvF,MAAI,iBAAiB;AACrB,MAAI,QAAQ,SAAS,GAAG;AACtB,qBAAiB;AACjB,YAAQ,QAAQ,CAAC,KAAK,UAAU;AAC9B,wBAAkB,GAAG,QAAQ,CAAC,KAAK,IAAI,OAAO;AAAA;AAAA,IAChD,CAAC;AACD,sBAAkB;AAAA,EACpB;AAEA,SAAO,GAAG,cAAc,oBAAoB,cAAc;AAAA;AAAA;AAAA,EAG1D,iBAAiB,aAAa;AAAA;AAAA;AAAA,WAGrB,QAAQ,KAAK;AAAA,QAChB,QAAQ,EAAE;AAAA,uBACK,QAAQ,GAAG,GAAG,WAAW;AAAA;AAAA;AAGhD;;;AC/EA,SAAS,SAAS;AAGX,IAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,aAAa,EACV,OAAO,EACP;AAAA,IACC;AAAA,EACF;AAAA,EACF,kBAAkB,EACf,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;;;AFID,SAAS,SAAS,QAAgB;AAChC,QAAM,YAAY,OAAO,MAAM,SAAS,GAAG,IACvC,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC,IACzB,OAAO;AAEX,UAAQ,OAAO,UAAU;AAAA,IACvB,KAAK;AACH,aAAO,UAAU,SAAS;AAAA,IAC5B,KAAK;AACH,aAAO,OAAO,SAAS;AAAA,IACzB,KAAK;AACH,aAAO,OAAO,SAAS;AAAA,IACzB;AACE,aAAO,UAAU,SAAS;AAAA,EAC9B;AACF;AAeA,eAAsB,qBACpB,SACA,QACA,QACA,aACA,YAA6B,CAAC,GAC9B,UAA4B,CAAC,GACJ;AACzB,QAAM,QAAQ,SAAS,MAAM;AAG7B,QAAM,SAAS,QAAQ,SAAS,IAC5B,6BAA6B,SAAS,QAAQ,SAAS,WAAW,IAClE,kBAAkB,SAAS,QAAQ,WAAW;AAElD,QAAM,EAAE,oBAAoB,IAAI,WAAW;AAAA,IACzC;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,QAAQ,OAAO,OAAO,EAAE,QAAQ,uBAAuB,CAAC;AAAA,EAC1D,CAAC;AAED,MAAI,kBAAkB;AACtB,MAAI,gBAA2C,CAAC;AAEhD,MAAI;AACF,qBAAiB,WAAW,qBAAqB;AAC/C,sBAAgB;AAGhB,UAAI,QAAQ,eAAe,QAAQ,gBAAgB,iBAAiB;AAClE,0BAAkB,QAAQ;AAC1B,kBAAU,sBAAsB,QAAQ,WAAW;AAAA,MACrD;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,cAAU,UAAU,KAAc;AAClC,UAAM;AAAA,EACR;AAGA,QAAM,SAAyB;AAAA,IAC7B,aAAa,cAAc,eAAe;AAAA,IAC1C,YAAY,cAAc,mBACtB;AAAA,MACE,SAAS,cAAc;AAAA,MACvB,aAAa;AAAA;AAAA,IACf,IACA;AAAA,EACN;AAEA,YAAU,aAAa,MAAM;AAC7B,SAAO;AACT;;;AGtGA,SAAS,cAAAA,mBAAkB;AAC3B,SAAS,QAAAC,aAAY;AACrB,SAAS,gBAAgB;AASzB,SAAS,cAA2C;AAElD,QAAM,QAAQ,QAAQ,IAAI,OAAO;AACjC,MAAI,OAAO;AACT,QAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AACnC,QAAI,MAAM,SAAS,KAAK,EAAG,QAAO;AAClC,QAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AAAA,EACrC;AAGA,MAAI,QAAQ,IAAI,cAAc,GAAG;AAC/B,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,IAAI,SAAS,GAAG,SAAS,SAAS,GAAG;AAC/C,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,WAAqC;AAC5C,QAAM,KAAK,SAAS;AAEpB,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AAEE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,kBAAkB,KAAgD;AAEzE,MAAID,YAAWC,MAAK,KAAK,MAAM,CAAC,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,MAAID,YAAWC,MAAK,KAAK,cAAc,CAAC,GAAG;AACzC,WAAO;AAAA,EACT;AAEA,MAAID,YAAWC,MAAK,KAAK,kBAAkB,CAAC,KAAKD,YAAWC,MAAK,KAAK,gBAAgB,CAAC,GAAG;AACxF,WAAO;AAAA,EACT;AAEA,MAAID,YAAWC,MAAK,KAAK,YAAY,CAAC,GAAG;AACvC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,oBAAwC;AACtD,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,kBAAkB,GAAG;AAEzC,SAAO;AAAA,IACL,OAAO,YAAY;AAAA,IACnB,IAAI,SAAS;AAAA,IACb;AAAA,IACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,EACvC;AACF;;;AChFA,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAG1B,IAAM,YAAY,UAAU,IAAI;AAEzB,IAAM,oBAAN,MAAgD;AAAA,EACrD,MAAM,iBAAiD;AAGrD,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM;AAAA,QACvB;AAAA,QACA,EAAE,UAAU,QAAQ;AAAA,MACtB;AAEA,YAAM,UAAU,OAAO,KAAK;AAC5B,UAAI,CAAC,SAAS;AACZ,eAAO;AAAA,MACT;AAIA,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,WAAW;AAAA,MACb;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,kBAAkB,OAAkC;AACxD,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM;AAAA,QACvB,4CAA4C,KAAK;AAAA,QACjD,EAAE,UAAU,QAAQ;AAAA,MACtB;AAEA,YAAM,WAAW,OACd,MAAM,OAAO,EACb,IAAI,SAAO,IAAI,KAAK,CAAC,EACrB,OAAO,SAAO,IAAI,SAAS,CAAC;AAE/B,aAAO;AAAA,IACT,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,SAAgE;AACnF,QAAI;AACF,YAAM,EAAE,QAAQ,OAAO,IAAI,MAAM;AAAA,QAC/B,wBAAwB,QAAQ,QAAQ,MAAM,KAAK,CAAC;AAAA,QACpD,EAAE,UAAU,QAAQ;AAAA,MACtB;AACA,aAAO;AAAA,QACL,QAAQ,SAAS;AAAA,QACjB,UAAU;AAAA,MACZ;AAAA,IACF,SAAS,OAAO;AACd,YAAM,YAAY;AAClB,aAAO;AAAA,QACL,SAAS,UAAU,UAAU,OAAO,UAAU,UAAU;AAAA,QACxD,UAAU,UAAU,QAAQ;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,uBAA+B;AAC7B,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkET,KAAK;AAAA,EACL;AACF;AAEO,IAAM,aAAa,IAAI,kBAAkB;;;AC7IhD,SAAS,QAAAC,aAAY;AACrB,SAAS,aAAAC,kBAAiB;AAG1B,IAAMC,aAAYD,WAAUD,KAAI;AAEzB,IAAM,cAAN,MAA0C;AAAA,EAC/C,MAAM,iBAAiD;AAOrD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,kBAAkB,OAAkC;AACxD,QAAI;AAEF,YAAM,EAAE,OAAO,IAAI,MAAME;AAAA,QACvB,oBAAoB,KAAK;AAAA,QACzB,EAAE,UAAU,QAAQ;AAAA,MACtB;AAEA,YAAM,WAAW,OACd,MAAM,OAAO,EACb,IAAI,SAAO,IAAI,KAAK,CAAC,EACrB,OAAO,SAAO,IAAI,SAAS,CAAC;AAE/B,aAAO;AAAA,IACT,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,SAAgE;AACnF,QAAI;AAGF,YAAM,iBAAiB,QAAQ,QAAQ,MAAM,OAAO;AACpD,YAAM,EAAE,QAAQ,OAAO,IAAI,MAAMA;AAAA,QAC/B,YAAY,cAAc;AAAA,QAC1B,EAAE,UAAU,QAAQ;AAAA,MACtB;AACA,aAAO;AAAA,QACL,QAAQ,SAAS;AAAA,QACjB,UAAU;AAAA,MACZ;AAAA,IACF,SAAS,OAAO;AACd,YAAM,YAAY;AAClB,aAAO;AAAA,QACL,SAAS,UAAU,UAAU,OAAO,UAAU,UAAU;AAAA,QACxD,UAAU,UAAU,QAAQ;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,uBAA+B;AAC7B,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwFT,KAAK;AAAA,EACL;AACF;AAEO,IAAM,OAAO,IAAI,YAAY;;;AC7IpC,IAAI,cAAmC;AAEhC,SAAS,WAAyB;AACvC,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,kBAAkB;AAE9B,UAAQ,IAAI,OAAO;AAAA,IACjB,KAAK;AACH,oBAAc;AACd;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AAEH,oBAAc;AACd;AAAA,IACF,KAAK;AAIH,oBAAc;AACd;AAAA,IACF;AAEE,UAAI,IAAI,OAAO,WAAW;AACxB,sBAAc;AAAA,MAChB,OAAO;AACL,sBAAc;AAAA,MAChB;AAAA,EACJ;AAEA,SAAO;AACT;;;AC5CA,OAAkB;;;ACAlB,OAAO,WAAW;AAMX,IAAM,SAAS;AAAA,EACpB,OAAO;AAAA;AAAA,EACP,UAAU;AAAA;AAAA,EACV,MAAM;AAAA;AAAA,EACN,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AAAA,EACP,QAAQ;AAAA;AAAA,EACR,UAAU;AAAA;AACZ;AAKO,IAAM,QAAQ;AAAA;AAAA,EAEnB,SAAS,MAAM,IAAI,OAAO,KAAK;AAAA,EAC/B,WAAW,MAAM,IAAI,OAAO,KAAK;AAAA,EACjC,SAAS,MAAM,IAAI,OAAO,KAAK;AAAA,EAC/B,OAAO,MAAM,IAAI,OAAO,MAAM;AAAA,EAC9B,SAAS,MAAM,IAAI,OAAO,KAAK;AAAA;AAAA,EAC/B,OAAO,MAAM,IAAI,OAAO,QAAQ;AAAA,EAChC,MAAM,MAAM,IAAI,OAAO,IAAI;AAAA;AAAA;AAAA,EAK3B,QAAQ,MAAM,IAAI,OAAO,KAAK,EAAE;AAAA;AAAA,EAGhC,SAAS,MAAM,IAAI,OAAO,KAAK,EAAE;AAAA;AAAA;AAAA,EAGjC,aAAa,MAAM,IAAI,OAAO,IAAI;AAAA;AAAA,EAGlC,iBAAiB,MAAM,IAAI,OAAO,KAAK,EAAE;AAAA;AAAA,EAGzC,gBAAgB,MAAM,IAAI,OAAO,KAAK;AAAA,EACtC,kBAAkB,MAAM,IAAI,OAAO,KAAK;AAAA,EACxC,eAAe,MAAM,IAAI,OAAO,MAAM;AAAA;AAAA;AAAA,EAItC,SAAS,MAAM,IAAI,OAAO,KAAK;AAAA,EAC/B,SAAS,MAAM,IAAI,OAAO,KAAK;AAAA;AAAA,EAE/B,YAAY,MAAM,IAAI,OAAO,QAAQ;AACvC;;;ADnDA,IAAM,QAAQ;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AACZ;AAcA,IAAI,oBAAoB;AAKjB,SAAS,0BAA0B,UAAwB;AAChE,MAAI,CAAC,SAAU;AAGf,QAAM,UAAU,SAAS,MAAM,iBAAiB;AAEhD,MAAI,SAAS;AAEX,YAAQ,OAAO,MAAM,MAAM,YAAY,OAAO,CAAC;AAC/C,wBAAoB,SAAS;AAAA,EAC/B;AACF;AAKO,SAAS,oBAA0B;AACxC,UAAQ,IAAI;AACZ,sBAAoB;AACtB;AAEO,SAAS,gBAAgB,YAAqC;AACnE,UAAQ,IAAI,MAAM,gBAAgB,GAAG,MAAM,SAAS,oBAAoB,CAAC;AACzE,UAAQ,IAAI;AAEZ,UAAQ,IAAI,MAAM,QAAQ,KAAK,MAAM,OAAO,IAAI,WAAW,OAAO,IAAI,CAAC;AACvE,UAAQ,IAAI;AACd;AAEO,SAAS,WAAW,SAAuB;AAChD,UAAQ,MAAM,MAAM,MAAM,GAAG,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;AACxD;AAEO,SAAS,aAAa,SAAuB;AAClD,UAAQ,IAAI,MAAM,QAAQ,GAAG,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;AAC1D;AAMO,SAAS,aAAa,OAAe,SAAuB;AACjE,UAAQ,IAAI,MAAM,MAAM,GAAG,KAAK,IAAI,IAAI,MAAM,KAAK,OAAO,CAAC;AAC7D;AAEO,SAAS,aAAa,SAAiB,SAAwB;AACpE,MAAI,SAAS;AACX,YAAQ,IAAI,MAAM,MAAM,WAAW,OAAO,EAAE,CAAC;AAAA,EAC/C;AACF;;;AEhFA,YAAY,cAAc;AAC1B,OAAkB;AAClB,SAAS,QAAAC,aAAY;AACrB,SAAS,aAAAC,kBAAiB;AAI1B,IAAMC,aAAYC,WAAUC,KAAI;AAOhC,eAAsB,eAAe,SAAyC;AAC5E,SAAO,IAAI,QAAQ,CAAC,YAAY;AAE9B,YAAQ,OAAO;AAAA,MACb,MAAM,UAAU,SAAS,IAAI,MAAM,MAAM,QAAQ,IACjD,MAAM,UAAU,KAAK,IAAI,MAAM,MAAM,SAAS,IAC9C,MAAM,UAAU,SAAS,IAAI,MAAM,MAAM,SAAS;AAAA,IACpD;AAGA,QAAI,QAAQ,MAAM,OAAO;AACvB,cAAQ,MAAM,WAAW,IAAI;AAAA,IAC/B;AACA,YAAQ,MAAM,OAAO;AACrB,YAAQ,MAAM,YAAY,MAAM;AAEhC,UAAM,UAAU,MAAM;AACpB,UAAI,QAAQ,MAAM,OAAO;AACvB,gBAAQ,MAAM,WAAW,KAAK;AAAA,MAChC;AACA,cAAQ,MAAM,MAAM;AACpB,cAAQ,MAAM,mBAAmB,MAAM;AAAA,IACzC;AAEA,YAAQ,MAAM,KAAK,QAAQ,CAAC,QAAgB;AAC1C,cAAQ;AAGR,UAAI,QAAQ,QAAQ,QAAQ,MAAM;AAChC,gBAAQ,EAAE,QAAQ,MAAM,CAAC;AACzB;AAAA,MACF;AAGA,UAAI,QAAQ,UAAY,QAAQ,OAAO,QAAQ,KAAU;AACvD,gBAAQ,IAAI,OAAO,MAAM,QAAQ,WAAW,CAAC;AAC7C,gBAAQ,EAAE,QAAQ,SAAS,CAAC;AAC5B;AAAA,MACF;AAGA,UAAI,QAAQ,OAAO,QAAQ,KAAK;AAC9B,gBAAQ,EAAE,QAAQ,OAAO,CAAC;AAC1B;AAAA,MACF;AAGA,cAAQ,IAAI,OAAO,MAAM,QAAQ,WAAW,CAAC;AAC7C,cAAQ,EAAE,QAAQ,SAAS,CAAC;AAAA,IAC9B,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,YAAY,iBAAiD;AACjF,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,KAAc,yBAAgB;AAAA,MAClC,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,YAAQ,IAAI,MAAM,UAAU,4DAA4D,CAAC;AAEzF,OAAG,SAAS,MAAM,MAAM,IAAI,GAAG,CAAC,WAAW;AACzC,SAAG,MAAM;AACT,YAAM,UAAU,OAAO,KAAK;AAC5B,cAAQ,WAAW,eAAe;AAAA,IACpC,CAAC;AAED,OAAG,GAAG,SAAS,MAAM;AACnB,UAAI,CAAC,GAAG,UAAU;AAChB,gBAAQ,IAAI;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,WAAW,SAAgC;AAC/D,UAAQ,IAAI,MAAM,UAAU;AAAA,WAAc,OAAO;AAAA,CAAI,CAAC;AACtD,UAAQ,IAAI,MAAM,MAAM,SAAI,OAAO,EAAE,CAAC,CAAC;AAEvC,MAAI;AAEF,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,SAAS,EAAE,eAAe,OAAO;AAKpE,QAAI,OAAQ,SAAQ,IAAI,MAAM;AAE9B,YAAQ,IAAI,MAAM,MAAM,SAAI,OAAO,EAAE,CAAC,CAAC;AACvC,QAAI,aAAa,GAAG;AAChB,cAAQ,IAAI,MAAM,QAAQ,0BAAqB,CAAC;AAAA,IACpD,OAAO;AACH,cAAQ,IAAI,MAAM,MAAM,wCAAmC,QAAQ,EAAE,CAAC;AAAA,IAC1E;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,IAAI,MAAM,MAAM,SAAI,OAAO,EAAE,CAAC,CAAC;AACvC,YAAQ,IAAI,MAAM,MAAM,0BAAqB,KAAK,EAAE,CAAC;AAAA,EACvD;AACF;;;AC3GO,SAAS,uBAAuB,MAGrC;AACA,QAAM,cAAc,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AACrE,MAAI,WAAkC;AACtC,MAAI,YAAY;AAEhB,QAAM,eAAe;AACrB,QAAM,YAAY,KAAK,MAAM,EAAE;AAE/B,WAAS,aAAqB;AAC5B,WAAO,UAAU,IAAI,CAAC,MAAM,MAAM;AAEhC,UAAI,SAAS,IAAK,QAAO;AAGzB,UAAI,KAAK,OAAO,IAAI,MAAM;AAExB,eAAO,KAAK,OAAO,IAAI,MACnB,MAAM,QAAQ,YAAY,KAAK,MAAM,KAAK,OAAO,IAAI,YAAY,MAAM,CAAC,CAAE,IAC1E,MAAM,QAAQ,YAAY,KAAK,MAAM,KAAK,OAAO,IAAI,YAAY,MAAM,CAAC,CAAE;AAAA,MAChF;AAGA,aAAO,MAAM,WAAW,IAAI;AAAA,IAC9B,CAAC,EAAE,KAAK,EAAE;AAAA,EACZ;AAEA,WAAS,SAAS;AAChB,QAAI,CAAC,UAAW;AAGhB,YAAQ,OAAO,MAAM,OAAO,WAAW,CAAC;AAAA,EAC1C;AAEA,SAAO;AAAA,IACL,QAAQ;AACN,UAAI,UAAW;AACf,kBAAY;AAGZ,cAAQ,OAAO,MAAM,MAAM,WAAW,YAAY,CAAC;AAGnD,iBAAW,YAAY,QAAQ,GAAG;AAAA,IACpC;AAAA,IAEA,OAAO;AACL,UAAI,CAAC,UAAW;AAChB,kBAAY;AAEZ,UAAI,UAAU;AACZ,sBAAc,QAAQ;AACtB,mBAAW;AAAA,MACb;AAGA,cAAQ,OAAO,MAAM,OAAO,IAAI,OAAO,aAAa,MAAM,IAAI,IAAI;AAAA,IACpE;AAAA,EACF;AACF;;;AZjDA,OAAkB;AAElB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,OAAO,EACZ,YAAY,uEAAuE,EACnF,QAAQ,OAAO;AAElB,QACG,OAAO,2BAA2B,yBAAyB,EAC3D,OAAO,yBAAyB,0CAA0C,EAC1E,OAAO,iBAAiB,gDAAgD,EACxE,OAAO,uBAAuB,uDAAuD,EACrF,OAAO,aAAa,qDAAqD,EACzE,OAAO,iBAAiB,qBAAqB,EAC7C,OAAO,WAAW,+BAA+B,EACjD,OAAO,oBAAoB,6CAA6C,EACxE,OAAO,OAAO,YAST;AAEJ,MAAI,QAAQ,OAAO;AACjB,YAAQ,IAAI,SAAS,EAAE,qBAAqB,CAAC;AAC7C;AAAA,EACF;AAGA,QAAM,kBAAoD,CAAC;AAC3D,MAAI,QAAQ,UAAU,OAAW,iBAAgB,QAAQ,QAAQ;AACjE,MAAI,QAAQ,YAAY,OAAW,iBAAgB,UAAU,QAAQ;AACrE,MAAI,QAAQ,QAAQ,OAAW,iBAAgB,mBAAmB,CAAC,QAAQ;AAE3E,QAAM,SAAS,WAAW,eAAe;AAEzC,eAAa,WAAW,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC,IAAI,OAAO,OAAO;AAEzE,QAAM,aAAa,eAAe,MAAM;AACxC,MAAI,CAAC,WAAW,OAAO;AACrB,eAAW,WAAW,KAAM;AAC5B,YAAQ,IAAI;AAAA,0BAA6B,WAAW,+CAA+C;AACnG,YAAQ,IAAI,wBAAwB;AACpC,YAAQ,IAAI,KAAK,UAAU;AAAA,MACzB,OAAO;AAAA,MACP,QAAQ;AAAA,IACV,GAAG,MAAM,CAAC,CAAC;AACX,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,UAAU,QAAQ;AACtB,MAAI,SAAS,QAAQ,UAAU;AAE/B,MAAI,CAAC,SAAS;AACZ,iBAAa,kDAAkD,OAAO,OAAO;AAC7E,UAAM,UAAU,MAAM,SAAS,EAAE,eAAe;AAEhD,QAAI,SAAS;AACX,gBAAU,QAAQ;AAClB,eAAS,QAAQ;AACjB,mBAAa,gBAAgB,OAAO;AAAA,IACtC,OAAO;AACL,mBAAa,iEAAiE;AAC9E,cAAQ,IAAI,UAAU;AACtB,cAAQ,IAAI,wDAAwD;AACpE,cAAQ,IAAI,oCAAoC;AAChD,cAAQ,IAAI,iBAAiB;AAC7B,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAGA,MAAI,UAAU,OAAO,SAAS;AAC5B,YAAQ,IAAI,WAAW;AACvB,YAAQ,IAAI,MAAM;AAAA,EACpB;AAEA,UAAQ,IAAI;AACZ,QAAM,YAAY,uBAAuB,4BAA4B;AACrE,YAAU,MAAM;AAGhB,QAAM,aAAa,kBAAkB;AACrC,MAAI,OAAO,SAAS;AAClB,iBAAa,gBAAgB,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC,IAAI,IAAI;AAAA,EAC1E;AAGA,MAAI,UAA4B,CAAC;AACjC,MAAI,QAAQ,SAAS;AACnB,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,QAAQ,OAAO;AACzC,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,kBAAU,OAAO,IAAI,CAAC,UAAoD;AAAA,UACxE,SAAS,KAAK;AAAA,UACd,QAAQ;AAAA,UACR,WAAW,KAAK,aAAa;AAAA,QAC/B,EAAE;AACF,qBAAa,oBAAoB,QAAQ,MAAM,sBAAsB,OAAO,OAAO;AAAA,MACrF;AAAA,IACF,SAAS,GAAG;AACV,mBAAa,iCAAiC,CAAC,IAAI,OAAO,OAAO;AAAA,IACnE;AAAA,EACF;AAEA,MAAI;AACF,QAAI,eAAe;AACnB,UAAM,SAAS,MAAM,qBAAqB,SAAS,QAAQ,QAAQ,YAAY;AAAA,MAC7E,qBAAqB,CAAC,SAAS;AAC7B,YAAI,cAAc;AAChB,oBAAU,KAAK;AACf,yBAAe;AAAA,QACjB;AAEA,kCAA0B,IAAI;AAAA,MAChC;AAAA,IACF,GAAG,OAAO;AAEV,QAAI,cAAc;AAChB,gBAAU,KAAK;AAAA,IACjB;AAEA,sBAAkB;AAClB,YAAQ,IAAI;AAGZ,QAAI,OAAO,cAAc,CAAC,QAAQ,SAAS;AACzC,sBAAgB,OAAO,UAAU;AAGjC,UAAI,OAAO,kBAAkB;AAC3B,cAAM,eAAe,MAAM,eAAe,OAAO,WAAW,OAAO;AAEnE,YAAI,aAAa,WAAW,OAAO;AACjC,gBAAM,WAAW,OAAO,WAAW,OAAO;AAAA,QAC5C,WAAW,aAAa,WAAW,QAAQ;AACzC,gBAAM,SAAS,MAAM,YAAY,OAAO,WAAW,OAAO;AAC1D,cAAI,QAAQ;AACV,kBAAM,WAAW,MAAM;AAAA,UACzB;AAAA,QACF;AAAA,MACF,OAAO;AAEL,cAAM,WAAW,OAAO,WAAW,OAAO;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,cAAU,KAAK;AACf,sBAAkB;AAElB,UAAM,MAAM;AACZ,eAAW,8BAA8B,IAAI,OAAO,EAAE;AAEtD,QAAI,OAAO,WAAW,IAAI,OAAO;AAC/B,cAAQ,IAAI,IAAI,KAAK;AAAA,IACvB;AAEA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QAAQ,MAAM;","names":["existsSync","join","exec","promisify","execAsync","exec","promisify","execAsync","promisify","exec"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tf-ai",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "AI-powered terminal assistant that explains errors and suggests fixes",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",