@supatest/cli 0.0.36 → 0.0.37
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 +137 -0
- package/dist/index.js +1319 -201
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -218,6 +218,512 @@ Summarize: X/Y tests passing
|
|
|
218
218
|
}
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
+
// src/prompts/help.ts
|
|
222
|
+
var helpPrompt;
|
|
223
|
+
var init_help = __esm({
|
|
224
|
+
"src/prompts/help.ts"() {
|
|
225
|
+
"use strict";
|
|
226
|
+
helpPrompt = `<role>
|
|
227
|
+
You are the Supatest CLI Help Assistant. You help users understand and use the Supatest CLI effectively. Users come to you with questions about:
|
|
228
|
+
- How to use specific slash commands
|
|
229
|
+
- Available features and capabilities
|
|
230
|
+
- Keyboard shortcuts and input tricks
|
|
231
|
+
- Configuration and project setup
|
|
232
|
+
- Environment variables and settings
|
|
233
|
+
- MCP (Model Context Protocol) server management
|
|
234
|
+
- npm package information and npm link setup
|
|
235
|
+
|
|
236
|
+
You provide clear, friendly, and accurate answers based on the comprehensive CLI documentation below. You can maintain conversation context across multiple messages, answer follow-up questions, and guide users toward the right features for their needs.
|
|
237
|
+
</role>
|
|
238
|
+
|
|
239
|
+
<capabilities>
|
|
240
|
+
You have access to multiple tools that allow you to help users interactively:
|
|
241
|
+
|
|
242
|
+
**Fetching Information:**
|
|
243
|
+
- WebFetch: Retrieve content from external URLs (e.g., npm package page)
|
|
244
|
+
- Package URL: https://www.npmjs.com/package/@supatest/cli
|
|
245
|
+
- Offer to check latest npm docs when users ask about versions or updates
|
|
246
|
+
|
|
247
|
+
**Taking Actions:**
|
|
248
|
+
When users ask for help with configuration, setup, or workflows, you can actually PERFORM those actions:
|
|
249
|
+
- **File Operations**: Read, create, or edit configuration files (.supatest/mcp.json, .supatest/SUPATEST.md, etc.)
|
|
250
|
+
- **Command Execution**: Run bash commands to check Node version, test MCP connections, etc.
|
|
251
|
+
- **Configuration Updates**: Help users add MCP servers, configure settings, update project files
|
|
252
|
+
- **Workflow Guidance**: Walk users through multi-step processes while actually doing the work
|
|
253
|
+
|
|
254
|
+
**When to Take Actions:**
|
|
255
|
+
- User asks "Can you help me add an MCP server?" \u2192 You can edit .supatest/mcp.json directly
|
|
256
|
+
- User asks "How do I configure X?" \u2192 You can create/update the config file as you explain
|
|
257
|
+
- User asks "What's in my current setup?" \u2192 You can read and show them their actual config
|
|
258
|
+
- User asks "Is my MCP server working?" \u2192 You can test the connection
|
|
259
|
+
- User wants to set environment variables \u2192 You can show them the exact file to edit
|
|
260
|
+
|
|
261
|
+
**Action Examples:**
|
|
262
|
+
- Adding a custom MCP server: Read current mcp.json, add new server config, write it back
|
|
263
|
+
- Setting up npm link: Create the necessary directory structure and explain the steps
|
|
264
|
+
- Checking project setup: Read SUPATEST.md to understand their current test framework
|
|
265
|
+
- Fixing configuration: Identify issues and update files to resolve them
|
|
266
|
+
|
|
267
|
+
Always ask permission before making changes to user files, and show them what you're doing.
|
|
268
|
+
</capabilities>
|
|
269
|
+
|
|
270
|
+
<cli-documentation>
|
|
271
|
+
|
|
272
|
+
## Slash Commands
|
|
273
|
+
|
|
274
|
+
Use these commands in interactive mode (type them and press Enter):
|
|
275
|
+
|
|
276
|
+
### Setup & Discovery
|
|
277
|
+
- **/setup** - Check prerequisites and set up required tools
|
|
278
|
+
- Verifies Node.js version (requires 18+)
|
|
279
|
+
- Checks for required browsers and frameworks
|
|
280
|
+
- Configures the default Playwright MCP server
|
|
281
|
+
- Run this once when starting with a new project
|
|
282
|
+
|
|
283
|
+
- **/discover** - Scan your project to detect test framework and structure
|
|
284
|
+
- Reads your package.json to find the test command
|
|
285
|
+
- Examines existing tests to learn patterns and conventions
|
|
286
|
+
- Writes findings to .supatest/SUPATEST.md
|
|
287
|
+
- Helps Supatest understand your project setup
|
|
288
|
+
- Useful when you want to refresh project information
|
|
289
|
+
|
|
290
|
+
### Navigation & View
|
|
291
|
+
- **/help** or **/?** - Show this help system
|
|
292
|
+
- Enter help mode to get answers about CLI features
|
|
293
|
+
- You can ask follow-up questions and explore topics
|
|
294
|
+
- Type regular questions or just ask about what you need
|
|
295
|
+
|
|
296
|
+
- **/resume** - Resume a previous session
|
|
297
|
+
- View recent sessions and pick one to continue
|
|
298
|
+
- Useful when you want to continue work where you left off
|
|
299
|
+
- Sessions are tracked with their progress and results
|
|
300
|
+
|
|
301
|
+
- **/clear** - Clear the message history
|
|
302
|
+
- Removes displayed messages from the terminal
|
|
303
|
+
- Note: This clears the terminal view but starts a new session
|
|
304
|
+
- Useful when you want a fresh start without losing context
|
|
305
|
+
|
|
306
|
+
### Configuration & Control
|
|
307
|
+
- **/model** - Cycle through available AI models
|
|
308
|
+
- Switch between different Claude model versions
|
|
309
|
+
- Useful if one model works better for your use case
|
|
310
|
+
- Model choice affects reasoning capability and speed
|
|
311
|
+
|
|
312
|
+
- **/provider** - Select your LLM provider
|
|
313
|
+
- Choose between "Supatest Managed" (default) or "Claude Max"
|
|
314
|
+
- Supatest Managed: Uses models through Supatest infrastructure
|
|
315
|
+
- Claude Max: Uses your Claude subscription directly
|
|
316
|
+
- Requires Claude Code login for Claude Max
|
|
317
|
+
- Available on macOS, Linux, and Windows
|
|
318
|
+
|
|
319
|
+
- **/mcp** - Show configured MCP servers
|
|
320
|
+
- View all Model Context Protocol servers available to the agent
|
|
321
|
+
- See connection status of each server
|
|
322
|
+
- Add, remove, or test servers
|
|
323
|
+
- MCP servers extend capabilities (e.g., Playwright for browser automation)
|
|
324
|
+
|
|
325
|
+
- **/login** - Authenticate with Supatest
|
|
326
|
+
- Opens your browser to log in to your Supatest account
|
|
327
|
+
- Creates a secure session token
|
|
328
|
+
- Required for most features
|
|
329
|
+
|
|
330
|
+
- **/logout** - Log out of Supatest
|
|
331
|
+
- Clears your authentication token
|
|
332
|
+
- Closes your current session
|
|
333
|
+
|
|
334
|
+
### Feedback & Exit
|
|
335
|
+
- **/feedback** - Report an issue or request a feature
|
|
336
|
+
- Send feedback directly to the Supatest team
|
|
337
|
+
- Describe problems, suggest improvements, or request features
|
|
338
|
+
- Helps improve the product
|
|
339
|
+
|
|
340
|
+
- **/exit** - Exit the CLI
|
|
341
|
+
- Cleanly shut down the Supatest CLI
|
|
342
|
+
- Saves session data
|
|
343
|
+
|
|
344
|
+
### Project Custom Commands
|
|
345
|
+
If your project has custom commands in .supatest/commands/:
|
|
346
|
+
- **/command-name** - Run a project-specific command
|
|
347
|
+
- Type /help to see all available commands
|
|
348
|
+
- Custom commands appear under "Project Commands" section
|
|
349
|
+
- Use Tab to autocomplete command names
|
|
350
|
+
|
|
351
|
+
## Keyboard Shortcuts
|
|
352
|
+
|
|
353
|
+
These work in interactive mode:
|
|
354
|
+
|
|
355
|
+
### Getting Help
|
|
356
|
+
- **?** - Toggle help (when input is empty)
|
|
357
|
+
- **Ctrl+H** - Toggle help menu anytime
|
|
358
|
+
- **ESC or Q** - Close overlays (help, menus, dialogs)
|
|
359
|
+
|
|
360
|
+
### Navigation
|
|
361
|
+
- **Ctrl+M** - Cycle through available models
|
|
362
|
+
- **Ctrl+P** - Cycle through LLM providers
|
|
363
|
+
|
|
364
|
+
### Input & Text Editing
|
|
365
|
+
- **Enter** - Submit your task/prompt
|
|
366
|
+
- **Shift+Enter** - Add a new line in your input (for multi-line prompts)
|
|
367
|
+
- **Ctrl+U** - Clear the current input line
|
|
368
|
+
- **Ctrl+C** - Exit (or clear input if you have content typed)
|
|
369
|
+
- **Ctrl+D** - Exit immediately
|
|
370
|
+
|
|
371
|
+
### Display & Output
|
|
372
|
+
- **Ctrl+L** - Clear terminal screen
|
|
373
|
+
- **Ctrl+O** - Toggle showing tool outputs
|
|
374
|
+
- Useful when you want to hide verbose command execution logs
|
|
375
|
+
|
|
376
|
+
### Interrupt & Control
|
|
377
|
+
- **ESC** - Interrupt a running agent
|
|
378
|
+
- Stops the current operation immediately
|
|
379
|
+
|
|
380
|
+
## File References
|
|
381
|
+
|
|
382
|
+
You can reference files in your prompts using the @filename syntax:
|
|
383
|
+
|
|
384
|
+
**Syntax**: @path/to/file.ts
|
|
385
|
+
- Example: "Fix the bug in @src/app.ts"
|
|
386
|
+
- Tab autocomplete: Type @ and press Tab to autocomplete file paths
|
|
387
|
+
- Drag & drop: You can drag files from your file explorer directly into the terminal
|
|
388
|
+
|
|
389
|
+
This tells the agent which files are relevant to your request.
|
|
390
|
+
|
|
391
|
+
## Configuration
|
|
392
|
+
|
|
393
|
+
### Environment Variables
|
|
394
|
+
|
|
395
|
+
Set these environment variables to configure Supatest:
|
|
396
|
+
|
|
397
|
+
- **SUPATEST_API_KEY** - Your Supatest API key for CI/CD automation
|
|
398
|
+
- Get from your Supatest dashboard
|
|
399
|
+
- Use in CI pipelines instead of interactive login
|
|
400
|
+
- Enables team/org sessions
|
|
401
|
+
|
|
402
|
+
- **SUPATEST_API_URL** - Custom API endpoint (advanced)
|
|
403
|
+
- Default: Supatest production API
|
|
404
|
+
- Use for self-hosted or development instances
|
|
405
|
+
|
|
406
|
+
- **SUPATEST_DEV** - Enable debug logging
|
|
407
|
+
- Set to any value to enable verbose debug output
|
|
408
|
+
- Useful for troubleshooting issues
|
|
409
|
+
- Writes detailed logs for analysis
|
|
410
|
+
|
|
411
|
+
- **CLAUDE_API_KEY** - Claude API key for Claude Max
|
|
412
|
+
- Automatically detected from Claude Code credentials when using Claude Max
|
|
413
|
+
- Only needed if automatic detection fails
|
|
414
|
+
|
|
415
|
+
### .supatest/ Directory Structure
|
|
416
|
+
|
|
417
|
+
Supatest creates and uses a .supatest/ directory in your project:
|
|
418
|
+
|
|
419
|
+
**Key files:**
|
|
420
|
+
- **.supatest/SUPATEST.md** - Project documentation written by /discover
|
|
421
|
+
- Contains test framework information
|
|
422
|
+
- Documents patterns, conventions, and setup
|
|
423
|
+
- Used by the agent to understand your project
|
|
424
|
+
|
|
425
|
+
- **.supatest/mcp.json** - MCP server configuration
|
|
426
|
+
- Defines Model Context Protocol servers available to the agent
|
|
427
|
+
- Can be project-level (committed to version control) or global
|
|
428
|
+
- Created by /setup with default Playwright server
|
|
429
|
+
|
|
430
|
+
- **.supatest/settings.json** - Project settings
|
|
431
|
+
- Stores user preferences
|
|
432
|
+
- Auto-generated when you make selections
|
|
433
|
+
|
|
434
|
+
**Custom content:**
|
|
435
|
+
- **.supatest/commands/** - Custom project commands
|
|
436
|
+
- Create .md files here to define project-specific slash commands
|
|
437
|
+
- Format: markdown with YAML frontmatter describing the command
|
|
438
|
+
- Discovered and shown in /help under "Project Commands"
|
|
439
|
+
|
|
440
|
+
- **.supatest/agents/** - Custom agent definitions (advanced)
|
|
441
|
+
- Define specialized agents for specific tasks
|
|
442
|
+
- Loaded automatically by the system prompt
|
|
443
|
+
|
|
444
|
+
## MCP (Model Context Protocol) Servers
|
|
445
|
+
|
|
446
|
+
MCP servers extend Supatest with additional tools and capabilities.
|
|
447
|
+
|
|
448
|
+
### What is MCP?
|
|
449
|
+
Model Context Protocol is a standard that allows AI agents to interact with external tools and services. MCP servers provide access to:
|
|
450
|
+
- Browser automation (Playwright)
|
|
451
|
+
- File system operations
|
|
452
|
+
- Custom project tools
|
|
453
|
+
- External services
|
|
454
|
+
|
|
455
|
+
### Default Setup
|
|
456
|
+
When you run /setup, Supatest automatically configures:
|
|
457
|
+
- **Playwright MCP Server** - Browser automation for E2E testing
|
|
458
|
+
- Command: npx @modelcontextprotocol/server-playwright
|
|
459
|
+
- Enables: Opening browsers, navigating pages, interacting with UI elements
|
|
460
|
+
|
|
461
|
+
### Configuration
|
|
462
|
+
|
|
463
|
+
MCP servers are configured in JSON files. Two levels of configuration:
|
|
464
|
+
|
|
465
|
+
**Project-level** (.supatest/mcp.json):
|
|
466
|
+
- Specific to your project
|
|
467
|
+
- Committed to version control
|
|
468
|
+
- Shared with your team
|
|
469
|
+
|
|
470
|
+
**Global** (~/.supatest/mcp.json):
|
|
471
|
+
- Applies to all projects on your machine
|
|
472
|
+
- Personal setup
|
|
473
|
+
|
|
474
|
+
Project servers take precedence over global servers with the same name.
|
|
475
|
+
|
|
476
|
+
### Configuration Format
|
|
477
|
+
|
|
478
|
+
\`\`\`json
|
|
479
|
+
{
|
|
480
|
+
"mcpServers": {
|
|
481
|
+
"playwright": {
|
|
482
|
+
"command": "npx",
|
|
483
|
+
"args": ["@modelcontextprotocol/server-playwright"],
|
|
484
|
+
"description": "Browser automation via Playwright",
|
|
485
|
+
"enabled": true
|
|
486
|
+
},
|
|
487
|
+
"custom-tool": {
|
|
488
|
+
"command": "node",
|
|
489
|
+
"args": ["/path/to/server.js"],
|
|
490
|
+
"env": {
|
|
491
|
+
"API_KEY": "value"
|
|
492
|
+
},
|
|
493
|
+
"description": "My custom tool",
|
|
494
|
+
"enabled": true
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
\`\`\`
|
|
499
|
+
|
|
500
|
+
**Fields:**
|
|
501
|
+
- **command** (required) - Executable to run (npx, node, python, etc.)
|
|
502
|
+
- **args** (optional) - Arguments to pass to the command
|
|
503
|
+
- **env** (optional) - Environment variables for the server process
|
|
504
|
+
- **description** (optional) - Human-readable description
|
|
505
|
+
- **enabled** (optional) - Whether server is active (default: true)
|
|
506
|
+
|
|
507
|
+
### Managing Servers with /mcp
|
|
508
|
+
|
|
509
|
+
Type /mcp to open the MCP server management interface:
|
|
510
|
+
|
|
511
|
+
- **View** - See all configured servers with their status and details
|
|
512
|
+
- **A** - Add a new server (guided setup)
|
|
513
|
+
- **R** - Remove a server
|
|
514
|
+
- **T** - Test connection to all servers
|
|
515
|
+
- **ESC/Q** - Close the interface
|
|
516
|
+
|
|
517
|
+
## npm Package Information
|
|
518
|
+
|
|
519
|
+
The Supatest CLI is published on npm as **@supatest/cli**.
|
|
520
|
+
|
|
521
|
+
### Package Details
|
|
522
|
+
- **Name**: @supatest/cli
|
|
523
|
+
- **Registry**: https://www.npmjs.com/package/@supatest/cli
|
|
524
|
+
- **Installation**: npm install -g @supatest/cli (global)
|
|
525
|
+
- **Usage without install**: npx @supatest/cli
|
|
526
|
+
|
|
527
|
+
### npm Link Setup
|
|
528
|
+
|
|
529
|
+
For local development of the Supatest CLI package:
|
|
530
|
+
|
|
531
|
+
**What is npm link?**
|
|
532
|
+
- Creates a symbolic link from your package to global node_modules
|
|
533
|
+
- Allows testing local changes without publishing to npm
|
|
534
|
+
- Perfect for development and testing
|
|
535
|
+
|
|
536
|
+
**Setup steps:**
|
|
537
|
+
|
|
538
|
+
1. Clone the Supatest repository
|
|
539
|
+
2. Navigate to cli/ directory: \`cd cli\`
|
|
540
|
+
3. Link the package: \`npm link\`
|
|
541
|
+
4. Now run \`supatest\` anywhere to use your local development version
|
|
542
|
+
|
|
543
|
+
**Usage:**
|
|
544
|
+
- Run \`supatest\` commands normally - they use your linked local version
|
|
545
|
+
- Changes you make are immediately reflected
|
|
546
|
+
- Useful for testing features before submitting pull requests
|
|
547
|
+
|
|
548
|
+
**To unlink:**
|
|
549
|
+
- Run \`npm unlink -g @supatest/cli\` to restore the npm version
|
|
550
|
+
|
|
551
|
+
**For team members:**
|
|
552
|
+
- Shared development: Other devs can link to the same directory
|
|
553
|
+
- Ensure you're on the same branch
|
|
554
|
+
- Test against each other's changes
|
|
555
|
+
|
|
556
|
+
### Checking Latest Docs
|
|
557
|
+
|
|
558
|
+
I can fetch the latest information from the npm package page if you'd like:
|
|
559
|
+
- Current version and release notes
|
|
560
|
+
- Package size and dependencies
|
|
561
|
+
- Recent updates and changes
|
|
562
|
+
- How to report issues or contribute
|
|
563
|
+
|
|
564
|
+
Just ask: "Can you check the npm docs?" or "What's the latest version?"
|
|
565
|
+
|
|
566
|
+
## Usage Tips & Tricks
|
|
567
|
+
|
|
568
|
+
### Multi-line Prompts
|
|
569
|
+
- Use Shift+Enter to write prompts across multiple lines
|
|
570
|
+
- Useful for complex instructions: "Test the login flow. First, verify email validation. Then test password reset."
|
|
571
|
+
|
|
572
|
+
### Referencing Files
|
|
573
|
+
- Drag files from your file explorer into the terminal to add paths
|
|
574
|
+
- Use @filename syntax with Tab autocomplete
|
|
575
|
+
- The agent will examine those files when analyzing your request
|
|
576
|
+
|
|
577
|
+
### Terminal Integration
|
|
578
|
+
- Most terminals support drag-and-drop of files
|
|
579
|
+
- Works with logs, test output, configuration files
|
|
580
|
+
- Paste long outputs and the agent will analyze them
|
|
581
|
+
|
|
582
|
+
### Running vs Reading Tests
|
|
583
|
+
- The agent can create, run, and fix tests automatically
|
|
584
|
+
- Provide clear descriptions of what to test
|
|
585
|
+
- The agent will handle framework-specific syntax
|
|
586
|
+
|
|
587
|
+
### Session Persistence
|
|
588
|
+
- Your conversations are saved and can be resumed
|
|
589
|
+
- Use /resume to continue work from where you left off
|
|
590
|
+
- Useful for long-running projects
|
|
591
|
+
|
|
592
|
+
### Efficient Feedback
|
|
593
|
+
- Be specific about what's wrong or what you need
|
|
594
|
+
- Include error messages when reporting issues
|
|
595
|
+
- Describe expected vs actual behavior
|
|
596
|
+
|
|
597
|
+
</cli-documentation>
|
|
598
|
+
|
|
599
|
+
<conversation-guidelines>
|
|
600
|
+
When answering questions:
|
|
601
|
+
|
|
602
|
+
1. **Be direct**: Answer the question asked first, then offer related information
|
|
603
|
+
2. **Show examples**: Include practical examples when explaining features
|
|
604
|
+
3. **Cross-reference**: When relevant, mention related commands or features
|
|
605
|
+
4. **Offer help**: Ask if the user needs clarification or has follow-up questions
|
|
606
|
+
5. **Know your limits**: For questions outside CLI features or npm link setup, suggest appropriate next steps
|
|
607
|
+
|
|
608
|
+
When users ask about npm documentation or want latest info:
|
|
609
|
+
- Offer to check the npm package page
|
|
610
|
+
- Use WebFetch to retrieve current information
|
|
611
|
+
- Share relevant details (versions, changes, links)
|
|
612
|
+
|
|
613
|
+
When users mix help with actual test building:
|
|
614
|
+
- Answer their help question
|
|
615
|
+
- Then ask if they want to start building tests or need more information
|
|
616
|
+
|
|
617
|
+
</conversation-guidelines>
|
|
618
|
+
|
|
619
|
+
<action-guide>
|
|
620
|
+
## How to Help Users with Actions
|
|
621
|
+
|
|
622
|
+
Beyond answering questions, you can actively help users by taking actions. Here are common scenarios and how to handle them:
|
|
623
|
+
|
|
624
|
+
### Adding an MCP Server
|
|
625
|
+
|
|
626
|
+
**When user asks:** "Can you help me add an MCP server?" or "How do I add a custom MCP tool?"
|
|
627
|
+
|
|
628
|
+
**Action steps:**
|
|
629
|
+
1. Ask what the MCP server is (command, args, optional env vars, description)
|
|
630
|
+
2. Check if .supatest/mcp.json exists: \`read .supatest/mcp.json\`
|
|
631
|
+
3. Parse the existing config (or create empty structure if missing)
|
|
632
|
+
4. Add the new server to mcpServers object
|
|
633
|
+
5. Write the updated config back
|
|
634
|
+
6. Show the user what was added and test it if they want
|
|
635
|
+
|
|
636
|
+
**Example interaction:**
|
|
637
|
+
- User: "I want to add a custom database tool as MCP server"
|
|
638
|
+
- You: "Great! I can help. Tell me the command to run, any arguments, and what it does"
|
|
639
|
+
- User: "node /path/to/db-tool.js --debug"
|
|
640
|
+
- You: "I'll add that to your .supatest/mcp.json. Let me read your current config first..."
|
|
641
|
+
[Read file, add server, write back]
|
|
642
|
+
- You: "Done! I've added your 'db-tool' server. Let me test the connection..."
|
|
643
|
+
[Run test command]
|
|
644
|
+
|
|
645
|
+
### Checking Current Configuration
|
|
646
|
+
|
|
647
|
+
**When user asks:** "What's my current setup?" or "Show me my MCP servers"
|
|
648
|
+
|
|
649
|
+
**Action steps:**
|
|
650
|
+
1. Read .supatest/SUPATEST.md to show test framework info
|
|
651
|
+
2. Read .supatest/mcp.json to show configured servers
|
|
652
|
+
3. Check .supatest/settings.json for saved preferences
|
|
653
|
+
4. Display the information clearly formatted
|
|
654
|
+
5. Explain what each configuration means
|
|
655
|
+
|
|
656
|
+
### Fixing Configuration Issues
|
|
657
|
+
|
|
658
|
+
**When user says:** "My MCP server isn't working" or "I think my config is wrong"
|
|
659
|
+
|
|
660
|
+
**Action steps:**
|
|
661
|
+
1. Read their configuration files
|
|
662
|
+
2. Test the MCP connection if it's a server
|
|
663
|
+
3. Check for common issues:
|
|
664
|
+
- Missing .supatest/ directory
|
|
665
|
+
- Malformed JSON
|
|
666
|
+
- Invalid command paths
|
|
667
|
+
- Missing required fields
|
|
668
|
+
4. Suggest fixes and apply them with user permission
|
|
669
|
+
5. Test again to confirm resolution
|
|
670
|
+
|
|
671
|
+
### Setting Up npm Link
|
|
672
|
+
|
|
673
|
+
**When user asks:** "How do I set up npm link for development?"
|
|
674
|
+
|
|
675
|
+
**Action steps:**
|
|
676
|
+
1. Verify they're in the cli/ directory (check current location)
|
|
677
|
+
2. Explain the steps
|
|
678
|
+
3. Run the npm link command for them (with permission)
|
|
679
|
+
4. Verify it worked by checking if supatest command is available
|
|
680
|
+
5. Show them how to unlink when done
|
|
681
|
+
|
|
682
|
+
### Environment Variable Help
|
|
683
|
+
|
|
684
|
+
**When user asks:** "How do I set SUPATEST_API_KEY?" or "Where do I put environment variables?"
|
|
685
|
+
|
|
686
|
+
**Action steps:**
|
|
687
|
+
1. Explain the options (export, .env file, shell config)
|
|
688
|
+
2. If they want help creating a .env file, offer to create one
|
|
689
|
+
3. Show them the format they need
|
|
690
|
+
4. For shell profile setup, show the exact lines to add
|
|
691
|
+
|
|
692
|
+
### Project Instructions Update
|
|
693
|
+
|
|
694
|
+
**When user says:** "Can you help me document my project setup?"
|
|
695
|
+
|
|
696
|
+
**Action steps:**
|
|
697
|
+
1. Read their current .supatest/SUPATEST.md if it exists
|
|
698
|
+
2. Ask about their test framework, patterns, conventions
|
|
699
|
+
3. Create/update the documentation
|
|
700
|
+
4. Show them the result and ask if it looks good
|
|
701
|
+
5. Write it back with their approval
|
|
702
|
+
|
|
703
|
+
### Permission and Transparency
|
|
704
|
+
|
|
705
|
+
**Critical rules for actions:**
|
|
706
|
+
- ALWAYS ask permission before modifying files
|
|
707
|
+
- SHOW what you're about to do before doing it
|
|
708
|
+
- After making changes, DISPLAY what was added/modified
|
|
709
|
+
- Explain WHY each change was necessary
|
|
710
|
+
- Offer to undo or adjust if user isn't happy with the result
|
|
711
|
+
- When in doubt, read the file first and show it to user before editing
|
|
712
|
+
|
|
713
|
+
### Tools You Can Use
|
|
714
|
+
|
|
715
|
+
- **Read**: Check configuration and setup files
|
|
716
|
+
- **Write/Edit**: Create or update config files
|
|
717
|
+
- **Bash**: Run commands to check Node version, test connections, verify npm link
|
|
718
|
+
- **WebFetch**: Get latest npm documentation when requested
|
|
719
|
+
|
|
720
|
+
Use these tools naturally when helping - not as a last resort, but as the primary way to provide hands-on assistance.
|
|
721
|
+
|
|
722
|
+
</action-guide>
|
|
723
|
+
`;
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
|
|
221
727
|
// src/prompts/planner.ts
|
|
222
728
|
var plannerPrompt;
|
|
223
729
|
var init_planner = __esm({
|
|
@@ -290,6 +796,7 @@ var init_prompts = __esm({
|
|
|
290
796
|
init_builder();
|
|
291
797
|
init_discover();
|
|
292
798
|
init_fixer();
|
|
799
|
+
init_help();
|
|
293
800
|
init_planner();
|
|
294
801
|
}
|
|
295
802
|
});
|
|
@@ -5384,7 +5891,7 @@ var CLI_VERSION;
|
|
|
5384
5891
|
var init_version = __esm({
|
|
5385
5892
|
"src/version.ts"() {
|
|
5386
5893
|
"use strict";
|
|
5387
|
-
CLI_VERSION = "0.0.
|
|
5894
|
+
CLI_VERSION = "0.0.37";
|
|
5388
5895
|
}
|
|
5389
5896
|
});
|
|
5390
5897
|
|
|
@@ -6240,6 +6747,7 @@ var init_command_discovery = __esm({
|
|
|
6240
6747
|
|
|
6241
6748
|
// src/utils/mcp-loader.ts
|
|
6242
6749
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
6750
|
+
import { homedir as homedir2 } from "os";
|
|
6243
6751
|
import { join as join4 } from "path";
|
|
6244
6752
|
function expandEnvVar(value) {
|
|
6245
6753
|
return value.replace(/\$\{([^}]+)\}/g, (_, expr) => {
|
|
@@ -6262,8 +6770,7 @@ function expandServerConfig(config2) {
|
|
|
6262
6770
|
}
|
|
6263
6771
|
return expanded;
|
|
6264
6772
|
}
|
|
6265
|
-
function
|
|
6266
|
-
const mcpPath = join4(cwd, ".supatest", "mcp.json");
|
|
6773
|
+
function loadMcpServersFromFile(mcpPath) {
|
|
6267
6774
|
if (!existsSync3(mcpPath)) {
|
|
6268
6775
|
return {};
|
|
6269
6776
|
}
|
|
@@ -6275,6 +6782,9 @@ function loadMcpServers(cwd) {
|
|
|
6275
6782
|
}
|
|
6276
6783
|
const expanded = {};
|
|
6277
6784
|
for (const [name, serverConfig] of Object.entries(config2.mcpServers)) {
|
|
6785
|
+
if (serverConfig.enabled === false) {
|
|
6786
|
+
continue;
|
|
6787
|
+
}
|
|
6278
6788
|
expanded[name] = expandServerConfig(serverConfig);
|
|
6279
6789
|
}
|
|
6280
6790
|
return expanded;
|
|
@@ -6286,6 +6796,13 @@ function loadMcpServers(cwd) {
|
|
|
6286
6796
|
return {};
|
|
6287
6797
|
}
|
|
6288
6798
|
}
|
|
6799
|
+
function loadMcpServers(cwd) {
|
|
6800
|
+
const globalMcpPath = join4(homedir2(), ".supatest", "mcp.json");
|
|
6801
|
+
const globalServers = loadMcpServersFromFile(globalMcpPath);
|
|
6802
|
+
const projectMcpPath = join4(cwd, ".supatest", "mcp.json");
|
|
6803
|
+
const projectServers = loadMcpServersFromFile(projectMcpPath);
|
|
6804
|
+
return { ...globalServers, ...projectServers };
|
|
6805
|
+
}
|
|
6289
6806
|
var init_mcp_loader = __esm({
|
|
6290
6807
|
"src/utils/mcp-loader.ts"() {
|
|
6291
6808
|
"use strict";
|
|
@@ -6318,7 +6835,7 @@ var init_project_instructions = __esm({
|
|
|
6318
6835
|
|
|
6319
6836
|
// src/core/agent.ts
|
|
6320
6837
|
import { createRequire } from "module";
|
|
6321
|
-
import { homedir as
|
|
6838
|
+
import { homedir as homedir3 } from "os";
|
|
6322
6839
|
import { dirname, join as join6 } from "path";
|
|
6323
6840
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
6324
6841
|
var CoreAgent;
|
|
@@ -6419,7 +6936,7 @@ ${projectInstructions}`,
|
|
|
6419
6936
|
this.presenter.onLog(`Auth: Using Claude Max (default Claude Code credentials)`);
|
|
6420
6937
|
logger.debug("[agent] Claude Max mode: Using default ~/.claude/ config, cleared provider credentials");
|
|
6421
6938
|
} else {
|
|
6422
|
-
const internalConfigDir = join6(
|
|
6939
|
+
const internalConfigDir = join6(homedir3(), ".supatest", "claude-internal");
|
|
6423
6940
|
cleanEnv.CLAUDE_CONFIG_DIR = internalConfigDir;
|
|
6424
6941
|
cleanEnv.ANTHROPIC_API_KEY = config2.supatestApiKey || "";
|
|
6425
6942
|
cleanEnv.ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || "";
|
|
@@ -6450,9 +6967,17 @@ ${projectInstructions}`,
|
|
|
6450
6967
|
// MCP servers from .supatest/mcp.json
|
|
6451
6968
|
// Users can add servers like Playwright if needed
|
|
6452
6969
|
mcpServers: (() => {
|
|
6970
|
+
logger.debug("[agent] Loading MCP servers for query", { cwd });
|
|
6453
6971
|
const servers = loadMcpServers(cwd);
|
|
6972
|
+
logger.debug("[agent] MCP servers loaded", {
|
|
6973
|
+
count: Object.keys(servers).length,
|
|
6974
|
+
serverNames: Object.keys(servers),
|
|
6975
|
+
fullConfig: servers
|
|
6976
|
+
});
|
|
6454
6977
|
if (Object.keys(servers).length > 0) {
|
|
6455
6978
|
this.presenter.onLog(`MCP servers: ${Object.keys(servers).join(", ")}`);
|
|
6979
|
+
} else {
|
|
6980
|
+
logger.debug("[agent] No MCP servers configured");
|
|
6456
6981
|
}
|
|
6457
6982
|
return servers;
|
|
6458
6983
|
})(),
|
|
@@ -6512,7 +7037,14 @@ ${projectInstructions}`,
|
|
|
6512
7037
|
maxTurns: options.maxTurns,
|
|
6513
7038
|
permissionMode: options.permissionMode,
|
|
6514
7039
|
hasResume: !!options.resume,
|
|
6515
|
-
cwd: options.cwd
|
|
7040
|
+
cwd: options.cwd,
|
|
7041
|
+
mcpServerCount: Object.keys(options.mcpServers || {}).length,
|
|
7042
|
+
mcpServerNames: Object.keys(options.mcpServers || {})
|
|
7043
|
+
});
|
|
7044
|
+
logger.debug("[agent] Full query options", {
|
|
7045
|
+
...options,
|
|
7046
|
+
// Don't log sensitive env vars
|
|
7047
|
+
env: Object.keys(options.env || {})
|
|
6516
7048
|
});
|
|
6517
7049
|
const queryIterator = query({ prompt, options });
|
|
6518
7050
|
if (this.messageBridge) {
|
|
@@ -8425,7 +8957,7 @@ var init_encryption = __esm({
|
|
|
8425
8957
|
|
|
8426
8958
|
// src/utils/token-storage.ts
|
|
8427
8959
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
8428
|
-
import { homedir as
|
|
8960
|
+
import { homedir as homedir5 } from "os";
|
|
8429
8961
|
import { join as join7 } from "path";
|
|
8430
8962
|
function getTokenFilePath() {
|
|
8431
8963
|
const apiUrl = process.env.SUPATEST_API_URL || PRODUCTION_API_URL;
|
|
@@ -8500,7 +9032,7 @@ var init_token_storage = __esm({
|
|
|
8500
9032
|
"src/utils/token-storage.ts"() {
|
|
8501
9033
|
"use strict";
|
|
8502
9034
|
init_encryption();
|
|
8503
|
-
CONFIG_DIR = join7(
|
|
9035
|
+
CONFIG_DIR = join7(homedir5(), ".supatest");
|
|
8504
9036
|
PRODUCTION_API_URL = "https://code-api.supatest.ai";
|
|
8505
9037
|
STORAGE_VERSION = 2;
|
|
8506
9038
|
TOKEN_FILE = join7(CONFIG_DIR, "token.json");
|
|
@@ -8625,18 +9157,22 @@ async function exchangeCodeForToken(code, state) {
|
|
|
8625
9157
|
function openBrowser(url) {
|
|
8626
9158
|
const os3 = platform();
|
|
8627
9159
|
let command;
|
|
9160
|
+
let args;
|
|
8628
9161
|
switch (os3) {
|
|
8629
9162
|
case "darwin":
|
|
8630
9163
|
command = "open";
|
|
9164
|
+
args = [url];
|
|
8631
9165
|
break;
|
|
8632
9166
|
case "win32":
|
|
8633
9167
|
command = "start";
|
|
9168
|
+
args = ["", url];
|
|
8634
9169
|
break;
|
|
8635
9170
|
default:
|
|
8636
9171
|
command = "xdg-open";
|
|
9172
|
+
args = [url];
|
|
8637
9173
|
}
|
|
8638
9174
|
const options = { detached: true, stdio: "ignore", shell: os3 === "win32" };
|
|
8639
|
-
spawn2(command,
|
|
9175
|
+
spawn2(command, args, options).unref();
|
|
8640
9176
|
}
|
|
8641
9177
|
function startCallbackServer(port, expectedState) {
|
|
8642
9178
|
return new Promise((resolve2, reject) => {
|
|
@@ -8975,7 +9511,7 @@ var init_login = __esm({
|
|
|
8975
9511
|
// src/utils/claude-max.ts
|
|
8976
9512
|
import { execSync as execSync5 } from "child_process";
|
|
8977
9513
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
8978
|
-
import { homedir as
|
|
9514
|
+
import { homedir as homedir6 } from "os";
|
|
8979
9515
|
import { join as join8 } from "path";
|
|
8980
9516
|
function isClaudeMaxAvailable() {
|
|
8981
9517
|
const platform2 = process.platform;
|
|
@@ -9012,7 +9548,7 @@ function checkMacOSKeychain() {
|
|
|
9012
9548
|
}
|
|
9013
9549
|
function checkCredentialsFile() {
|
|
9014
9550
|
try {
|
|
9015
|
-
const credentialsPath = join8(
|
|
9551
|
+
const credentialsPath = join8(homedir6(), ".claude", ".credentials.json");
|
|
9016
9552
|
if (!existsSync6(credentialsPath)) {
|
|
9017
9553
|
logger.debug("[claude-max] Credentials file not found", { path: credentialsPath });
|
|
9018
9554
|
return false;
|
|
@@ -9040,16 +9576,248 @@ var init_claude_max = __esm({
|
|
|
9040
9576
|
}
|
|
9041
9577
|
});
|
|
9042
9578
|
|
|
9043
|
-
// src/utils/
|
|
9579
|
+
// src/utils/mcp-manager.ts
|
|
9044
9580
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
9581
|
+
import { homedir as homedir7 } from "os";
|
|
9045
9582
|
import { join as join9 } from "path";
|
|
9583
|
+
function getGlobalMcpPath() {
|
|
9584
|
+
return join9(homedir7(), ".supatest", "mcp.json");
|
|
9585
|
+
}
|
|
9586
|
+
function getProjectMcpPath(cwd) {
|
|
9587
|
+
return join9(cwd, ".supatest", "mcp.json");
|
|
9588
|
+
}
|
|
9589
|
+
function loadMcpConfigFromFile(mcpPath, scope) {
|
|
9590
|
+
if (!existsSync7(mcpPath)) {
|
|
9591
|
+
return {};
|
|
9592
|
+
}
|
|
9593
|
+
try {
|
|
9594
|
+
const content = readFileSync6(mcpPath, "utf-8");
|
|
9595
|
+
const config2 = JSON.parse(content);
|
|
9596
|
+
if (!config2.mcpServers) {
|
|
9597
|
+
return {};
|
|
9598
|
+
}
|
|
9599
|
+
const servers = {};
|
|
9600
|
+
for (const [name, serverConfig] of Object.entries(config2.mcpServers)) {
|
|
9601
|
+
servers[name] = {
|
|
9602
|
+
name,
|
|
9603
|
+
command: serverConfig.command,
|
|
9604
|
+
args: serverConfig.args,
|
|
9605
|
+
env: serverConfig.env,
|
|
9606
|
+
enabled: serverConfig.enabled !== false,
|
|
9607
|
+
// Default to enabled
|
|
9608
|
+
description: serverConfig.description,
|
|
9609
|
+
scope
|
|
9610
|
+
};
|
|
9611
|
+
}
|
|
9612
|
+
return servers;
|
|
9613
|
+
} catch (error) {
|
|
9614
|
+
console.warn(
|
|
9615
|
+
`Warning: Failed to load MCP config from ${mcpPath}:`,
|
|
9616
|
+
error instanceof Error ? error.message : String(error)
|
|
9617
|
+
);
|
|
9618
|
+
return {};
|
|
9619
|
+
}
|
|
9620
|
+
}
|
|
9621
|
+
function loadMcpConfig(cwd) {
|
|
9622
|
+
const globalServers = loadMcpConfigFromFile(getGlobalMcpPath(), "global");
|
|
9623
|
+
const projectServers = loadMcpConfigFromFile(getProjectMcpPath(cwd), "project");
|
|
9624
|
+
return { ...globalServers, ...projectServers };
|
|
9625
|
+
}
|
|
9626
|
+
function saveMcpConfigToFile(mcpPath, servers) {
|
|
9627
|
+
const mcpDir = join9(mcpPath, "..");
|
|
9628
|
+
if (!existsSync7(mcpDir)) {
|
|
9629
|
+
mkdirSync3(mcpDir, { recursive: true });
|
|
9630
|
+
}
|
|
9631
|
+
const config2 = {
|
|
9632
|
+
mcpServers: {}
|
|
9633
|
+
};
|
|
9634
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
9635
|
+
config2.mcpServers[name] = {
|
|
9636
|
+
command: server.command,
|
|
9637
|
+
args: server.args,
|
|
9638
|
+
env: server.env,
|
|
9639
|
+
enabled: server.enabled,
|
|
9640
|
+
description: server.description
|
|
9641
|
+
};
|
|
9642
|
+
}
|
|
9643
|
+
writeFileSync2(mcpPath, JSON.stringify(config2, null, 2), "utf-8");
|
|
9644
|
+
}
|
|
9645
|
+
function saveMcpConfig(cwd, servers) {
|
|
9646
|
+
const globalServers = {};
|
|
9647
|
+
const projectServers = {};
|
|
9648
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
9649
|
+
if (server.scope === "global") {
|
|
9650
|
+
globalServers[name] = server;
|
|
9651
|
+
} else {
|
|
9652
|
+
projectServers[name] = server;
|
|
9653
|
+
}
|
|
9654
|
+
}
|
|
9655
|
+
if (Object.keys(globalServers).length > 0) {
|
|
9656
|
+
saveMcpConfigToFile(getGlobalMcpPath(), globalServers);
|
|
9657
|
+
}
|
|
9658
|
+
if (Object.keys(projectServers).length > 0) {
|
|
9659
|
+
saveMcpConfigToFile(getProjectMcpPath(cwd), projectServers);
|
|
9660
|
+
}
|
|
9661
|
+
}
|
|
9662
|
+
function addMcpServer(cwd, name, command, args, env, description, scope = "project") {
|
|
9663
|
+
const servers = loadMcpConfig(cwd);
|
|
9664
|
+
const isUpdate = !!servers[name];
|
|
9665
|
+
servers[name] = {
|
|
9666
|
+
name,
|
|
9667
|
+
command,
|
|
9668
|
+
args,
|
|
9669
|
+
env,
|
|
9670
|
+
enabled: true,
|
|
9671
|
+
description,
|
|
9672
|
+
scope
|
|
9673
|
+
};
|
|
9674
|
+
try {
|
|
9675
|
+
saveMcpConfig(cwd, servers);
|
|
9676
|
+
const action = isUpdate ? "updated" : "added";
|
|
9677
|
+
const scopeLabel = scope === "global" ? "(global)" : "(project)";
|
|
9678
|
+
return {
|
|
9679
|
+
success: true,
|
|
9680
|
+
message: `MCP server "${name}" ${action} successfully ${scopeLabel}`,
|
|
9681
|
+
isUpdate
|
|
9682
|
+
};
|
|
9683
|
+
} catch (error) {
|
|
9684
|
+
return {
|
|
9685
|
+
success: false,
|
|
9686
|
+
message: `Failed to add MCP server: ${error instanceof Error ? error.message : String(error)}`,
|
|
9687
|
+
isUpdate
|
|
9688
|
+
};
|
|
9689
|
+
}
|
|
9690
|
+
}
|
|
9691
|
+
function removeMcpServer(cwd, name) {
|
|
9692
|
+
const servers = loadMcpConfig(cwd);
|
|
9693
|
+
if (!servers[name]) {
|
|
9694
|
+
return {
|
|
9695
|
+
success: false,
|
|
9696
|
+
message: `MCP server "${name}" not found`
|
|
9697
|
+
};
|
|
9698
|
+
}
|
|
9699
|
+
delete servers[name];
|
|
9700
|
+
try {
|
|
9701
|
+
saveMcpConfig(cwd, servers);
|
|
9702
|
+
return {
|
|
9703
|
+
success: true,
|
|
9704
|
+
message: `MCP server "${name}" removed successfully`
|
|
9705
|
+
};
|
|
9706
|
+
} catch (error) {
|
|
9707
|
+
return {
|
|
9708
|
+
success: false,
|
|
9709
|
+
message: `Failed to remove MCP server: ${error instanceof Error ? error.message : String(error)}`
|
|
9710
|
+
};
|
|
9711
|
+
}
|
|
9712
|
+
}
|
|
9713
|
+
function enableMcpServer(cwd, name) {
|
|
9714
|
+
const servers = loadMcpConfig(cwd);
|
|
9715
|
+
if (!servers[name]) {
|
|
9716
|
+
return {
|
|
9717
|
+
success: false,
|
|
9718
|
+
message: `MCP server "${name}" not found`
|
|
9719
|
+
};
|
|
9720
|
+
}
|
|
9721
|
+
servers[name].enabled = true;
|
|
9722
|
+
try {
|
|
9723
|
+
saveMcpConfig(cwd, servers);
|
|
9724
|
+
return {
|
|
9725
|
+
success: true,
|
|
9726
|
+
message: `MCP server "${name}" enabled`
|
|
9727
|
+
};
|
|
9728
|
+
} catch (error) {
|
|
9729
|
+
return {
|
|
9730
|
+
success: false,
|
|
9731
|
+
message: `Failed to enable MCP server: ${error instanceof Error ? error.message : String(error)}`
|
|
9732
|
+
};
|
|
9733
|
+
}
|
|
9734
|
+
}
|
|
9735
|
+
function disableMcpServer(cwd, name) {
|
|
9736
|
+
const servers = loadMcpConfig(cwd);
|
|
9737
|
+
if (!servers[name]) {
|
|
9738
|
+
return {
|
|
9739
|
+
success: false,
|
|
9740
|
+
message: `MCP server "${name}" not found`
|
|
9741
|
+
};
|
|
9742
|
+
}
|
|
9743
|
+
servers[name].enabled = false;
|
|
9744
|
+
try {
|
|
9745
|
+
saveMcpConfig(cwd, servers);
|
|
9746
|
+
return {
|
|
9747
|
+
success: true,
|
|
9748
|
+
message: `MCP server "${name}" disabled`
|
|
9749
|
+
};
|
|
9750
|
+
} catch (error) {
|
|
9751
|
+
return {
|
|
9752
|
+
success: false,
|
|
9753
|
+
message: `Failed to disable MCP server: ${error instanceof Error ? error.message : String(error)}`
|
|
9754
|
+
};
|
|
9755
|
+
}
|
|
9756
|
+
}
|
|
9757
|
+
async function testMcpConnection(name, server) {
|
|
9758
|
+
try {
|
|
9759
|
+
let Client;
|
|
9760
|
+
let StdioClientTransport;
|
|
9761
|
+
try {
|
|
9762
|
+
const clientModule = await import("@modelcontextprotocol/sdk/client");
|
|
9763
|
+
const stdioModule = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
9764
|
+
Client = clientModule.Client;
|
|
9765
|
+
StdioClientTransport = stdioModule.StdioClientTransport;
|
|
9766
|
+
} catch (error) {
|
|
9767
|
+
return {
|
|
9768
|
+
status: "failed",
|
|
9769
|
+
message: `MCP SDK not installed for server "${name}"`,
|
|
9770
|
+
error: `Install @modelcontextprotocol/sdk to enable connection testing: ${error instanceof Error ? error.message : String(error)}`
|
|
9771
|
+
};
|
|
9772
|
+
}
|
|
9773
|
+
const client = new Client({
|
|
9774
|
+
name: "supatest-mcp-test",
|
|
9775
|
+
version: "1.0.0"
|
|
9776
|
+
});
|
|
9777
|
+
const transport = new StdioClientTransport({
|
|
9778
|
+
command: server.command,
|
|
9779
|
+
args: server.args || [],
|
|
9780
|
+
env: { ...process.env, ...server.env }
|
|
9781
|
+
});
|
|
9782
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
9783
|
+
setTimeout(() => reject(new Error("Connection timeout (5s)")), 5e3);
|
|
9784
|
+
});
|
|
9785
|
+
await Promise.race([
|
|
9786
|
+
(async () => {
|
|
9787
|
+
await client.connect(transport);
|
|
9788
|
+
await client.close();
|
|
9789
|
+
})(),
|
|
9790
|
+
timeoutPromise
|
|
9791
|
+
]);
|
|
9792
|
+
return {
|
|
9793
|
+
status: "connected",
|
|
9794
|
+
message: `MCP server "${name}" connected successfully`
|
|
9795
|
+
};
|
|
9796
|
+
} catch (error) {
|
|
9797
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9798
|
+
return {
|
|
9799
|
+
status: "failed",
|
|
9800
|
+
message: `Failed to connect to MCP server "${name}"`,
|
|
9801
|
+
error: errorMessage
|
|
9802
|
+
};
|
|
9803
|
+
}
|
|
9804
|
+
}
|
|
9805
|
+
var init_mcp_manager = __esm({
|
|
9806
|
+
"src/utils/mcp-manager.ts"() {
|
|
9807
|
+
"use strict";
|
|
9808
|
+
}
|
|
9809
|
+
});
|
|
9810
|
+
|
|
9811
|
+
// src/utils/settings-loader.ts
|
|
9812
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
9813
|
+
import { join as join10 } from "path";
|
|
9046
9814
|
function loadSupatestSettings(cwd) {
|
|
9047
|
-
const settingsPath =
|
|
9048
|
-
if (!
|
|
9815
|
+
const settingsPath = join10(cwd, ".supatest", "settings.json");
|
|
9816
|
+
if (!existsSync8(settingsPath)) {
|
|
9049
9817
|
return {};
|
|
9050
9818
|
}
|
|
9051
9819
|
try {
|
|
9052
|
-
const content =
|
|
9820
|
+
const content = readFileSync7(settingsPath, "utf-8");
|
|
9053
9821
|
return JSON.parse(content);
|
|
9054
9822
|
} catch (error) {
|
|
9055
9823
|
console.warn(
|
|
@@ -9060,11 +9828,11 @@ function loadSupatestSettings(cwd) {
|
|
|
9060
9828
|
}
|
|
9061
9829
|
}
|
|
9062
9830
|
function saveSupatestSettings(cwd, settings) {
|
|
9063
|
-
const settingsDir =
|
|
9064
|
-
const settingsPath =
|
|
9831
|
+
const settingsDir = join10(cwd, ".supatest");
|
|
9832
|
+
const settingsPath = join10(settingsDir, "settings.json");
|
|
9065
9833
|
try {
|
|
9066
|
-
if (!
|
|
9067
|
-
|
|
9834
|
+
if (!existsSync8(settingsDir)) {
|
|
9835
|
+
mkdirSync4(settingsDir, { recursive: true });
|
|
9068
9836
|
}
|
|
9069
9837
|
const existingSettings = loadSupatestSettings(cwd);
|
|
9070
9838
|
const mergedSettings = {
|
|
@@ -9080,7 +9848,7 @@ function saveSupatestSettings(cwd, settings) {
|
|
|
9080
9848
|
...settings.hooks
|
|
9081
9849
|
}
|
|
9082
9850
|
};
|
|
9083
|
-
|
|
9851
|
+
writeFileSync3(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
|
|
9084
9852
|
} catch (error) {
|
|
9085
9853
|
console.warn(
|
|
9086
9854
|
`Warning: Failed to save settings to ${settingsPath}:`,
|
|
@@ -10497,56 +11265,6 @@ var init_FixFlow = __esm({
|
|
|
10497
11265
|
}
|
|
10498
11266
|
});
|
|
10499
11267
|
|
|
10500
|
-
// src/ui/components/HelpMenu.tsx
|
|
10501
|
-
import { Box as Box20, Text as Text18, useInput as useInput5 } from "ink";
|
|
10502
|
-
import React23, { useEffect as useEffect9, useState as useState9 } from "react";
|
|
10503
|
-
var HelpMenu;
|
|
10504
|
-
var init_HelpMenu = __esm({
|
|
10505
|
-
"src/ui/components/HelpMenu.tsx"() {
|
|
10506
|
-
"use strict";
|
|
10507
|
-
init_command_discovery();
|
|
10508
|
-
init_theme();
|
|
10509
|
-
HelpMenu = ({ isAuthenticated, onClose, cwd }) => {
|
|
10510
|
-
const [customCommands, setCustomCommands] = useState9([]);
|
|
10511
|
-
useEffect9(() => {
|
|
10512
|
-
const projectDir = cwd || process.cwd();
|
|
10513
|
-
const commands = discoverCommands(projectDir);
|
|
10514
|
-
setCustomCommands(commands);
|
|
10515
|
-
}, [cwd]);
|
|
10516
|
-
useInput5((input, key) => {
|
|
10517
|
-
if (key.escape || input === "q" || input === "?" || key.ctrl && input === "h") {
|
|
10518
|
-
onClose();
|
|
10519
|
-
}
|
|
10520
|
-
});
|
|
10521
|
-
return /* @__PURE__ */ React23.createElement(
|
|
10522
|
-
Box20,
|
|
10523
|
-
{
|
|
10524
|
-
borderColor: theme.border.accent,
|
|
10525
|
-
borderStyle: "round",
|
|
10526
|
-
flexDirection: "column",
|
|
10527
|
-
paddingX: 2,
|
|
10528
|
-
paddingY: 1
|
|
10529
|
-
},
|
|
10530
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.accent }, "\u{1F4D6} Supatest AI CLI - Help"),
|
|
10531
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10532
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Slash Commands:"),
|
|
10533
|
-
/* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/help"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " or "), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/?"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Toggle this help menu")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/resume"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Resume a previous session")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/clear"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Clear message history")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/model"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Cycle through available models")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/provider"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Select LLM provider (Supatest Managed or Claude Max)")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/setup"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Initial setup for Supatest CLI")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/discover"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Discover test framework and write to SUPATEST.md")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/feedback"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Report an issue or request a feature")), isAuthenticated ? /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/logout"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Log out of Supatest")) : /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/login"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Authenticate with Supatest")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/exit"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Exit the CLI"))),
|
|
10534
|
-
customCommands.length > 0 && /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }), /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Project Commands:"), /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, customCommands.slice(0, 5).map((cmd) => /* @__PURE__ */ React23.createElement(Text18, { key: cmd.name }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/", cmd.name), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, cmd.description ? ` - ${cmd.description}` : ""))), customCommands.length > 5 && /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "...and ", customCommands.length - 5, " more (use Tab to autocomplete)"))),
|
|
10535
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10536
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Keyboard Shortcuts:"),
|
|
10537
|
-
/* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "?"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " ", "- Toggle help (when input is empty)")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+H"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Toggle help")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+C"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " ", "- Exit (or clear input if not empty)")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+D"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Exit immediately")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+L"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Clear terminal screen")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+U"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Clear current input line")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "ESC"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Interrupt running agent")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Shift+Enter"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Add new line in input")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "ctrl+o"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Toggle tool outputs")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+M"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Cycle through models"))),
|
|
10538
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10539
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "File References:"),
|
|
10540
|
-
/* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "@filename"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " ", "- Reference a file (autocomplete with Tab)")), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, 'Example: "Fix the bug in @src/app.ts"')),
|
|
10541
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10542
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Tips:"),
|
|
10543
|
-
/* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Press Enter to submit your task"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Use Shift+Enter to write multi-line prompts"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Drag and drop files into the terminal to add file paths"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 The agent will automatically run tools and fix issues"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Use Ctrl+L to clear the terminal screen without clearing the messages history")),
|
|
10544
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "ESC"), " or ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "?"), " to close"))
|
|
10545
|
-
);
|
|
10546
|
-
};
|
|
10547
|
-
}
|
|
10548
|
-
});
|
|
10549
|
-
|
|
10550
11268
|
// src/ui/utils/file-completion.ts
|
|
10551
11269
|
import fs4 from "fs";
|
|
10552
11270
|
import path4 from "path";
|
|
@@ -10630,8 +11348,8 @@ var init_file_completion = __esm({
|
|
|
10630
11348
|
});
|
|
10631
11349
|
|
|
10632
11350
|
// src/ui/components/ModelSelector.tsx
|
|
10633
|
-
import { Box as
|
|
10634
|
-
import
|
|
11351
|
+
import { Box as Box20, Text as Text18, useInput as useInput5 } from "ink";
|
|
11352
|
+
import React23, { useState as useState9 } from "react";
|
|
10635
11353
|
function getAvailableModels(isClaudeMax) {
|
|
10636
11354
|
if (isClaudeMax) {
|
|
10637
11355
|
return AVAILABLE_MODELS.map((m) => ({
|
|
@@ -10666,8 +11384,8 @@ var init_ModelSelector = __esm({
|
|
|
10666
11384
|
}) => {
|
|
10667
11385
|
const models = getAvailableModels(isClaudeMax);
|
|
10668
11386
|
const currentIndex = models.findIndex((m) => m.id === currentModel);
|
|
10669
|
-
const [selectedIndex, setSelectedIndex] =
|
|
10670
|
-
|
|
11387
|
+
const [selectedIndex, setSelectedIndex] = useState9(currentIndex >= 0 ? currentIndex : 0);
|
|
11388
|
+
useInput5((input, key) => {
|
|
10671
11389
|
if (key.upArrow) {
|
|
10672
11390
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : models.length - 1);
|
|
10673
11391
|
} else if (key.downArrow) {
|
|
@@ -10678,12 +11396,12 @@ var init_ModelSelector = __esm({
|
|
|
10678
11396
|
onCancel();
|
|
10679
11397
|
}
|
|
10680
11398
|
});
|
|
10681
|
-
return /* @__PURE__ */
|
|
11399
|
+
return /* @__PURE__ */ React23.createElement(Box20, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React23.createElement(Box20, { marginBottom: 1 }, /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.accent }, "Select Model")), /* @__PURE__ */ React23.createElement(Box20, { marginBottom: 1 }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "Cost: ", /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.info }, "0.5x"), " (Small) \u2022 ", /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.info }, "1x"), " (Medium) \u2022 ", /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.info }, "2x"), " (Premium)")), /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column" }, models.map((model, index) => {
|
|
10682
11400
|
const isSelected = index === selectedIndex;
|
|
10683
11401
|
const isCurrent = model.id === currentModel;
|
|
10684
11402
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
10685
|
-
return /* @__PURE__ */
|
|
10686
|
-
|
|
11403
|
+
return /* @__PURE__ */ React23.createElement(Box20, { gap: 1, key: model.id }, /* @__PURE__ */ React23.createElement(
|
|
11404
|
+
Text18,
|
|
10687
11405
|
{
|
|
10688
11406
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
10689
11407
|
bold: isSelected,
|
|
@@ -10691,15 +11409,15 @@ var init_ModelSelector = __esm({
|
|
|
10691
11409
|
},
|
|
10692
11410
|
indicator,
|
|
10693
11411
|
model.name.padEnd(12)
|
|
10694
|
-
), /* @__PURE__ */
|
|
10695
|
-
|
|
11412
|
+
), /* @__PURE__ */ React23.createElement(
|
|
11413
|
+
Text18,
|
|
10696
11414
|
{
|
|
10697
11415
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
10698
11416
|
color: isSelected ? "black" : theme.text.dim
|
|
10699
11417
|
},
|
|
10700
11418
|
model.description
|
|
10701
|
-
), isCurrent && /* @__PURE__ */
|
|
10702
|
-
})), /* @__PURE__ */
|
|
11419
|
+
), isCurrent && /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.success }, " (current)"));
|
|
11420
|
+
})), /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "Enter"), " select \u2022 ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "ESC"), " cancel")));
|
|
10703
11421
|
};
|
|
10704
11422
|
}
|
|
10705
11423
|
});
|
|
@@ -10707,8 +11425,8 @@ var init_ModelSelector = __esm({
|
|
|
10707
11425
|
// src/ui/components/InputPrompt.tsx
|
|
10708
11426
|
import path5 from "path";
|
|
10709
11427
|
import chalk4 from "chalk";
|
|
10710
|
-
import { Box as
|
|
10711
|
-
import
|
|
11428
|
+
import { Box as Box21, Text as Text19 } from "ink";
|
|
11429
|
+
import React24, { forwardRef, useEffect as useEffect9, useImperativeHandle, useState as useState10 } from "react";
|
|
10712
11430
|
var InputPrompt;
|
|
10713
11431
|
var init_InputPrompt = __esm({
|
|
10714
11432
|
"src/ui/components/InputPrompt.tsx"() {
|
|
@@ -10732,13 +11450,13 @@ var init_InputPrompt = __esm({
|
|
|
10732
11450
|
isClaudeMax = false
|
|
10733
11451
|
}, ref) => {
|
|
10734
11452
|
const { messages, agentMode, selectedModel, setSelectedModel, isAgentRunning, usageStats } = useSession();
|
|
10735
|
-
const [value, setValue] =
|
|
10736
|
-
const [cursorOffset, setCursorOffset] =
|
|
10737
|
-
const [allFiles, setAllFiles] =
|
|
10738
|
-
const [suggestions, setSuggestions] =
|
|
10739
|
-
const [activeSuggestion, setActiveSuggestion] =
|
|
10740
|
-
const [showSuggestions, setShowSuggestions] =
|
|
10741
|
-
const [mentionStartIndex, setMentionStartIndex] =
|
|
11453
|
+
const [value, setValue] = useState10("");
|
|
11454
|
+
const [cursorOffset, setCursorOffset] = useState10(0);
|
|
11455
|
+
const [allFiles, setAllFiles] = useState10([]);
|
|
11456
|
+
const [suggestions, setSuggestions] = useState10([]);
|
|
11457
|
+
const [activeSuggestion, setActiveSuggestion] = useState10(0);
|
|
11458
|
+
const [showSuggestions, setShowSuggestions] = useState10(false);
|
|
11459
|
+
const [mentionStartIndex, setMentionStartIndex] = useState10(-1);
|
|
10742
11460
|
const BUILTIN_SLASH_COMMANDS = [
|
|
10743
11461
|
{ name: "/help", desc: "Show help" },
|
|
10744
11462
|
{ name: "/resume", desc: "Resume session" },
|
|
@@ -10749,13 +11467,14 @@ var init_InputPrompt = __esm({
|
|
|
10749
11467
|
{ name: "/feedback", desc: "Report an issue" },
|
|
10750
11468
|
{ name: "/setup", desc: "Install Playwright browsers" },
|
|
10751
11469
|
{ name: "/discover", desc: "Discover test framework" },
|
|
11470
|
+
{ name: "/mcp", desc: "Show MCP servers" },
|
|
10752
11471
|
{ name: "/login", desc: "Authenticate with Supatest" },
|
|
10753
11472
|
{ name: "/logout", desc: "Log out" },
|
|
10754
11473
|
{ name: "/exit", desc: "Exit CLI" }
|
|
10755
11474
|
];
|
|
10756
|
-
const [customCommands, setCustomCommands] =
|
|
10757
|
-
const [isSlashCommand, setIsSlashCommand] =
|
|
10758
|
-
|
|
11475
|
+
const [customCommands, setCustomCommands] = useState10([]);
|
|
11476
|
+
const [isSlashCommand, setIsSlashCommand] = useState10(false);
|
|
11477
|
+
useEffect9(() => {
|
|
10759
11478
|
try {
|
|
10760
11479
|
const projectDir = cwd || process.cwd();
|
|
10761
11480
|
const discovered = discoverCommands(projectDir);
|
|
@@ -10776,7 +11495,7 @@ var init_InputPrompt = __esm({
|
|
|
10776
11495
|
onInputChange?.("");
|
|
10777
11496
|
}
|
|
10778
11497
|
}));
|
|
10779
|
-
|
|
11498
|
+
useEffect9(() => {
|
|
10780
11499
|
setTimeout(() => {
|
|
10781
11500
|
try {
|
|
10782
11501
|
const files = getFiles();
|
|
@@ -10890,8 +11609,8 @@ var init_InputPrompt = __esm({
|
|
|
10890
11609
|
setSelectedModel(getNextModel(selectedModel, isClaudeMax));
|
|
10891
11610
|
return;
|
|
10892
11611
|
}
|
|
10893
|
-
if (input === "?" && value.length === 0
|
|
10894
|
-
|
|
11612
|
+
if (input === "?" && value.length === 0) {
|
|
11613
|
+
onSubmit("/help");
|
|
10895
11614
|
return;
|
|
10896
11615
|
}
|
|
10897
11616
|
if (showSuggestions && !key.shift) {
|
|
@@ -10976,8 +11695,8 @@ var init_InputPrompt = __esm({
|
|
|
10976
11695
|
}
|
|
10977
11696
|
charCount += lineLength + 1;
|
|
10978
11697
|
}
|
|
10979
|
-
return /* @__PURE__ */
|
|
10980
|
-
|
|
11698
|
+
return /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column", width: "100%" }, showSuggestions && /* @__PURE__ */ React24.createElement(
|
|
11699
|
+
Box21,
|
|
10981
11700
|
{
|
|
10982
11701
|
borderColor: theme.border.default,
|
|
10983
11702
|
borderStyle: "round",
|
|
@@ -10988,12 +11707,12 @@ var init_InputPrompt = __esm({
|
|
|
10988
11707
|
suggestions.map((item, idx) => {
|
|
10989
11708
|
const isSeparator = item.startsWith("\u2500\u2500\u2500\u2500\u2500");
|
|
10990
11709
|
if (isSeparator) {
|
|
10991
|
-
return /* @__PURE__ */
|
|
11710
|
+
return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim, key: item }, " ", item);
|
|
10992
11711
|
}
|
|
10993
|
-
return /* @__PURE__ */
|
|
11712
|
+
return /* @__PURE__ */ React24.createElement(Text19, { color: idx === activeSuggestion ? theme.text.accent : theme.text.dim, key: item }, idx === activeSuggestion ? "\u276F " : " ", item);
|
|
10994
11713
|
})
|
|
10995
|
-
), /* @__PURE__ */
|
|
10996
|
-
|
|
11714
|
+
), /* @__PURE__ */ React24.createElement(
|
|
11715
|
+
Box21,
|
|
10997
11716
|
{
|
|
10998
11717
|
borderColor: disabled ? theme.border.default : theme.border.accent,
|
|
10999
11718
|
borderStyle: "round",
|
|
@@ -11003,24 +11722,288 @@ var init_InputPrompt = __esm({
|
|
|
11003
11722
|
paddingX: 1,
|
|
11004
11723
|
width: "100%"
|
|
11005
11724
|
},
|
|
11006
|
-
/* @__PURE__ */
|
|
11725
|
+
/* @__PURE__ */ React24.createElement(Box21, { flexDirection: "row" }, /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.accent }, "\u276F "), /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column", flexGrow: 1 }, !hasContent && !disabled && /* @__PURE__ */ React24.createElement(Text19, null, chalk4.inverse(placeholder.slice(0, 1)), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim, italic: true }, placeholder.slice(1))), lines.length > 0 && /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column" }, lines.map((line, idx) => {
|
|
11007
11726
|
if (idx === cursorLine && !disabled) {
|
|
11008
11727
|
const before = line.slice(0, cursorCol);
|
|
11009
11728
|
const charAtCursor = line[cursorCol] || " ";
|
|
11010
11729
|
const after = line.slice(cursorCol + 1);
|
|
11011
|
-
return /* @__PURE__ */
|
|
11730
|
+
return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.primary, key: idx }, before, chalk4.inverse(charAtCursor), after);
|
|
11012
11731
|
}
|
|
11013
|
-
return /* @__PURE__ */
|
|
11014
|
-
})), !hasContent && disabled && /* @__PURE__ */
|
|
11015
|
-
), /* @__PURE__ */
|
|
11732
|
+
return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.primary, key: idx }, line);
|
|
11733
|
+
})), !hasContent && disabled && /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim, italic: true }, "Waiting for agent to complete...")))
|
|
11734
|
+
), /* @__PURE__ */ React24.createElement(Box21, { justifyContent: "space-between", paddingX: 1 }, /* @__PURE__ */ React24.createElement(Box21, { gap: 2 }, /* @__PURE__ */ React24.createElement(Box21, null, /* @__PURE__ */ React24.createElement(Text19, { color: agentMode === "plan" ? theme.status.inProgress : theme.text.dim }, agentMode === "plan" ? "\u23F8 plan" : "\u25B6 build"), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, " (shift+tab)")), /* @__PURE__ */ React24.createElement(Box21, null, /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, "model:"), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.info }, getModelDisplayName(selectedModel)), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, " (Cost: "), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.info }, getModelCostLabel(selectedModel)), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, ") (shift+m)"))), /* @__PURE__ */ React24.createElement(Box21, null, /* @__PURE__ */ React24.createElement(Text19, { color: usageStats && usageStats.contextPct >= 90 ? theme.text.error : usageStats && usageStats.contextPct >= 75 ? theme.text.warning : theme.text.dim }, usageStats?.contextPct ?? 0, "% context used"), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, " ", "(", usageStats ? usageStats.inputTokens >= 1e3 ? `${(usageStats.inputTokens / 1e3).toFixed(1)}K` : usageStats.inputTokens : 0, " / ", usageStats ? usageStats.contextWindow >= 1e3 ? `${(usageStats.contextWindow / 1e3).toFixed(0)}K` : usageStats.contextWindow : "200K", ")"))));
|
|
11016
11735
|
});
|
|
11017
11736
|
InputPrompt.displayName = "InputPrompt";
|
|
11018
11737
|
}
|
|
11019
11738
|
});
|
|
11020
11739
|
|
|
11021
|
-
// src/ui/components/
|
|
11740
|
+
// src/ui/components/McpAddDialog.tsx
|
|
11741
|
+
import { Box as Box22, Text as Text20, useInput as useInput6 } from "ink";
|
|
11742
|
+
import React25, { useState as useState11 } from "react";
|
|
11743
|
+
var McpAddDialog;
|
|
11744
|
+
var init_McpAddDialog = __esm({
|
|
11745
|
+
"src/ui/components/McpAddDialog.tsx"() {
|
|
11746
|
+
"use strict";
|
|
11747
|
+
init_theme();
|
|
11748
|
+
McpAddDialog = ({ onConfirm, onCancel }) => {
|
|
11749
|
+
const [mode, setMode] = useState11("name" /* Name */);
|
|
11750
|
+
const [serverName, setServerName] = useState11("");
|
|
11751
|
+
const [command, setCommand] = useState11("");
|
|
11752
|
+
const [args, setArgs] = useState11("");
|
|
11753
|
+
const [description, setDescription] = useState11("");
|
|
11754
|
+
const [scope, setScope] = useState11("project");
|
|
11755
|
+
useInput6((input, key) => {
|
|
11756
|
+
if (key.escape) {
|
|
11757
|
+
onCancel();
|
|
11758
|
+
return;
|
|
11759
|
+
}
|
|
11760
|
+
if (key.return) {
|
|
11761
|
+
if (mode === "name" /* Name */ && serverName.trim()) {
|
|
11762
|
+
setMode("command" /* Command */);
|
|
11763
|
+
} else if (mode === "command" /* Command */ && command.trim()) {
|
|
11764
|
+
setMode("args" /* Args */);
|
|
11765
|
+
} else if (mode === "args" /* Args */) {
|
|
11766
|
+
setMode("description" /* Description */);
|
|
11767
|
+
} else if (mode === "description" /* Description */) {
|
|
11768
|
+
setMode("scope" /* Scope */);
|
|
11769
|
+
} else if (mode === "scope" /* Scope */) {
|
|
11770
|
+
setMode("confirm" /* Confirm */);
|
|
11771
|
+
} else if (mode === "confirm" /* Confirm */) {
|
|
11772
|
+
const parsedArgs = args.trim().split(/\s+/).filter((a) => a);
|
|
11773
|
+
onConfirm(serverName, command, parsedArgs.length > 0 ? parsedArgs : void 0, {}, description, scope);
|
|
11774
|
+
}
|
|
11775
|
+
return;
|
|
11776
|
+
}
|
|
11777
|
+
if (mode === "name" /* Name */) {
|
|
11778
|
+
if (input.length === 1 && /[a-zA-Z0-9-_]/.test(input)) {
|
|
11779
|
+
setServerName((prev) => prev + input);
|
|
11780
|
+
} else if (key.backspace && serverName.length > 0) {
|
|
11781
|
+
setServerName((prev) => prev.slice(0, -1));
|
|
11782
|
+
}
|
|
11783
|
+
} else if (mode === "command" /* Command */) {
|
|
11784
|
+
if (input.length === 1) {
|
|
11785
|
+
setCommand((prev) => prev + input);
|
|
11786
|
+
} else if (key.backspace && command.length > 0) {
|
|
11787
|
+
setCommand((prev) => prev.slice(0, -1));
|
|
11788
|
+
}
|
|
11789
|
+
} else if (mode === "args" /* Args */) {
|
|
11790
|
+
if (input.length === 1) {
|
|
11791
|
+
setArgs((prev) => prev + input);
|
|
11792
|
+
} else if (key.backspace && args.length > 0) {
|
|
11793
|
+
setArgs((prev) => prev.slice(0, -1));
|
|
11794
|
+
}
|
|
11795
|
+
} else if (mode === "description" /* Description */) {
|
|
11796
|
+
if (input.length === 1) {
|
|
11797
|
+
setDescription((prev) => prev + input);
|
|
11798
|
+
} else if (key.backspace && description.length > 0) {
|
|
11799
|
+
setDescription((prev) => prev.slice(0, -1));
|
|
11800
|
+
}
|
|
11801
|
+
} else if (mode === "scope" /* Scope */) {
|
|
11802
|
+
if (key.upArrow) {
|
|
11803
|
+
setScope("project");
|
|
11804
|
+
} else if (key.downArrow) {
|
|
11805
|
+
setScope("global");
|
|
11806
|
+
}
|
|
11807
|
+
} else if (mode === "confirm" /* Confirm */) {
|
|
11808
|
+
if (input === "y" || input === "Y") {
|
|
11809
|
+
const parsedArgs = args.trim().split(/\s+/).filter((a) => a);
|
|
11810
|
+
onConfirm(
|
|
11811
|
+
serverName,
|
|
11812
|
+
command,
|
|
11813
|
+
parsedArgs.length > 0 ? parsedArgs : void 0,
|
|
11814
|
+
{},
|
|
11815
|
+
description,
|
|
11816
|
+
scope
|
|
11817
|
+
);
|
|
11818
|
+
} else if (input === "n" || input === "N") {
|
|
11819
|
+
onCancel();
|
|
11820
|
+
}
|
|
11821
|
+
}
|
|
11822
|
+
});
|
|
11823
|
+
return /* @__PURE__ */ React25.createElement(
|
|
11824
|
+
Box22,
|
|
11825
|
+
{
|
|
11826
|
+
borderColor: theme.border.accent,
|
|
11827
|
+
borderStyle: "round",
|
|
11828
|
+
flexDirection: "column",
|
|
11829
|
+
paddingX: 2,
|
|
11830
|
+
paddingY: 1
|
|
11831
|
+
},
|
|
11832
|
+
/* @__PURE__ */ React25.createElement(Text20, { bold: true, color: theme.text.accent }, "\u2795 Add MCP Server"),
|
|
11833
|
+
/* @__PURE__ */ React25.createElement(Box22, { marginTop: 1 }),
|
|
11834
|
+
mode === "name" /* Name */ && /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Server name (alphanumeric, - or _):"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, "> ", serverName || "_")),
|
|
11835
|
+
mode === "command" /* Command */ && /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Server name: ", serverName), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 0 }), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Command (e.g., npx, node, python):"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, "> ", command || "_")),
|
|
11836
|
+
mode === "args" /* Args */ && /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Server name: ", serverName), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Command: ", command), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 0 }), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Arguments (space-separated, press Enter to skip):"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, "> ", args || "(none)")),
|
|
11837
|
+
mode === "description" /* Description */ && /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Server name: ", serverName), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Command: ", command), args && /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Args: ", args), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 0 }), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Description (optional, press Enter to skip):"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, "> ", description || "(none)")),
|
|
11838
|
+
mode === "scope" /* Scope */ && /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Server name: ", serverName), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Command: ", command), args && /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Args: ", args), description && /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Description: ", description), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 0 }), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Choose scope (\u2191\u2193 to navigate):"), /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "row", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { bold: scope === "project", color: scope === "project" ? theme.text.accent : theme.text.dim }, scope === "project" ? "\u25B6 " : " ", "Project (.supatest/mcp.json)")), /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React25.createElement(Text20, { bold: scope === "global", color: scope === "global" ? theme.text.accent : theme.text.dim }, scope === "global" ? "\u25B6 " : " ", "Global (~/.supatest/mcp.json)")), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 1 }), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "Press Enter to continue")),
|
|
11839
|
+
mode === "confirm" /* Confirm */ && /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Review configuration:"), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 0 }), /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column", marginLeft: 2 }, /* @__PURE__ */ React25.createElement(Text20, null, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Name: "), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, serverName)), /* @__PURE__ */ React25.createElement(Text20, null, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Command: "), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, command)), args && /* @__PURE__ */ React25.createElement(Text20, null, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Args: "), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, args)), description && /* @__PURE__ */ React25.createElement(Text20, null, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Description: "), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, description)), /* @__PURE__ */ React25.createElement(Text20, null, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Scope: "), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, scope))), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 1 }), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.secondary }, "Add this server? (y/n)")),
|
|
11840
|
+
/* @__PURE__ */ React25.createElement(Box22, { marginTop: 1 }),
|
|
11841
|
+
/* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "Press ESC to cancel")
|
|
11842
|
+
);
|
|
11843
|
+
};
|
|
11844
|
+
}
|
|
11845
|
+
});
|
|
11846
|
+
|
|
11847
|
+
// src/ui/components/McpServerSelector.tsx
|
|
11022
11848
|
import { Box as Box23, Text as Text21, useInput as useInput7 } from "ink";
|
|
11023
11849
|
import React26, { useState as useState12 } from "react";
|
|
11850
|
+
var McpServerSelector;
|
|
11851
|
+
var init_McpServerSelector = __esm({
|
|
11852
|
+
"src/ui/components/McpServerSelector.tsx"() {
|
|
11853
|
+
"use strict";
|
|
11854
|
+
init_theme();
|
|
11855
|
+
McpServerSelector = ({
|
|
11856
|
+
servers,
|
|
11857
|
+
action,
|
|
11858
|
+
onSelect,
|
|
11859
|
+
onCancel
|
|
11860
|
+
}) => {
|
|
11861
|
+
const [selectedIndex, setSelectedIndex] = useState12(0);
|
|
11862
|
+
useInput7((input, key) => {
|
|
11863
|
+
if (key.escape) {
|
|
11864
|
+
onCancel();
|
|
11865
|
+
return;
|
|
11866
|
+
}
|
|
11867
|
+
if (key.upArrow) {
|
|
11868
|
+
setSelectedIndex((prev) => prev > 0 ? prev - 1 : servers.length - 1);
|
|
11869
|
+
} else if (key.downArrow) {
|
|
11870
|
+
setSelectedIndex((prev) => prev < servers.length - 1 ? prev + 1 : 0);
|
|
11871
|
+
} else if (key.return) {
|
|
11872
|
+
onSelect(servers[selectedIndex].name);
|
|
11873
|
+
}
|
|
11874
|
+
});
|
|
11875
|
+
const actionLabels = {
|
|
11876
|
+
remove: "Remove MCP Server",
|
|
11877
|
+
enable: "Enable MCP Server",
|
|
11878
|
+
disable: "Disable MCP Server",
|
|
11879
|
+
test: "Test MCP Server Connection"
|
|
11880
|
+
};
|
|
11881
|
+
const actionColors = {
|
|
11882
|
+
remove: theme.text.error,
|
|
11883
|
+
enable: theme.text.success,
|
|
11884
|
+
disable: theme.text.warning,
|
|
11885
|
+
test: theme.text.accent
|
|
11886
|
+
};
|
|
11887
|
+
return /* @__PURE__ */ React26.createElement(
|
|
11888
|
+
Box23,
|
|
11889
|
+
{
|
|
11890
|
+
borderColor: theme.border.accent,
|
|
11891
|
+
borderStyle: "round",
|
|
11892
|
+
flexDirection: "column",
|
|
11893
|
+
paddingX: 2,
|
|
11894
|
+
paddingY: 1
|
|
11895
|
+
},
|
|
11896
|
+
/* @__PURE__ */ React26.createElement(Text21, { bold: true, color: actionColors[action] }, actionLabels[action]),
|
|
11897
|
+
/* @__PURE__ */ React26.createElement(Box23, { marginTop: 1 }),
|
|
11898
|
+
servers.length === 0 ? /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "column" }, /* @__PURE__ */ React26.createElement(Text21, { color: theme.text.dim }, "No MCP servers configured.")) : /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "column" }, servers.map((server, index) => {
|
|
11899
|
+
const isSelected = index === selectedIndex;
|
|
11900
|
+
const statusEmoji = server.enabled ? "\u2713" : "\u2717";
|
|
11901
|
+
return /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "row", key: server.name }, /* @__PURE__ */ React26.createElement(
|
|
11902
|
+
Text21,
|
|
11903
|
+
{
|
|
11904
|
+
bold: isSelected,
|
|
11905
|
+
color: isSelected ? theme.text.accent : theme.text.dim
|
|
11906
|
+
},
|
|
11907
|
+
isSelected ? "\u276F " : " "
|
|
11908
|
+
), /* @__PURE__ */ React26.createElement(Text21, { color: isSelected ? theme.text.accent : theme.text.secondary }, statusEmoji, " ", server.name), server.scope && /* @__PURE__ */ React26.createElement(Text21, { color: theme.text.dim }, " (", server.scope, ")"), server.description && /* @__PURE__ */ React26.createElement(Text21, { color: theme.text.dim }, " - ", server.description));
|
|
11909
|
+
})),
|
|
11910
|
+
/* @__PURE__ */ React26.createElement(Box23, { marginTop: 1 }),
|
|
11911
|
+
/* @__PURE__ */ React26.createElement(Text21, { color: theme.text.dim }, "Use \u2191\u2193 to select, Enter to confirm, ESC to cancel")
|
|
11912
|
+
);
|
|
11913
|
+
};
|
|
11914
|
+
}
|
|
11915
|
+
});
|
|
11916
|
+
|
|
11917
|
+
// src/ui/components/McpServersDisplay.tsx
|
|
11918
|
+
import { Box as Box24, Text as Text22, useInput as useInput8 } from "ink";
|
|
11919
|
+
import Spinner3 from "ink-spinner";
|
|
11920
|
+
import React27, { useEffect as useEffect10, useState as useState13 } from "react";
|
|
11921
|
+
var McpServersDisplay;
|
|
11922
|
+
var init_McpServersDisplay = __esm({
|
|
11923
|
+
"src/ui/components/McpServersDisplay.tsx"() {
|
|
11924
|
+
"use strict";
|
|
11925
|
+
init_mcp_manager();
|
|
11926
|
+
init_theme();
|
|
11927
|
+
McpServersDisplay = ({
|
|
11928
|
+
onClose,
|
|
11929
|
+
onAdd,
|
|
11930
|
+
onRemove,
|
|
11931
|
+
onTest,
|
|
11932
|
+
cwd
|
|
11933
|
+
}) => {
|
|
11934
|
+
const [servers, setServers] = useState13([]);
|
|
11935
|
+
const [isTestingAll, setIsTestingAll] = useState13(false);
|
|
11936
|
+
useEffect10(() => {
|
|
11937
|
+
const projectDir = cwd || process.cwd();
|
|
11938
|
+
const loadedServers = loadMcpConfig(projectDir);
|
|
11939
|
+
const serversArray = Object.values(loadedServers).map((server) => ({
|
|
11940
|
+
...server,
|
|
11941
|
+
status: "unknown"
|
|
11942
|
+
}));
|
|
11943
|
+
setServers(serversArray);
|
|
11944
|
+
}, [cwd]);
|
|
11945
|
+
useInput8((input, key) => {
|
|
11946
|
+
if (key.escape || input === "q") {
|
|
11947
|
+
onClose();
|
|
11948
|
+
} else if (input === "a") {
|
|
11949
|
+
onAdd?.();
|
|
11950
|
+
} else if (input === "r") {
|
|
11951
|
+
onRemove?.();
|
|
11952
|
+
} else if (input === "t") {
|
|
11953
|
+
onTest?.();
|
|
11954
|
+
}
|
|
11955
|
+
});
|
|
11956
|
+
return /* @__PURE__ */ React27.createElement(
|
|
11957
|
+
Box24,
|
|
11958
|
+
{
|
|
11959
|
+
borderColor: theme.border.accent,
|
|
11960
|
+
borderStyle: "round",
|
|
11961
|
+
flexDirection: "column",
|
|
11962
|
+
paddingX: 2,
|
|
11963
|
+
paddingY: 1
|
|
11964
|
+
},
|
|
11965
|
+
/* @__PURE__ */ React27.createElement(Text22, { bold: true, color: theme.text.accent }, "\u{1F50C} Model Context Protocol (MCP) Servers"),
|
|
11966
|
+
/* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }),
|
|
11967
|
+
servers.length === 0 ? /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column" }, /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "No MCP servers configured."), /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }), /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "A"), " to add a new server, or create", " ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, ".supatest/mcp.json"), ":"), /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }), /* @__PURE__ */ React27.createElement(
|
|
11968
|
+
Box24,
|
|
11969
|
+
{
|
|
11970
|
+
borderColor: theme.border.default,
|
|
11971
|
+
borderStyle: "round",
|
|
11972
|
+
flexDirection: "column",
|
|
11973
|
+
marginLeft: 2,
|
|
11974
|
+
paddingX: 1,
|
|
11975
|
+
paddingY: 0
|
|
11976
|
+
},
|
|
11977
|
+
/* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, `{
|
|
11978
|
+
"mcpServers": {
|
|
11979
|
+
"server-name": {
|
|
11980
|
+
"command": "npx",
|
|
11981
|
+
"args": ["@modelcontextprotocol/server-package"]
|
|
11982
|
+
}
|
|
11983
|
+
}
|
|
11984
|
+
}`)
|
|
11985
|
+
)) : /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column" }, isTestingAll && /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "row", marginBottom: 1 }, /* @__PURE__ */ React27.createElement(Box24, { width: 2 }, /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.accent }, /* @__PURE__ */ React27.createElement(Spinner3, { type: "dots" }))), /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.secondary }, "Testing connections...")), servers.map((server, index) => {
|
|
11986
|
+
let statusIndicator = "";
|
|
11987
|
+
let statusText = "";
|
|
11988
|
+
if (server.status === "connected") {
|
|
11989
|
+
statusIndicator = "\u2713";
|
|
11990
|
+
statusText = " - Connected";
|
|
11991
|
+
} else if (server.status === "failed") {
|
|
11992
|
+
statusIndicator = "\u2717";
|
|
11993
|
+
statusText = " - Failed";
|
|
11994
|
+
}
|
|
11995
|
+
return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", key: server.name }, index > 0 && /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }), /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "row" }, statusIndicator && /* @__PURE__ */ React27.createElement(Text22, { color: server.status === "connected" ? theme.text.success : theme.text.error }, statusIndicator, " "), /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: theme.text.accent }, server.name), server.scope && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, " (", server.scope, ")"), !server.enabled && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.warning }, " [disabled]"), statusText && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, statusText)), /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.secondary }, "Command: ", /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, server.command)), server.args && server.args.length > 0 && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.secondary }, "Args: ", /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, server.args.join(" "))), server.env && Object.keys(server.env).length > 0 && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.secondary }, "Env: ", /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, Object.entries(server.env).map(([k, v]) => `${k}=${v}`).join(", "))), server.description && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.secondary }, "Description:", " ", /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, server.description)), server.status === "failed" && server.errorMessage && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.error }, "Error: ", server.errorMessage)));
|
|
11996
|
+
})),
|
|
11997
|
+
/* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }),
|
|
11998
|
+
/* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column" }, /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "A"), " - Add server | ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "R"), " - Remove | ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "T"), " - Test"), /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "ESC"), " or ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "Q"), " to close"))
|
|
11999
|
+
);
|
|
12000
|
+
};
|
|
12001
|
+
}
|
|
12002
|
+
});
|
|
12003
|
+
|
|
12004
|
+
// src/ui/components/ProviderSelector.tsx
|
|
12005
|
+
import { Box as Box25, Text as Text23, useInput as useInput9 } from "ink";
|
|
12006
|
+
import React28, { useState as useState14 } from "react";
|
|
11024
12007
|
var PROVIDERS, ProviderSelector;
|
|
11025
12008
|
var init_ProviderSelector = __esm({
|
|
11026
12009
|
"src/ui/components/ProviderSelector.tsx"() {
|
|
@@ -11050,10 +12033,10 @@ var init_ProviderSelector = __esm({
|
|
|
11050
12033
|
const currentIndex = availableProviders.findIndex(
|
|
11051
12034
|
(p) => p.id === currentProvider
|
|
11052
12035
|
);
|
|
11053
|
-
const [selectedIndex, setSelectedIndex] =
|
|
12036
|
+
const [selectedIndex, setSelectedIndex] = useState14(
|
|
11054
12037
|
currentIndex >= 0 ? currentIndex : 0
|
|
11055
12038
|
);
|
|
11056
|
-
|
|
12039
|
+
useInput9((input, key) => {
|
|
11057
12040
|
if (key.upArrow) {
|
|
11058
12041
|
setSelectedIndex(
|
|
11059
12042
|
(prev) => prev > 0 ? prev - 1 : availableProviders.length - 1
|
|
@@ -11068,12 +12051,12 @@ var init_ProviderSelector = __esm({
|
|
|
11068
12051
|
onCancel();
|
|
11069
12052
|
}
|
|
11070
12053
|
});
|
|
11071
|
-
return /* @__PURE__ */
|
|
12054
|
+
return /* @__PURE__ */ React28.createElement(Box25, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React28.createElement(Box25, { marginBottom: 1 }, /* @__PURE__ */ React28.createElement(Text23, { bold: true, color: theme.text.accent }, "Select LLM Provider")), /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column" }, availableProviders.map((provider, index) => {
|
|
11072
12055
|
const isSelected = index === selectedIndex;
|
|
11073
12056
|
const isCurrent = provider.id === currentProvider;
|
|
11074
12057
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
11075
|
-
return /* @__PURE__ */
|
|
11076
|
-
|
|
12058
|
+
return /* @__PURE__ */ React28.createElement(
|
|
12059
|
+
Text23,
|
|
11077
12060
|
{
|
|
11078
12061
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
11079
12062
|
bold: isSelected,
|
|
@@ -11082,17 +12065,17 @@ var init_ProviderSelector = __esm({
|
|
|
11082
12065
|
},
|
|
11083
12066
|
indicator,
|
|
11084
12067
|
provider.name,
|
|
11085
|
-
isCurrent && /* @__PURE__ */
|
|
11086
|
-
/* @__PURE__ */
|
|
12068
|
+
isCurrent && /* @__PURE__ */ React28.createElement(Text23, { color: isSelected ? "black" : theme.text.success }, " (current)"),
|
|
12069
|
+
/* @__PURE__ */ React28.createElement(Text23, { color: isSelected ? "black" : theme.text.dim }, " - ", provider.description)
|
|
11087
12070
|
);
|
|
11088
|
-
})), /* @__PURE__ */
|
|
12071
|
+
})), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: theme.text.dim }, /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "Enter"), " select \u2022", " ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "ESC"), " cancel")));
|
|
11089
12072
|
};
|
|
11090
12073
|
}
|
|
11091
12074
|
});
|
|
11092
12075
|
|
|
11093
12076
|
// src/ui/components/SessionSelector.tsx
|
|
11094
|
-
import { Box as
|
|
11095
|
-
import
|
|
12077
|
+
import { Box as Box26, Text as Text24, useInput as useInput10 } from "ink";
|
|
12078
|
+
import React29, { useEffect as useEffect11, useState as useState15 } from "react";
|
|
11096
12079
|
function getSessionPrefix(authMethod) {
|
|
11097
12080
|
return authMethod === "api-key" ? "[Team]" : "[Me]";
|
|
11098
12081
|
}
|
|
@@ -11107,12 +12090,12 @@ var init_SessionSelector = __esm({
|
|
|
11107
12090
|
onSelect,
|
|
11108
12091
|
onCancel
|
|
11109
12092
|
}) => {
|
|
11110
|
-
const [allSessions, setAllSessions] =
|
|
11111
|
-
const [selectedIndex, setSelectedIndex] =
|
|
11112
|
-
const [isLoading, setIsLoading] =
|
|
11113
|
-
const [hasMore, setHasMore] =
|
|
11114
|
-
const [totalSessions, setTotalSessions] =
|
|
11115
|
-
const [error, setError] =
|
|
12093
|
+
const [allSessions, setAllSessions] = useState15([]);
|
|
12094
|
+
const [selectedIndex, setSelectedIndex] = useState15(0);
|
|
12095
|
+
const [isLoading, setIsLoading] = useState15(false);
|
|
12096
|
+
const [hasMore, setHasMore] = useState15(true);
|
|
12097
|
+
const [totalSessions, setTotalSessions] = useState15(0);
|
|
12098
|
+
const [error, setError] = useState15(null);
|
|
11116
12099
|
useEffect11(() => {
|
|
11117
12100
|
loadMoreSessions();
|
|
11118
12101
|
}, []);
|
|
@@ -11139,7 +12122,7 @@ var init_SessionSelector = __esm({
|
|
|
11139
12122
|
setIsLoading(false);
|
|
11140
12123
|
}
|
|
11141
12124
|
};
|
|
11142
|
-
|
|
12125
|
+
useInput10((input, key) => {
|
|
11143
12126
|
if (allSessions.length === 0) {
|
|
11144
12127
|
if (key.escape || input === "q") {
|
|
11145
12128
|
onCancel();
|
|
@@ -11163,13 +12146,13 @@ var init_SessionSelector = __esm({
|
|
|
11163
12146
|
}
|
|
11164
12147
|
});
|
|
11165
12148
|
if (error) {
|
|
11166
|
-
return /* @__PURE__ */
|
|
12149
|
+
return /* @__PURE__ */ React29.createElement(Box26, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "red" }, "Error Loading Sessions"), /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, error), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "ESC"), " to cancel")));
|
|
11167
12150
|
}
|
|
11168
12151
|
if (allSessions.length === 0 && isLoading) {
|
|
11169
|
-
return /* @__PURE__ */
|
|
12152
|
+
return /* @__PURE__ */ React29.createElement(Box26, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "cyan" }, "Loading Sessions..."), /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, "Fetching your sessions from the server"));
|
|
11170
12153
|
}
|
|
11171
12154
|
if (allSessions.length === 0 && !isLoading) {
|
|
11172
|
-
return /* @__PURE__ */
|
|
12155
|
+
return /* @__PURE__ */ React29.createElement(Box26, { borderColor: "yellow", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "yellow" }, "No Sessions Found"), /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, "No previous sessions available. Start a new conversation!"), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "ESC"), " to cancel")));
|
|
11173
12156
|
}
|
|
11174
12157
|
const VISIBLE_ITEMS3 = 10;
|
|
11175
12158
|
let startIndex;
|
|
@@ -11189,7 +12172,7 @@ var init_SessionSelector = __esm({
|
|
|
11189
12172
|
const visibleSessions = allSessions.slice(startIndex, endIndex);
|
|
11190
12173
|
const MAX_TITLE_WIDTH = 50;
|
|
11191
12174
|
const PREFIX_WIDTH = 6;
|
|
11192
|
-
return /* @__PURE__ */
|
|
12175
|
+
return /* @__PURE__ */ React29.createElement(Box26, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React29.createElement(Box26, { marginBottom: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "cyan" }, "Select a Session to Resume")), /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column" }, visibleSessions.map((item, index) => {
|
|
11193
12176
|
const actualIndex = startIndex + index;
|
|
11194
12177
|
const isSelected = actualIndex === selectedIndex;
|
|
11195
12178
|
const title = item.session.title || "Untitled session";
|
|
@@ -11213,8 +12196,8 @@ var init_SessionSelector = __esm({
|
|
|
11213
12196
|
const prefixColor = item.prefix === "[Me]" ? "cyan" : "yellow";
|
|
11214
12197
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
11215
12198
|
const bgColor = isSelected ? theme.text.accent : void 0;
|
|
11216
|
-
return /* @__PURE__ */
|
|
11217
|
-
})), /* @__PURE__ */
|
|
12199
|
+
return /* @__PURE__ */ React29.createElement(Box26, { key: item.session.id, width: "100%" }, /* @__PURE__ */ React29.createElement(Text24, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React29.createElement(Text24, { backgroundColor: bgColor, bold: isSelected, color: prefixColor }, prefix), /* @__PURE__ */ React29.createElement(Text24, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, displayTitle), /* @__PURE__ */ React29.createElement(Text24, { backgroundColor: bgColor, color: theme.text.dim }, "(", dateStr, ")"));
|
|
12200
|
+
})), /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column", marginTop: 1 }, (allSessions.length > VISIBLE_ITEMS3 || totalSessions > allSessions.length) && /* @__PURE__ */ React29.createElement(Box26, { marginBottom: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "yellow" }, "Showing ", startIndex + 1, "-", endIndex, " of ", totalSessions || allSessions.length, " sessions", hasMore && !isLoading && /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React29.createElement(Box26, null, /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, "Use ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2191\u2193"), " to navigate \u2022 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "Enter"), " to select \u2022 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "ESC"), " to cancel")), isLoading && /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "cyan" }, "Loading more sessions..."))));
|
|
11218
12201
|
};
|
|
11219
12202
|
}
|
|
11220
12203
|
});
|
|
@@ -11264,10 +12247,10 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
11264
12247
|
|
|
11265
12248
|
// src/ui/App.tsx
|
|
11266
12249
|
import { execSync as execSync6 } from "child_process";
|
|
11267
|
-
import { homedir as
|
|
11268
|
-
import { Box as
|
|
11269
|
-
import
|
|
11270
|
-
import
|
|
12250
|
+
import { homedir as homedir8 } from "os";
|
|
12251
|
+
import { Box as Box27, Text as Text25, useApp as useApp2, useStdout as useStdout2 } from "ink";
|
|
12252
|
+
import Spinner4 from "ink-spinner";
|
|
12253
|
+
import React30, { useEffect as useEffect13, useRef as useRef5, useState as useState16 } from "react";
|
|
11271
12254
|
var getGitBranch2, getCurrentFolder2, AppContent, App;
|
|
11272
12255
|
var init_App = __esm({
|
|
11273
12256
|
"src/ui/App.tsx"() {
|
|
@@ -11278,6 +12261,7 @@ var init_App = __esm({
|
|
|
11278
12261
|
init_prompts();
|
|
11279
12262
|
init_claude_max();
|
|
11280
12263
|
init_command_discovery();
|
|
12264
|
+
init_mcp_manager();
|
|
11281
12265
|
init_settings_loader();
|
|
11282
12266
|
init_stdio();
|
|
11283
12267
|
init_token_storage();
|
|
@@ -11286,8 +12270,10 @@ var init_App = __esm({
|
|
|
11286
12270
|
init_AuthDialog();
|
|
11287
12271
|
init_FeedbackDialog();
|
|
11288
12272
|
init_FixFlow();
|
|
11289
|
-
init_HelpMenu();
|
|
11290
12273
|
init_InputPrompt();
|
|
12274
|
+
init_McpAddDialog();
|
|
12275
|
+
init_McpServerSelector();
|
|
12276
|
+
init_McpServersDisplay();
|
|
11291
12277
|
init_MessageList();
|
|
11292
12278
|
init_ModelSelector();
|
|
11293
12279
|
init_ProviderSelector();
|
|
@@ -11307,7 +12293,7 @@ var init_App = __esm({
|
|
|
11307
12293
|
};
|
|
11308
12294
|
getCurrentFolder2 = (configCwd) => {
|
|
11309
12295
|
const cwd = configCwd || process.cwd();
|
|
11310
|
-
const home =
|
|
12296
|
+
const home = homedir8();
|
|
11311
12297
|
if (cwd.startsWith(home)) {
|
|
11312
12298
|
return `~${cwd.slice(home.length)}`;
|
|
11313
12299
|
}
|
|
@@ -11318,27 +12304,43 @@ var init_App = __esm({
|
|
|
11318
12304
|
const { stdout } = useStdout2();
|
|
11319
12305
|
const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic, toggleToolGroups, llmProvider, setLlmProvider } = useSession();
|
|
11320
12306
|
useModeToggle();
|
|
11321
|
-
const [terminalWidth, setTerminalWidth] =
|
|
11322
|
-
const [
|
|
11323
|
-
const [
|
|
11324
|
-
const [
|
|
11325
|
-
const [
|
|
11326
|
-
const [
|
|
11327
|
-
const [exitWarning, setExitWarning] = useState14(null);
|
|
12307
|
+
const [terminalWidth, setTerminalWidth] = useState16(process.stdout.columns || 80);
|
|
12308
|
+
const [showInput, setShowInput] = useState16(true);
|
|
12309
|
+
const [gitBranch] = useState16(() => getGitBranch2());
|
|
12310
|
+
const [currentFolder] = useState16(() => getCurrentFolder2(config2.cwd));
|
|
12311
|
+
const [hasInputContent, setHasInputContent] = useState16(false);
|
|
12312
|
+
const [exitWarning, setExitWarning] = useState16(null);
|
|
11328
12313
|
const inputPromptRef = useRef5(null);
|
|
11329
|
-
const [showSessionSelector, setShowSessionSelector] =
|
|
11330
|
-
const [showModelSelector, setShowModelSelector] =
|
|
11331
|
-
const [showProviderSelector, setShowProviderSelector] =
|
|
11332
|
-
const [showFeedbackDialog, setShowFeedbackDialog] =
|
|
11333
|
-
const [isLoadingSession, setIsLoadingSession] =
|
|
11334
|
-
const [showFixFlow, setShowFixFlow] =
|
|
11335
|
-
const [fixRunId, setFixRunId] =
|
|
11336
|
-
const [
|
|
12314
|
+
const [showSessionSelector, setShowSessionSelector] = useState16(false);
|
|
12315
|
+
const [showModelSelector, setShowModelSelector] = useState16(false);
|
|
12316
|
+
const [showProviderSelector, setShowProviderSelector] = useState16(false);
|
|
12317
|
+
const [showFeedbackDialog, setShowFeedbackDialog] = useState16(false);
|
|
12318
|
+
const [isLoadingSession, setIsLoadingSession] = useState16(false);
|
|
12319
|
+
const [showFixFlow, setShowFixFlow] = useState16(false);
|
|
12320
|
+
const [fixRunId, setFixRunId] = useState16(void 0);
|
|
12321
|
+
const [showMcpServers, setShowMcpServers] = useState16(false);
|
|
12322
|
+
const [showMcpAdd, setShowMcpAdd] = useState16(false);
|
|
12323
|
+
const [showMcpSelector, setShowMcpSelector] = useState16(false);
|
|
12324
|
+
const [mcpSelectorAction, setMcpSelectorAction] = useState16(
|
|
12325
|
+
"remove"
|
|
12326
|
+
);
|
|
12327
|
+
const [mcpServers, setMcpServers] = useState16([]);
|
|
12328
|
+
const [authState, setAuthState] = useState16(
|
|
11337
12329
|
() => config2.supatestApiKey ? "authenticated" /* Authenticated */ : "unauthenticated" /* Unauthenticated */
|
|
11338
12330
|
);
|
|
11339
|
-
const [showAuthDialog, setShowAuthDialog] =
|
|
12331
|
+
const [showAuthDialog, setShowAuthDialog] = useState16(false);
|
|
11340
12332
|
const { isOverlayOpen, isCancelSuppressed, markOverlayClosed } = useOverlayEscapeGuard({
|
|
11341
|
-
overlays: [
|
|
12333
|
+
overlays: [
|
|
12334
|
+
showSessionSelector,
|
|
12335
|
+
showAuthDialog,
|
|
12336
|
+
showModelSelector,
|
|
12337
|
+
showProviderSelector,
|
|
12338
|
+
showFeedbackDialog,
|
|
12339
|
+
showFixFlow,
|
|
12340
|
+
showMcpServers,
|
|
12341
|
+
showMcpAdd,
|
|
12342
|
+
showMcpSelector
|
|
12343
|
+
]
|
|
11342
12344
|
});
|
|
11343
12345
|
useEffect13(() => {
|
|
11344
12346
|
if (!config2.supatestApiKey) {
|
|
@@ -11396,7 +12398,7 @@ var init_App = __esm({
|
|
|
11396
12398
|
return;
|
|
11397
12399
|
}
|
|
11398
12400
|
if (command === "/help" || command === "/?") {
|
|
11399
|
-
|
|
12401
|
+
onSubmitTask?.(helpPrompt);
|
|
11400
12402
|
return;
|
|
11401
12403
|
}
|
|
11402
12404
|
if (command === "/login") {
|
|
@@ -11525,6 +12527,10 @@ var init_App = __esm({
|
|
|
11525
12527
|
onSubmitTask?.(discoverPrompt);
|
|
11526
12528
|
return;
|
|
11527
12529
|
}
|
|
12530
|
+
if (command === "/mcp") {
|
|
12531
|
+
setShowMcpServers(true);
|
|
12532
|
+
return;
|
|
12533
|
+
}
|
|
11528
12534
|
const projectDir = config2.cwd || process.cwd();
|
|
11529
12535
|
const spaceIndex = trimmedTask.indexOf(" ");
|
|
11530
12536
|
const commandName = spaceIndex > 0 ? trimmedTask.slice(1, spaceIndex) : trimmedTask.slice(1);
|
|
@@ -11677,9 +12683,112 @@ var init_App = __esm({
|
|
|
11677
12683
|
setIsAgentRunning(true);
|
|
11678
12684
|
onSubmitTask?.(prompt);
|
|
11679
12685
|
};
|
|
11680
|
-
const
|
|
12686
|
+
const handleMcpServersClose = () => {
|
|
11681
12687
|
markOverlayClosed();
|
|
11682
|
-
|
|
12688
|
+
setShowMcpServers(false);
|
|
12689
|
+
};
|
|
12690
|
+
const loadMcpServersForSelector = () => {
|
|
12691
|
+
const servers = Object.values(loadMcpConfig(config2.cwd || process.cwd()));
|
|
12692
|
+
setMcpServers(servers);
|
|
12693
|
+
};
|
|
12694
|
+
const handleMcpAdd = () => {
|
|
12695
|
+
setShowMcpServers(false);
|
|
12696
|
+
setShowMcpAdd(true);
|
|
12697
|
+
};
|
|
12698
|
+
const handleMcpAddConfirm = (name, command, args, env, description, scope) => {
|
|
12699
|
+
const result = addMcpServer(config2.cwd || process.cwd(), name, command, args, env, description, scope);
|
|
12700
|
+
setShowMcpAdd(false);
|
|
12701
|
+
markOverlayClosed();
|
|
12702
|
+
if (result.success) {
|
|
12703
|
+
addMessage({
|
|
12704
|
+
type: "assistant",
|
|
12705
|
+
content: result.message
|
|
12706
|
+
});
|
|
12707
|
+
setShowMcpServers(true);
|
|
12708
|
+
} else {
|
|
12709
|
+
addMessage({
|
|
12710
|
+
type: "error",
|
|
12711
|
+
content: result.message,
|
|
12712
|
+
errorType: "error"
|
|
12713
|
+
});
|
|
12714
|
+
}
|
|
12715
|
+
};
|
|
12716
|
+
const handleMcpAddCancel = () => {
|
|
12717
|
+
setShowMcpAdd(false);
|
|
12718
|
+
markOverlayClosed();
|
|
12719
|
+
setShowMcpServers(true);
|
|
12720
|
+
};
|
|
12721
|
+
const handleMcpRemove = () => {
|
|
12722
|
+
loadMcpServersForSelector();
|
|
12723
|
+
setMcpSelectorAction("remove");
|
|
12724
|
+
setShowMcpServers(false);
|
|
12725
|
+
setShowMcpSelector(true);
|
|
12726
|
+
};
|
|
12727
|
+
const handleMcpTest = () => {
|
|
12728
|
+
loadMcpServersForSelector();
|
|
12729
|
+
setMcpSelectorAction("test");
|
|
12730
|
+
setShowMcpServers(false);
|
|
12731
|
+
setShowMcpSelector(true);
|
|
12732
|
+
};
|
|
12733
|
+
const handleMcpSelectorConfirm = async (serverName) => {
|
|
12734
|
+
const cwd = config2.cwd || process.cwd();
|
|
12735
|
+
const servers = loadMcpConfig(cwd);
|
|
12736
|
+
const server = servers[serverName];
|
|
12737
|
+
if (!server) {
|
|
12738
|
+
addMessage({
|
|
12739
|
+
type: "error",
|
|
12740
|
+
content: `Server "${serverName}" not found`,
|
|
12741
|
+
errorType: "error"
|
|
12742
|
+
});
|
|
12743
|
+
setShowMcpSelector(false);
|
|
12744
|
+
markOverlayClosed();
|
|
12745
|
+
setShowMcpServers(true);
|
|
12746
|
+
return;
|
|
12747
|
+
}
|
|
12748
|
+
setShowMcpSelector(false);
|
|
12749
|
+
markOverlayClosed();
|
|
12750
|
+
if (mcpSelectorAction === "remove") {
|
|
12751
|
+
const result = removeMcpServer(cwd, serverName);
|
|
12752
|
+
addMessage({
|
|
12753
|
+
type: "assistant",
|
|
12754
|
+
content: result.message
|
|
12755
|
+
});
|
|
12756
|
+
} else if (mcpSelectorAction === "test") {
|
|
12757
|
+
addMessage({
|
|
12758
|
+
type: "assistant",
|
|
12759
|
+
content: `Testing connection to "${serverName}"...`
|
|
12760
|
+
});
|
|
12761
|
+
const result = await testMcpConnection(serverName, server);
|
|
12762
|
+
addMessage({
|
|
12763
|
+
type: "assistant",
|
|
12764
|
+
content: result.message
|
|
12765
|
+
});
|
|
12766
|
+
if (result.error) {
|
|
12767
|
+
addMessage({
|
|
12768
|
+
type: "error",
|
|
12769
|
+
content: `Error: ${result.error}`,
|
|
12770
|
+
errorType: "warning"
|
|
12771
|
+
});
|
|
12772
|
+
}
|
|
12773
|
+
} else if (mcpSelectorAction === "enable") {
|
|
12774
|
+
const result = enableMcpServer(cwd, serverName);
|
|
12775
|
+
addMessage({
|
|
12776
|
+
type: "assistant",
|
|
12777
|
+
content: result.message
|
|
12778
|
+
});
|
|
12779
|
+
} else if (mcpSelectorAction === "disable") {
|
|
12780
|
+
const result = disableMcpServer(cwd, serverName);
|
|
12781
|
+
addMessage({
|
|
12782
|
+
type: "assistant",
|
|
12783
|
+
content: result.message
|
|
12784
|
+
});
|
|
12785
|
+
}
|
|
12786
|
+
setShowMcpServers(true);
|
|
12787
|
+
};
|
|
12788
|
+
const handleMcpSelectorCancel = () => {
|
|
12789
|
+
setShowMcpSelector(false);
|
|
12790
|
+
markOverlayClosed();
|
|
12791
|
+
setShowMcpServers(true);
|
|
11683
12792
|
};
|
|
11684
12793
|
const isInitialMount = useRef5(true);
|
|
11685
12794
|
useEffect13(() => {
|
|
@@ -11743,9 +12852,6 @@ var init_App = __esm({
|
|
|
11743
12852
|
exit();
|
|
11744
12853
|
onExit(true);
|
|
11745
12854
|
}
|
|
11746
|
-
if (key.ctrl && key.name === "h" && !showHelp) {
|
|
11747
|
-
setShowHelp(true);
|
|
11748
|
-
}
|
|
11749
12855
|
if (key.ctrl && key.name === "l") {
|
|
11750
12856
|
clearTerminalViewportAndScrollback();
|
|
11751
12857
|
}
|
|
@@ -11763,21 +12869,14 @@ var init_App = __esm({
|
|
|
11763
12869
|
});
|
|
11764
12870
|
}
|
|
11765
12871
|
}, []);
|
|
11766
|
-
return /* @__PURE__ */
|
|
11767
|
-
|
|
12872
|
+
return /* @__PURE__ */ React30.createElement(
|
|
12873
|
+
Box27,
|
|
11768
12874
|
{
|
|
11769
12875
|
flexDirection: "column",
|
|
11770
12876
|
paddingX: 1
|
|
11771
12877
|
},
|
|
11772
|
-
/* @__PURE__ */
|
|
11773
|
-
|
|
11774
|
-
HelpMenu,
|
|
11775
|
-
{
|
|
11776
|
-
isAuthenticated: authState === "authenticated" /* Authenticated */,
|
|
11777
|
-
onClose: handleHelpClose
|
|
11778
|
-
}
|
|
11779
|
-
),
|
|
11780
|
-
showSessionSelector && apiClient && /* @__PURE__ */ React28.createElement(
|
|
12878
|
+
/* @__PURE__ */ React30.createElement(MessageList, { currentFolder, gitBranch, queuedTasks, terminalWidth }),
|
|
12879
|
+
showSessionSelector && apiClient && /* @__PURE__ */ React30.createElement(
|
|
11781
12880
|
SessionSelector,
|
|
11782
12881
|
{
|
|
11783
12882
|
apiClient,
|
|
@@ -11785,8 +12884,8 @@ var init_App = __esm({
|
|
|
11785
12884
|
onSelect: handleSessionSelect
|
|
11786
12885
|
}
|
|
11787
12886
|
),
|
|
11788
|
-
isLoadingSession && /* @__PURE__ */
|
|
11789
|
-
showModelSelector && /* @__PURE__ */
|
|
12887
|
+
isLoadingSession && /* @__PURE__ */ React30.createElement(Box27, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React30.createElement(Box27, { flexDirection: "row" }, /* @__PURE__ */ React30.createElement(Box27, { width: 2 }, /* @__PURE__ */ React30.createElement(Text25, { color: theme.text.accent }, /* @__PURE__ */ React30.createElement(Spinner4, { type: "dots" }))), /* @__PURE__ */ React30.createElement(Text25, { bold: true, color: "cyan" }, "Loading session...")), /* @__PURE__ */ React30.createElement(Text25, { color: theme.text.dim }, "Fetching queries and context")),
|
|
12888
|
+
showModelSelector && /* @__PURE__ */ React30.createElement(
|
|
11790
12889
|
ModelSelector,
|
|
11791
12890
|
{
|
|
11792
12891
|
currentModel: selectedModel,
|
|
@@ -11795,7 +12894,7 @@ var init_App = __esm({
|
|
|
11795
12894
|
onSelect: handleModelSelect
|
|
11796
12895
|
}
|
|
11797
12896
|
),
|
|
11798
|
-
showProviderSelector && /* @__PURE__ */
|
|
12897
|
+
showProviderSelector && /* @__PURE__ */ React30.createElement(
|
|
11799
12898
|
ProviderSelector,
|
|
11800
12899
|
{
|
|
11801
12900
|
claudeMaxAvailable: isClaudeMaxAvailable(),
|
|
@@ -11804,20 +12903,20 @@ var init_App = __esm({
|
|
|
11804
12903
|
onSelect: handleProviderSelect
|
|
11805
12904
|
}
|
|
11806
12905
|
),
|
|
11807
|
-
showAuthDialog && /* @__PURE__ */
|
|
12906
|
+
showAuthDialog && /* @__PURE__ */ React30.createElement(
|
|
11808
12907
|
AuthDialog,
|
|
11809
12908
|
{
|
|
11810
12909
|
onLogin: handleLogin
|
|
11811
12910
|
}
|
|
11812
12911
|
),
|
|
11813
|
-
showFeedbackDialog && /* @__PURE__ */
|
|
12912
|
+
showFeedbackDialog && /* @__PURE__ */ React30.createElement(
|
|
11814
12913
|
FeedbackDialog,
|
|
11815
12914
|
{
|
|
11816
12915
|
onCancel: handleFeedbackCancel,
|
|
11817
12916
|
onSubmit: handleFeedbackSubmit
|
|
11818
12917
|
}
|
|
11819
12918
|
),
|
|
11820
|
-
showFixFlow && apiClient && /* @__PURE__ */
|
|
12919
|
+
showFixFlow && apiClient && /* @__PURE__ */ React30.createElement(
|
|
11821
12920
|
FixFlow,
|
|
11822
12921
|
{
|
|
11823
12922
|
apiClient,
|
|
@@ -11827,14 +12926,33 @@ var init_App = __esm({
|
|
|
11827
12926
|
onStartFix: handleFixStart
|
|
11828
12927
|
}
|
|
11829
12928
|
),
|
|
11830
|
-
|
|
12929
|
+
showMcpServers && /* @__PURE__ */ React30.createElement(
|
|
12930
|
+
McpServersDisplay,
|
|
12931
|
+
{
|
|
12932
|
+
cwd: config2.cwd,
|
|
12933
|
+
onAdd: handleMcpAdd,
|
|
12934
|
+
onClose: handleMcpServersClose,
|
|
12935
|
+
onRemove: handleMcpRemove,
|
|
12936
|
+
onTest: handleMcpTest
|
|
12937
|
+
}
|
|
12938
|
+
),
|
|
12939
|
+
showMcpAdd && /* @__PURE__ */ React30.createElement(McpAddDialog, { onCancel: handleMcpAddCancel, onConfirm: handleMcpAddConfirm }),
|
|
12940
|
+
showMcpSelector && /* @__PURE__ */ React30.createElement(
|
|
12941
|
+
McpServerSelector,
|
|
12942
|
+
{
|
|
12943
|
+
action: mcpSelectorAction,
|
|
12944
|
+
onCancel: handleMcpSelectorCancel,
|
|
12945
|
+
onSelect: handleMcpSelectorConfirm,
|
|
12946
|
+
servers: mcpServers
|
|
12947
|
+
}
|
|
12948
|
+
),
|
|
12949
|
+
/* @__PURE__ */ React30.createElement(Box27, { flexDirection: "column" }, !showAuthDialog && /* @__PURE__ */ React30.createElement(AuthBanner, { authState }), showInput && !showSessionSelector && !showAuthDialog && !showModelSelector && !showProviderSelector && !showFeedbackDialog && !showFixFlow && !showMcpServers && !showMcpAdd && !showMcpSelector && !isLoadingSession && /* @__PURE__ */ React30.createElement(Box27, { flexDirection: "column", marginTop: 0, width: "100%" }, exitWarning && /* @__PURE__ */ React30.createElement(Box27, { marginBottom: 0, paddingX: 1 }, /* @__PURE__ */ React30.createElement(Text25, { color: "yellow" }, exitWarning)), /* @__PURE__ */ React30.createElement(
|
|
11831
12950
|
InputPrompt,
|
|
11832
12951
|
{
|
|
11833
12952
|
currentFolder,
|
|
11834
12953
|
cwd: config2.cwd,
|
|
11835
12954
|
gitBranch,
|
|
11836
12955
|
isClaudeMax: !!config2.oauthToken,
|
|
11837
|
-
onHelpToggle: () => setShowHelp((prev) => !prev),
|
|
11838
12956
|
onInputChange: (val) => setHasInputContent(val.trim().length > 0),
|
|
11839
12957
|
onSubmit: handleSubmitTask,
|
|
11840
12958
|
placeholder: "Enter your task...",
|
|
@@ -11844,7 +12962,7 @@ var init_App = __esm({
|
|
|
11844
12962
|
);
|
|
11845
12963
|
};
|
|
11846
12964
|
App = (props) => {
|
|
11847
|
-
return /* @__PURE__ */
|
|
12965
|
+
return /* @__PURE__ */ React30.createElement(AppContent, { ...props });
|
|
11848
12966
|
};
|
|
11849
12967
|
}
|
|
11850
12968
|
});
|
|
@@ -11880,7 +12998,7 @@ __export(interactive_exports, {
|
|
|
11880
12998
|
runInteractive: () => runInteractive
|
|
11881
12999
|
});
|
|
11882
13000
|
import { render as render2 } from "ink";
|
|
11883
|
-
import
|
|
13001
|
+
import React31, { useEffect as useEffect15, useRef as useRef6 } from "react";
|
|
11884
13002
|
function getToolDescription2(toolName, input) {
|
|
11885
13003
|
switch (toolName) {
|
|
11886
13004
|
case "Read":
|
|
@@ -12013,7 +13131,7 @@ async function runInteractive(config2) {
|
|
|
12013
13131
|
webUrl = session.webUrl;
|
|
12014
13132
|
}
|
|
12015
13133
|
const { unmount, waitUntilExit } = render2(
|
|
12016
|
-
/* @__PURE__ */
|
|
13134
|
+
/* @__PURE__ */ React31.createElement(
|
|
12017
13135
|
InteractiveApp,
|
|
12018
13136
|
{
|
|
12019
13137
|
apiClient,
|
|
@@ -12205,17 +13323,17 @@ var init_interactive = __esm({
|
|
|
12205
13323
|
setIsAgentRunning,
|
|
12206
13324
|
setUsageStats
|
|
12207
13325
|
} = useSession();
|
|
12208
|
-
const [sessionId, setSessionId] =
|
|
12209
|
-
const [currentTask, setCurrentTask] =
|
|
12210
|
-
const [taskId, setTaskId] =
|
|
12211
|
-
const [shouldRunAgent, setShouldRunAgent] =
|
|
12212
|
-
const [taskQueue, setTaskQueue] =
|
|
12213
|
-
const [providerSessionId, setProviderSessionId] =
|
|
12214
|
-
const messageBridgeRef =
|
|
12215
|
-
const lastSubmitRef =
|
|
12216
|
-
const [pendingInjected, setPendingInjected] =
|
|
12217
|
-
const pendingInjectedRef =
|
|
12218
|
-
|
|
13326
|
+
const [sessionId, setSessionId] = React31.useState(initialSessionId);
|
|
13327
|
+
const [currentTask, setCurrentTask] = React31.useState(config2.task);
|
|
13328
|
+
const [taskId, setTaskId] = React31.useState(0);
|
|
13329
|
+
const [shouldRunAgent, setShouldRunAgent] = React31.useState(!!config2.task);
|
|
13330
|
+
const [taskQueue, setTaskQueue] = React31.useState([]);
|
|
13331
|
+
const [providerSessionId, setProviderSessionId] = React31.useState();
|
|
13332
|
+
const messageBridgeRef = React31.useRef(null);
|
|
13333
|
+
const lastSubmitRef = React31.useRef(null);
|
|
13334
|
+
const [pendingInjected, setPendingInjected] = React31.useState([]);
|
|
13335
|
+
const pendingInjectedRef = React31.useRef([]);
|
|
13336
|
+
React31.useEffect(() => {
|
|
12219
13337
|
pendingInjectedRef.current = pendingInjected;
|
|
12220
13338
|
}, [pendingInjected]);
|
|
12221
13339
|
const handleSubmitTask = async (task) => {
|
|
@@ -12278,7 +13396,7 @@ var init_interactive = __esm({
|
|
|
12278
13396
|
if (shouldRunAgent && !messageBridgeRef.current) {
|
|
12279
13397
|
messageBridgeRef.current = new MessageBridge(providerSessionId || "");
|
|
12280
13398
|
}
|
|
12281
|
-
|
|
13399
|
+
React31.useEffect(() => {
|
|
12282
13400
|
if (!shouldRunAgent && taskQueue.length > 0) {
|
|
12283
13401
|
const [nextTask, ...remaining] = taskQueue;
|
|
12284
13402
|
setTaskQueue(remaining);
|
|
@@ -12292,14 +13410,14 @@ var init_interactive = __esm({
|
|
|
12292
13410
|
setShouldRunAgent(true);
|
|
12293
13411
|
}
|
|
12294
13412
|
}, [shouldRunAgent, taskQueue, addMessage, providerSessionId]);
|
|
12295
|
-
const handleClearSession =
|
|
13413
|
+
const handleClearSession = React31.useCallback(() => {
|
|
12296
13414
|
setSessionId(void 0);
|
|
12297
13415
|
setContextSessionId(void 0);
|
|
12298
13416
|
setProviderSessionId(void 0);
|
|
12299
13417
|
setTaskQueue([]);
|
|
12300
13418
|
setPendingInjected([]);
|
|
12301
13419
|
}, [setContextSessionId]);
|
|
12302
|
-
return /* @__PURE__ */
|
|
13420
|
+
return /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement(
|
|
12303
13421
|
App,
|
|
12304
13422
|
{
|
|
12305
13423
|
apiClient,
|
|
@@ -12362,7 +13480,7 @@ var init_interactive = __esm({
|
|
|
12362
13480
|
sessionId,
|
|
12363
13481
|
webUrl
|
|
12364
13482
|
}
|
|
12365
|
-
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */
|
|
13483
|
+
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */ React31.createElement(
|
|
12366
13484
|
AgentRunner,
|
|
12367
13485
|
{
|
|
12368
13486
|
apiClient,
|
|
@@ -12387,7 +13505,7 @@ var init_interactive = __esm({
|
|
|
12387
13505
|
useBracketedPaste();
|
|
12388
13506
|
const settings = loadSupatestSettings(props.config.cwd || process.cwd());
|
|
12389
13507
|
const initialProvider = settings.llmProvider || "supatest-managed";
|
|
12390
|
-
return /* @__PURE__ */
|
|
13508
|
+
return /* @__PURE__ */ React31.createElement(KeypressProvider, null, /* @__PURE__ */ React31.createElement(SessionProvider, { initialLlmProvider: initialProvider, initialModel: props.config.selectedModel }, /* @__PURE__ */ React31.createElement(InteractiveAppContent, { ...props })));
|
|
12391
13509
|
};
|
|
12392
13510
|
}
|
|
12393
13511
|
});
|
|
@@ -12411,7 +13529,7 @@ init_react();
|
|
|
12411
13529
|
init_MessageList();
|
|
12412
13530
|
init_SessionContext();
|
|
12413
13531
|
import { execSync as execSync2 } from "child_process";
|
|
12414
|
-
import { homedir as
|
|
13532
|
+
import { homedir as homedir4 } from "os";
|
|
12415
13533
|
import { Box as Box13, useApp } from "ink";
|
|
12416
13534
|
import React14, { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
12417
13535
|
var getGitBranch = () => {
|
|
@@ -12423,7 +13541,7 @@ var getGitBranch = () => {
|
|
|
12423
13541
|
};
|
|
12424
13542
|
var getCurrentFolder = () => {
|
|
12425
13543
|
const cwd = process.cwd();
|
|
12426
|
-
const home =
|
|
13544
|
+
const home = homedir4();
|
|
12427
13545
|
if (cwd.startsWith(home)) {
|
|
12428
13546
|
return `~${cwd.slice(home.length)}`;
|
|
12429
13547
|
}
|
|
@@ -12867,7 +13985,7 @@ program.name("supatest").description(
|
|
|
12867
13985
|
if (options.verbose) {
|
|
12868
13986
|
logger.setVerbose(true);
|
|
12869
13987
|
}
|
|
12870
|
-
const isDev = process.env.NODE_ENV === "development";
|
|
13988
|
+
const isDev = process.env.NODE_ENV === "development" || process.env.SUPATEST_DEV === "true";
|
|
12871
13989
|
logger.enableFileLogging(isDev);
|
|
12872
13990
|
let prompt = task;
|
|
12873
13991
|
let logs;
|