@supatest/cli 0.0.35 → 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 +1345 -211
- 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
|
});
|
|
@@ -610,14 +1117,30 @@ function getToolDisplayName(toolName) {
|
|
|
610
1117
|
}
|
|
611
1118
|
function getToolCategory(toolName) {
|
|
612
1119
|
const name = toolName.toLowerCase();
|
|
613
|
-
if (name.includes("read"))
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
if (name.includes("
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
if (name.includes("
|
|
620
|
-
|
|
1120
|
+
if (name.includes("read")) {
|
|
1121
|
+
return "Read";
|
|
1122
|
+
}
|
|
1123
|
+
if (name.includes("write")) {
|
|
1124
|
+
return "Write";
|
|
1125
|
+
}
|
|
1126
|
+
if (name.includes("edit")) {
|
|
1127
|
+
return "Edit";
|
|
1128
|
+
}
|
|
1129
|
+
if (name.includes("bash") || name.includes("command")) {
|
|
1130
|
+
return "Command";
|
|
1131
|
+
}
|
|
1132
|
+
if (name.includes("glob")) {
|
|
1133
|
+
return "Glob";
|
|
1134
|
+
}
|
|
1135
|
+
if (name.includes("grep")) {
|
|
1136
|
+
return "Grep";
|
|
1137
|
+
}
|
|
1138
|
+
if (name.includes("task")) {
|
|
1139
|
+
return "Task";
|
|
1140
|
+
}
|
|
1141
|
+
if (name.includes("todo")) {
|
|
1142
|
+
return "Todo";
|
|
1143
|
+
}
|
|
621
1144
|
return getToolDisplayName(toolName);
|
|
622
1145
|
}
|
|
623
1146
|
function getToolGroupCounts(tools) {
|
|
@@ -5368,7 +5891,7 @@ var CLI_VERSION;
|
|
|
5368
5891
|
var init_version = __esm({
|
|
5369
5892
|
"src/version.ts"() {
|
|
5370
5893
|
"use strict";
|
|
5371
|
-
CLI_VERSION = "0.0.
|
|
5894
|
+
CLI_VERSION = "0.0.37";
|
|
5372
5895
|
}
|
|
5373
5896
|
});
|
|
5374
5897
|
|
|
@@ -6224,6 +6747,7 @@ var init_command_discovery = __esm({
|
|
|
6224
6747
|
|
|
6225
6748
|
// src/utils/mcp-loader.ts
|
|
6226
6749
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
6750
|
+
import { homedir as homedir2 } from "os";
|
|
6227
6751
|
import { join as join4 } from "path";
|
|
6228
6752
|
function expandEnvVar(value) {
|
|
6229
6753
|
return value.replace(/\$\{([^}]+)\}/g, (_, expr) => {
|
|
@@ -6246,8 +6770,7 @@ function expandServerConfig(config2) {
|
|
|
6246
6770
|
}
|
|
6247
6771
|
return expanded;
|
|
6248
6772
|
}
|
|
6249
|
-
function
|
|
6250
|
-
const mcpPath = join4(cwd, ".supatest", "mcp.json");
|
|
6773
|
+
function loadMcpServersFromFile(mcpPath) {
|
|
6251
6774
|
if (!existsSync3(mcpPath)) {
|
|
6252
6775
|
return {};
|
|
6253
6776
|
}
|
|
@@ -6259,6 +6782,9 @@ function loadMcpServers(cwd) {
|
|
|
6259
6782
|
}
|
|
6260
6783
|
const expanded = {};
|
|
6261
6784
|
for (const [name, serverConfig] of Object.entries(config2.mcpServers)) {
|
|
6785
|
+
if (serverConfig.enabled === false) {
|
|
6786
|
+
continue;
|
|
6787
|
+
}
|
|
6262
6788
|
expanded[name] = expandServerConfig(serverConfig);
|
|
6263
6789
|
}
|
|
6264
6790
|
return expanded;
|
|
@@ -6270,6 +6796,13 @@ function loadMcpServers(cwd) {
|
|
|
6270
6796
|
return {};
|
|
6271
6797
|
}
|
|
6272
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
|
+
}
|
|
6273
6806
|
var init_mcp_loader = __esm({
|
|
6274
6807
|
"src/utils/mcp-loader.ts"() {
|
|
6275
6808
|
"use strict";
|
|
@@ -6302,7 +6835,7 @@ var init_project_instructions = __esm({
|
|
|
6302
6835
|
|
|
6303
6836
|
// src/core/agent.ts
|
|
6304
6837
|
import { createRequire } from "module";
|
|
6305
|
-
import { homedir as
|
|
6838
|
+
import { homedir as homedir3 } from "os";
|
|
6306
6839
|
import { dirname, join as join6 } from "path";
|
|
6307
6840
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
6308
6841
|
var CoreAgent;
|
|
@@ -6403,7 +6936,7 @@ ${projectInstructions}`,
|
|
|
6403
6936
|
this.presenter.onLog(`Auth: Using Claude Max (default Claude Code credentials)`);
|
|
6404
6937
|
logger.debug("[agent] Claude Max mode: Using default ~/.claude/ config, cleared provider credentials");
|
|
6405
6938
|
} else {
|
|
6406
|
-
const internalConfigDir = join6(
|
|
6939
|
+
const internalConfigDir = join6(homedir3(), ".supatest", "claude-internal");
|
|
6407
6940
|
cleanEnv.CLAUDE_CONFIG_DIR = internalConfigDir;
|
|
6408
6941
|
cleanEnv.ANTHROPIC_API_KEY = config2.supatestApiKey || "";
|
|
6409
6942
|
cleanEnv.ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || "";
|
|
@@ -6434,9 +6967,17 @@ ${projectInstructions}`,
|
|
|
6434
6967
|
// MCP servers from .supatest/mcp.json
|
|
6435
6968
|
// Users can add servers like Playwright if needed
|
|
6436
6969
|
mcpServers: (() => {
|
|
6970
|
+
logger.debug("[agent] Loading MCP servers for query", { cwd });
|
|
6437
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
|
+
});
|
|
6438
6977
|
if (Object.keys(servers).length > 0) {
|
|
6439
6978
|
this.presenter.onLog(`MCP servers: ${Object.keys(servers).join(", ")}`);
|
|
6979
|
+
} else {
|
|
6980
|
+
logger.debug("[agent] No MCP servers configured");
|
|
6440
6981
|
}
|
|
6441
6982
|
return servers;
|
|
6442
6983
|
})(),
|
|
@@ -6496,7 +7037,14 @@ ${projectInstructions}`,
|
|
|
6496
7037
|
maxTurns: options.maxTurns,
|
|
6497
7038
|
permissionMode: options.permissionMode,
|
|
6498
7039
|
hasResume: !!options.resume,
|
|
6499
|
-
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 || {})
|
|
6500
7048
|
});
|
|
6501
7049
|
const queryIterator = query({ prompt, options });
|
|
6502
7050
|
if (this.messageBridge) {
|
|
@@ -8259,7 +8807,7 @@ var init_MessageList = __esm({
|
|
|
8259
8807
|
return { ...group.messages[0], _isMessage: true };
|
|
8260
8808
|
})
|
|
8261
8809
|
], [completedGroups]);
|
|
8262
|
-
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: staticRemountKey }, (item) => {
|
|
8810
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: `${staticRemountKey}-${toolGroupsExpanded ? "expanded" : "collapsed"}` }, (item) => {
|
|
8263
8811
|
if (item.type === "header") {
|
|
8264
8812
|
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: "header" }, /* @__PURE__ */ React13.createElement(Header, { currentFolder, gitBranch, headless }));
|
|
8265
8813
|
}
|
|
@@ -8281,7 +8829,7 @@ var init_MessageList = __esm({
|
|
|
8281
8829
|
return null;
|
|
8282
8830
|
}
|
|
8283
8831
|
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: group.type === "group" ? `current-group-${idx}` : group.messages[0].id, width: "100%" }, content);
|
|
8284
|
-
}), /* @__PURE__ */ React13.createElement(QueuedMessageDisplay, { messageQueue: queuedTasks }), isAgentRunning && !hasPendingAssistant && /* @__PURE__ */ React13.createElement(LoadingMessage, { headless, key: "loading" }));
|
|
8832
|
+
}), /* @__PURE__ */ React13.createElement(QueuedMessageDisplay, { messageQueue: queuedTasks }), isAgentRunning && !hasPendingAssistant && /* @__PURE__ */ React13.createElement(LoadingMessage, { headless, key: "loading" }), /* @__PURE__ */ React13.createElement(Box12, { height: 1 }));
|
|
8285
8833
|
};
|
|
8286
8834
|
}
|
|
8287
8835
|
});
|
|
@@ -8409,7 +8957,7 @@ var init_encryption = __esm({
|
|
|
8409
8957
|
|
|
8410
8958
|
// src/utils/token-storage.ts
|
|
8411
8959
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
8412
|
-
import { homedir as
|
|
8960
|
+
import { homedir as homedir5 } from "os";
|
|
8413
8961
|
import { join as join7 } from "path";
|
|
8414
8962
|
function getTokenFilePath() {
|
|
8415
8963
|
const apiUrl = process.env.SUPATEST_API_URL || PRODUCTION_API_URL;
|
|
@@ -8484,7 +9032,7 @@ var init_token_storage = __esm({
|
|
|
8484
9032
|
"src/utils/token-storage.ts"() {
|
|
8485
9033
|
"use strict";
|
|
8486
9034
|
init_encryption();
|
|
8487
|
-
CONFIG_DIR = join7(
|
|
9035
|
+
CONFIG_DIR = join7(homedir5(), ".supatest");
|
|
8488
9036
|
PRODUCTION_API_URL = "https://code-api.supatest.ai";
|
|
8489
9037
|
STORAGE_VERSION = 2;
|
|
8490
9038
|
TOKEN_FILE = join7(CONFIG_DIR, "token.json");
|
|
@@ -8609,18 +9157,22 @@ async function exchangeCodeForToken(code, state) {
|
|
|
8609
9157
|
function openBrowser(url) {
|
|
8610
9158
|
const os3 = platform();
|
|
8611
9159
|
let command;
|
|
9160
|
+
let args;
|
|
8612
9161
|
switch (os3) {
|
|
8613
9162
|
case "darwin":
|
|
8614
9163
|
command = "open";
|
|
9164
|
+
args = [url];
|
|
8615
9165
|
break;
|
|
8616
9166
|
case "win32":
|
|
8617
9167
|
command = "start";
|
|
9168
|
+
args = ["", url];
|
|
8618
9169
|
break;
|
|
8619
9170
|
default:
|
|
8620
9171
|
command = "xdg-open";
|
|
9172
|
+
args = [url];
|
|
8621
9173
|
}
|
|
8622
9174
|
const options = { detached: true, stdio: "ignore", shell: os3 === "win32" };
|
|
8623
|
-
spawn2(command,
|
|
9175
|
+
spawn2(command, args, options).unref();
|
|
8624
9176
|
}
|
|
8625
9177
|
function startCallbackServer(port, expectedState) {
|
|
8626
9178
|
return new Promise((resolve2, reject) => {
|
|
@@ -8959,7 +9511,7 @@ var init_login = __esm({
|
|
|
8959
9511
|
// src/utils/claude-max.ts
|
|
8960
9512
|
import { execSync as execSync5 } from "child_process";
|
|
8961
9513
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
8962
|
-
import { homedir as
|
|
9514
|
+
import { homedir as homedir6 } from "os";
|
|
8963
9515
|
import { join as join8 } from "path";
|
|
8964
9516
|
function isClaudeMaxAvailable() {
|
|
8965
9517
|
const platform2 = process.platform;
|
|
@@ -8996,7 +9548,7 @@ function checkMacOSKeychain() {
|
|
|
8996
9548
|
}
|
|
8997
9549
|
function checkCredentialsFile() {
|
|
8998
9550
|
try {
|
|
8999
|
-
const credentialsPath = join8(
|
|
9551
|
+
const credentialsPath = join8(homedir6(), ".claude", ".credentials.json");
|
|
9000
9552
|
if (!existsSync6(credentialsPath)) {
|
|
9001
9553
|
logger.debug("[claude-max] Credentials file not found", { path: credentialsPath });
|
|
9002
9554
|
return false;
|
|
@@ -9024,16 +9576,248 @@ var init_claude_max = __esm({
|
|
|
9024
9576
|
}
|
|
9025
9577
|
});
|
|
9026
9578
|
|
|
9027
|
-
// src/utils/
|
|
9579
|
+
// src/utils/mcp-manager.ts
|
|
9028
9580
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
9581
|
+
import { homedir as homedir7 } from "os";
|
|
9029
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";
|
|
9030
9814
|
function loadSupatestSettings(cwd) {
|
|
9031
|
-
const settingsPath =
|
|
9032
|
-
if (!
|
|
9815
|
+
const settingsPath = join10(cwd, ".supatest", "settings.json");
|
|
9816
|
+
if (!existsSync8(settingsPath)) {
|
|
9033
9817
|
return {};
|
|
9034
9818
|
}
|
|
9035
9819
|
try {
|
|
9036
|
-
const content =
|
|
9820
|
+
const content = readFileSync7(settingsPath, "utf-8");
|
|
9037
9821
|
return JSON.parse(content);
|
|
9038
9822
|
} catch (error) {
|
|
9039
9823
|
console.warn(
|
|
@@ -9044,11 +9828,11 @@ function loadSupatestSettings(cwd) {
|
|
|
9044
9828
|
}
|
|
9045
9829
|
}
|
|
9046
9830
|
function saveSupatestSettings(cwd, settings) {
|
|
9047
|
-
const settingsDir =
|
|
9048
|
-
const settingsPath =
|
|
9831
|
+
const settingsDir = join10(cwd, ".supatest");
|
|
9832
|
+
const settingsPath = join10(settingsDir, "settings.json");
|
|
9049
9833
|
try {
|
|
9050
|
-
if (!
|
|
9051
|
-
|
|
9834
|
+
if (!existsSync8(settingsDir)) {
|
|
9835
|
+
mkdirSync4(settingsDir, { recursive: true });
|
|
9052
9836
|
}
|
|
9053
9837
|
const existingSettings = loadSupatestSettings(cwd);
|
|
9054
9838
|
const mergedSettings = {
|
|
@@ -9064,7 +9848,7 @@ function saveSupatestSettings(cwd, settings) {
|
|
|
9064
9848
|
...settings.hooks
|
|
9065
9849
|
}
|
|
9066
9850
|
};
|
|
9067
|
-
|
|
9851
|
+
writeFileSync3(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
|
|
9068
9852
|
} catch (error) {
|
|
9069
9853
|
console.warn(
|
|
9070
9854
|
`Warning: Failed to save settings to ${settingsPath}:`,
|
|
@@ -10481,56 +11265,6 @@ var init_FixFlow = __esm({
|
|
|
10481
11265
|
}
|
|
10482
11266
|
});
|
|
10483
11267
|
|
|
10484
|
-
// src/ui/components/HelpMenu.tsx
|
|
10485
|
-
import { Box as Box20, Text as Text18, useInput as useInput5 } from "ink";
|
|
10486
|
-
import React23, { useEffect as useEffect9, useState as useState9 } from "react";
|
|
10487
|
-
var HelpMenu;
|
|
10488
|
-
var init_HelpMenu = __esm({
|
|
10489
|
-
"src/ui/components/HelpMenu.tsx"() {
|
|
10490
|
-
"use strict";
|
|
10491
|
-
init_command_discovery();
|
|
10492
|
-
init_theme();
|
|
10493
|
-
HelpMenu = ({ isAuthenticated, onClose, cwd }) => {
|
|
10494
|
-
const [customCommands, setCustomCommands] = useState9([]);
|
|
10495
|
-
useEffect9(() => {
|
|
10496
|
-
const projectDir = cwd || process.cwd();
|
|
10497
|
-
const commands = discoverCommands(projectDir);
|
|
10498
|
-
setCustomCommands(commands);
|
|
10499
|
-
}, [cwd]);
|
|
10500
|
-
useInput5((input, key) => {
|
|
10501
|
-
if (key.escape || input === "q" || input === "?" || key.ctrl && input === "h") {
|
|
10502
|
-
onClose();
|
|
10503
|
-
}
|
|
10504
|
-
});
|
|
10505
|
-
return /* @__PURE__ */ React23.createElement(
|
|
10506
|
-
Box20,
|
|
10507
|
-
{
|
|
10508
|
-
borderColor: theme.border.accent,
|
|
10509
|
-
borderStyle: "round",
|
|
10510
|
-
flexDirection: "column",
|
|
10511
|
-
paddingX: 2,
|
|
10512
|
-
paddingY: 1
|
|
10513
|
-
},
|
|
10514
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.accent }, "\u{1F4D6} Supatest AI CLI - Help"),
|
|
10515
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10516
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Slash Commands:"),
|
|
10517
|
-
/* @__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"))),
|
|
10518
|
-
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)"))),
|
|
10519
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10520
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Keyboard Shortcuts:"),
|
|
10521
|
-
/* @__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"))),
|
|
10522
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10523
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "File References:"),
|
|
10524
|
-
/* @__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"')),
|
|
10525
|
-
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10526
|
-
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Tips:"),
|
|
10527
|
-
/* @__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")),
|
|
10528
|
-
/* @__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"))
|
|
10529
|
-
);
|
|
10530
|
-
};
|
|
10531
|
-
}
|
|
10532
|
-
});
|
|
10533
|
-
|
|
10534
11268
|
// src/ui/utils/file-completion.ts
|
|
10535
11269
|
import fs4 from "fs";
|
|
10536
11270
|
import path4 from "path";
|
|
@@ -10614,8 +11348,8 @@ var init_file_completion = __esm({
|
|
|
10614
11348
|
});
|
|
10615
11349
|
|
|
10616
11350
|
// src/ui/components/ModelSelector.tsx
|
|
10617
|
-
import { Box as
|
|
10618
|
-
import
|
|
11351
|
+
import { Box as Box20, Text as Text18, useInput as useInput5 } from "ink";
|
|
11352
|
+
import React23, { useState as useState9 } from "react";
|
|
10619
11353
|
function getAvailableModels(isClaudeMax) {
|
|
10620
11354
|
if (isClaudeMax) {
|
|
10621
11355
|
return AVAILABLE_MODELS.map((m) => ({
|
|
@@ -10650,8 +11384,8 @@ var init_ModelSelector = __esm({
|
|
|
10650
11384
|
}) => {
|
|
10651
11385
|
const models = getAvailableModels(isClaudeMax);
|
|
10652
11386
|
const currentIndex = models.findIndex((m) => m.id === currentModel);
|
|
10653
|
-
const [selectedIndex, setSelectedIndex] =
|
|
10654
|
-
|
|
11387
|
+
const [selectedIndex, setSelectedIndex] = useState9(currentIndex >= 0 ? currentIndex : 0);
|
|
11388
|
+
useInput5((input, key) => {
|
|
10655
11389
|
if (key.upArrow) {
|
|
10656
11390
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : models.length - 1);
|
|
10657
11391
|
} else if (key.downArrow) {
|
|
@@ -10662,12 +11396,12 @@ var init_ModelSelector = __esm({
|
|
|
10662
11396
|
onCancel();
|
|
10663
11397
|
}
|
|
10664
11398
|
});
|
|
10665
|
-
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) => {
|
|
10666
11400
|
const isSelected = index === selectedIndex;
|
|
10667
11401
|
const isCurrent = model.id === currentModel;
|
|
10668
11402
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
10669
|
-
return /* @__PURE__ */
|
|
10670
|
-
|
|
11403
|
+
return /* @__PURE__ */ React23.createElement(Box20, { gap: 1, key: model.id }, /* @__PURE__ */ React23.createElement(
|
|
11404
|
+
Text18,
|
|
10671
11405
|
{
|
|
10672
11406
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
10673
11407
|
bold: isSelected,
|
|
@@ -10675,15 +11409,15 @@ var init_ModelSelector = __esm({
|
|
|
10675
11409
|
},
|
|
10676
11410
|
indicator,
|
|
10677
11411
|
model.name.padEnd(12)
|
|
10678
|
-
), /* @__PURE__ */
|
|
10679
|
-
|
|
11412
|
+
), /* @__PURE__ */ React23.createElement(
|
|
11413
|
+
Text18,
|
|
10680
11414
|
{
|
|
10681
11415
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
10682
11416
|
color: isSelected ? "black" : theme.text.dim
|
|
10683
11417
|
},
|
|
10684
11418
|
model.description
|
|
10685
|
-
), isCurrent && /* @__PURE__ */
|
|
10686
|
-
})), /* @__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")));
|
|
10687
11421
|
};
|
|
10688
11422
|
}
|
|
10689
11423
|
});
|
|
@@ -10691,8 +11425,8 @@ var init_ModelSelector = __esm({
|
|
|
10691
11425
|
// src/ui/components/InputPrompt.tsx
|
|
10692
11426
|
import path5 from "path";
|
|
10693
11427
|
import chalk4 from "chalk";
|
|
10694
|
-
import { Box as
|
|
10695
|
-
import
|
|
11428
|
+
import { Box as Box21, Text as Text19 } from "ink";
|
|
11429
|
+
import React24, { forwardRef, useEffect as useEffect9, useImperativeHandle, useState as useState10 } from "react";
|
|
10696
11430
|
var InputPrompt;
|
|
10697
11431
|
var init_InputPrompt = __esm({
|
|
10698
11432
|
"src/ui/components/InputPrompt.tsx"() {
|
|
@@ -10716,13 +11450,13 @@ var init_InputPrompt = __esm({
|
|
|
10716
11450
|
isClaudeMax = false
|
|
10717
11451
|
}, ref) => {
|
|
10718
11452
|
const { messages, agentMode, selectedModel, setSelectedModel, isAgentRunning, usageStats } = useSession();
|
|
10719
|
-
const [value, setValue] =
|
|
10720
|
-
const [cursorOffset, setCursorOffset] =
|
|
10721
|
-
const [allFiles, setAllFiles] =
|
|
10722
|
-
const [suggestions, setSuggestions] =
|
|
10723
|
-
const [activeSuggestion, setActiveSuggestion] =
|
|
10724
|
-
const [showSuggestions, setShowSuggestions] =
|
|
10725
|
-
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);
|
|
10726
11460
|
const BUILTIN_SLASH_COMMANDS = [
|
|
10727
11461
|
{ name: "/help", desc: "Show help" },
|
|
10728
11462
|
{ name: "/resume", desc: "Resume session" },
|
|
@@ -10733,13 +11467,14 @@ var init_InputPrompt = __esm({
|
|
|
10733
11467
|
{ name: "/feedback", desc: "Report an issue" },
|
|
10734
11468
|
{ name: "/setup", desc: "Install Playwright browsers" },
|
|
10735
11469
|
{ name: "/discover", desc: "Discover test framework" },
|
|
11470
|
+
{ name: "/mcp", desc: "Show MCP servers" },
|
|
10736
11471
|
{ name: "/login", desc: "Authenticate with Supatest" },
|
|
10737
11472
|
{ name: "/logout", desc: "Log out" },
|
|
10738
11473
|
{ name: "/exit", desc: "Exit CLI" }
|
|
10739
11474
|
];
|
|
10740
|
-
const [customCommands, setCustomCommands] =
|
|
10741
|
-
const [isSlashCommand, setIsSlashCommand] =
|
|
10742
|
-
|
|
11475
|
+
const [customCommands, setCustomCommands] = useState10([]);
|
|
11476
|
+
const [isSlashCommand, setIsSlashCommand] = useState10(false);
|
|
11477
|
+
useEffect9(() => {
|
|
10743
11478
|
try {
|
|
10744
11479
|
const projectDir = cwd || process.cwd();
|
|
10745
11480
|
const discovered = discoverCommands(projectDir);
|
|
@@ -10760,7 +11495,7 @@ var init_InputPrompt = __esm({
|
|
|
10760
11495
|
onInputChange?.("");
|
|
10761
11496
|
}
|
|
10762
11497
|
}));
|
|
10763
|
-
|
|
11498
|
+
useEffect9(() => {
|
|
10764
11499
|
setTimeout(() => {
|
|
10765
11500
|
try {
|
|
10766
11501
|
const files = getFiles();
|
|
@@ -10874,8 +11609,8 @@ var init_InputPrompt = __esm({
|
|
|
10874
11609
|
setSelectedModel(getNextModel(selectedModel, isClaudeMax));
|
|
10875
11610
|
return;
|
|
10876
11611
|
}
|
|
10877
|
-
if (input === "?" && value.length === 0
|
|
10878
|
-
|
|
11612
|
+
if (input === "?" && value.length === 0) {
|
|
11613
|
+
onSubmit("/help");
|
|
10879
11614
|
return;
|
|
10880
11615
|
}
|
|
10881
11616
|
if (showSuggestions && !key.shift) {
|
|
@@ -10960,8 +11695,8 @@ var init_InputPrompt = __esm({
|
|
|
10960
11695
|
}
|
|
10961
11696
|
charCount += lineLength + 1;
|
|
10962
11697
|
}
|
|
10963
|
-
return /* @__PURE__ */
|
|
10964
|
-
|
|
11698
|
+
return /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column", width: "100%" }, showSuggestions && /* @__PURE__ */ React24.createElement(
|
|
11699
|
+
Box21,
|
|
10965
11700
|
{
|
|
10966
11701
|
borderColor: theme.border.default,
|
|
10967
11702
|
borderStyle: "round",
|
|
@@ -10972,12 +11707,12 @@ var init_InputPrompt = __esm({
|
|
|
10972
11707
|
suggestions.map((item, idx) => {
|
|
10973
11708
|
const isSeparator = item.startsWith("\u2500\u2500\u2500\u2500\u2500");
|
|
10974
11709
|
if (isSeparator) {
|
|
10975
|
-
return /* @__PURE__ */
|
|
11710
|
+
return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim, key: item }, " ", item);
|
|
10976
11711
|
}
|
|
10977
|
-
return /* @__PURE__ */
|
|
11712
|
+
return /* @__PURE__ */ React24.createElement(Text19, { color: idx === activeSuggestion ? theme.text.accent : theme.text.dim, key: item }, idx === activeSuggestion ? "\u276F " : " ", item);
|
|
10978
11713
|
})
|
|
10979
|
-
), /* @__PURE__ */
|
|
10980
|
-
|
|
11714
|
+
), /* @__PURE__ */ React24.createElement(
|
|
11715
|
+
Box21,
|
|
10981
11716
|
{
|
|
10982
11717
|
borderColor: disabled ? theme.border.default : theme.border.accent,
|
|
10983
11718
|
borderStyle: "round",
|
|
@@ -10987,24 +11722,288 @@ var init_InputPrompt = __esm({
|
|
|
10987
11722
|
paddingX: 1,
|
|
10988
11723
|
width: "100%"
|
|
10989
11724
|
},
|
|
10990
|
-
/* @__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) => {
|
|
10991
11726
|
if (idx === cursorLine && !disabled) {
|
|
10992
11727
|
const before = line.slice(0, cursorCol);
|
|
10993
11728
|
const charAtCursor = line[cursorCol] || " ";
|
|
10994
11729
|
const after = line.slice(cursorCol + 1);
|
|
10995
|
-
return /* @__PURE__ */
|
|
11730
|
+
return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.primary, key: idx }, before, chalk4.inverse(charAtCursor), after);
|
|
10996
11731
|
}
|
|
10997
|
-
return /* @__PURE__ */
|
|
10998
|
-
})), !hasContent && disabled && /* @__PURE__ */
|
|
10999
|
-
), /* @__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", ")"))));
|
|
11000
11735
|
});
|
|
11001
11736
|
InputPrompt.displayName = "InputPrompt";
|
|
11002
11737
|
}
|
|
11003
11738
|
});
|
|
11004
11739
|
|
|
11005
|
-
// 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
|
|
11006
11848
|
import { Box as Box23, Text as Text21, useInput as useInput7 } from "ink";
|
|
11007
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";
|
|
11008
12007
|
var PROVIDERS, ProviderSelector;
|
|
11009
12008
|
var init_ProviderSelector = __esm({
|
|
11010
12009
|
"src/ui/components/ProviderSelector.tsx"() {
|
|
@@ -11034,10 +12033,10 @@ var init_ProviderSelector = __esm({
|
|
|
11034
12033
|
const currentIndex = availableProviders.findIndex(
|
|
11035
12034
|
(p) => p.id === currentProvider
|
|
11036
12035
|
);
|
|
11037
|
-
const [selectedIndex, setSelectedIndex] =
|
|
12036
|
+
const [selectedIndex, setSelectedIndex] = useState14(
|
|
11038
12037
|
currentIndex >= 0 ? currentIndex : 0
|
|
11039
12038
|
);
|
|
11040
|
-
|
|
12039
|
+
useInput9((input, key) => {
|
|
11041
12040
|
if (key.upArrow) {
|
|
11042
12041
|
setSelectedIndex(
|
|
11043
12042
|
(prev) => prev > 0 ? prev - 1 : availableProviders.length - 1
|
|
@@ -11052,12 +12051,12 @@ var init_ProviderSelector = __esm({
|
|
|
11052
12051
|
onCancel();
|
|
11053
12052
|
}
|
|
11054
12053
|
});
|
|
11055
|
-
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) => {
|
|
11056
12055
|
const isSelected = index === selectedIndex;
|
|
11057
12056
|
const isCurrent = provider.id === currentProvider;
|
|
11058
12057
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
11059
|
-
return /* @__PURE__ */
|
|
11060
|
-
|
|
12058
|
+
return /* @__PURE__ */ React28.createElement(
|
|
12059
|
+
Text23,
|
|
11061
12060
|
{
|
|
11062
12061
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
11063
12062
|
bold: isSelected,
|
|
@@ -11066,17 +12065,17 @@ var init_ProviderSelector = __esm({
|
|
|
11066
12065
|
},
|
|
11067
12066
|
indicator,
|
|
11068
12067
|
provider.name,
|
|
11069
|
-
isCurrent && /* @__PURE__ */
|
|
11070
|
-
/* @__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)
|
|
11071
12070
|
);
|
|
11072
|
-
})), /* @__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")));
|
|
11073
12072
|
};
|
|
11074
12073
|
}
|
|
11075
12074
|
});
|
|
11076
12075
|
|
|
11077
12076
|
// src/ui/components/SessionSelector.tsx
|
|
11078
|
-
import { Box as
|
|
11079
|
-
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";
|
|
11080
12079
|
function getSessionPrefix(authMethod) {
|
|
11081
12080
|
return authMethod === "api-key" ? "[Team]" : "[Me]";
|
|
11082
12081
|
}
|
|
@@ -11091,12 +12090,12 @@ var init_SessionSelector = __esm({
|
|
|
11091
12090
|
onSelect,
|
|
11092
12091
|
onCancel
|
|
11093
12092
|
}) => {
|
|
11094
|
-
const [allSessions, setAllSessions] =
|
|
11095
|
-
const [selectedIndex, setSelectedIndex] =
|
|
11096
|
-
const [isLoading, setIsLoading] =
|
|
11097
|
-
const [hasMore, setHasMore] =
|
|
11098
|
-
const [totalSessions, setTotalSessions] =
|
|
11099
|
-
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);
|
|
11100
12099
|
useEffect11(() => {
|
|
11101
12100
|
loadMoreSessions();
|
|
11102
12101
|
}, []);
|
|
@@ -11123,7 +12122,7 @@ var init_SessionSelector = __esm({
|
|
|
11123
12122
|
setIsLoading(false);
|
|
11124
12123
|
}
|
|
11125
12124
|
};
|
|
11126
|
-
|
|
12125
|
+
useInput10((input, key) => {
|
|
11127
12126
|
if (allSessions.length === 0) {
|
|
11128
12127
|
if (key.escape || input === "q") {
|
|
11129
12128
|
onCancel();
|
|
@@ -11147,13 +12146,13 @@ var init_SessionSelector = __esm({
|
|
|
11147
12146
|
}
|
|
11148
12147
|
});
|
|
11149
12148
|
if (error) {
|
|
11150
|
-
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")));
|
|
11151
12150
|
}
|
|
11152
12151
|
if (allSessions.length === 0 && isLoading) {
|
|
11153
|
-
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"));
|
|
11154
12153
|
}
|
|
11155
12154
|
if (allSessions.length === 0 && !isLoading) {
|
|
11156
|
-
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")));
|
|
11157
12156
|
}
|
|
11158
12157
|
const VISIBLE_ITEMS3 = 10;
|
|
11159
12158
|
let startIndex;
|
|
@@ -11173,7 +12172,7 @@ var init_SessionSelector = __esm({
|
|
|
11173
12172
|
const visibleSessions = allSessions.slice(startIndex, endIndex);
|
|
11174
12173
|
const MAX_TITLE_WIDTH = 50;
|
|
11175
12174
|
const PREFIX_WIDTH = 6;
|
|
11176
|
-
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) => {
|
|
11177
12176
|
const actualIndex = startIndex + index;
|
|
11178
12177
|
const isSelected = actualIndex === selectedIndex;
|
|
11179
12178
|
const title = item.session.title || "Untitled session";
|
|
@@ -11197,8 +12196,8 @@ var init_SessionSelector = __esm({
|
|
|
11197
12196
|
const prefixColor = item.prefix === "[Me]" ? "cyan" : "yellow";
|
|
11198
12197
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
11199
12198
|
const bgColor = isSelected ? theme.text.accent : void 0;
|
|
11200
|
-
return /* @__PURE__ */
|
|
11201
|
-
})), /* @__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..."))));
|
|
11202
12201
|
};
|
|
11203
12202
|
}
|
|
11204
12203
|
});
|
|
@@ -11248,10 +12247,10 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
11248
12247
|
|
|
11249
12248
|
// src/ui/App.tsx
|
|
11250
12249
|
import { execSync as execSync6 } from "child_process";
|
|
11251
|
-
import { homedir as
|
|
11252
|
-
import { Box as
|
|
11253
|
-
import
|
|
11254
|
-
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";
|
|
11255
12254
|
var getGitBranch2, getCurrentFolder2, AppContent, App;
|
|
11256
12255
|
var init_App = __esm({
|
|
11257
12256
|
"src/ui/App.tsx"() {
|
|
@@ -11262,6 +12261,7 @@ var init_App = __esm({
|
|
|
11262
12261
|
init_prompts();
|
|
11263
12262
|
init_claude_max();
|
|
11264
12263
|
init_command_discovery();
|
|
12264
|
+
init_mcp_manager();
|
|
11265
12265
|
init_settings_loader();
|
|
11266
12266
|
init_stdio();
|
|
11267
12267
|
init_token_storage();
|
|
@@ -11270,8 +12270,10 @@ var init_App = __esm({
|
|
|
11270
12270
|
init_AuthDialog();
|
|
11271
12271
|
init_FeedbackDialog();
|
|
11272
12272
|
init_FixFlow();
|
|
11273
|
-
init_HelpMenu();
|
|
11274
12273
|
init_InputPrompt();
|
|
12274
|
+
init_McpAddDialog();
|
|
12275
|
+
init_McpServerSelector();
|
|
12276
|
+
init_McpServersDisplay();
|
|
11275
12277
|
init_MessageList();
|
|
11276
12278
|
init_ModelSelector();
|
|
11277
12279
|
init_ProviderSelector();
|
|
@@ -11291,7 +12293,7 @@ var init_App = __esm({
|
|
|
11291
12293
|
};
|
|
11292
12294
|
getCurrentFolder2 = (configCwd) => {
|
|
11293
12295
|
const cwd = configCwd || process.cwd();
|
|
11294
|
-
const home =
|
|
12296
|
+
const home = homedir8();
|
|
11295
12297
|
if (cwd.startsWith(home)) {
|
|
11296
12298
|
return `~${cwd.slice(home.length)}`;
|
|
11297
12299
|
}
|
|
@@ -11302,27 +12304,43 @@ var init_App = __esm({
|
|
|
11302
12304
|
const { stdout } = useStdout2();
|
|
11303
12305
|
const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic, toggleToolGroups, llmProvider, setLlmProvider } = useSession();
|
|
11304
12306
|
useModeToggle();
|
|
11305
|
-
const [terminalWidth, setTerminalWidth] =
|
|
11306
|
-
const [
|
|
11307
|
-
const [
|
|
11308
|
-
const [
|
|
11309
|
-
const [
|
|
11310
|
-
const [
|
|
11311
|
-
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);
|
|
11312
12313
|
const inputPromptRef = useRef5(null);
|
|
11313
|
-
const [showSessionSelector, setShowSessionSelector] =
|
|
11314
|
-
const [showModelSelector, setShowModelSelector] =
|
|
11315
|
-
const [showProviderSelector, setShowProviderSelector] =
|
|
11316
|
-
const [showFeedbackDialog, setShowFeedbackDialog] =
|
|
11317
|
-
const [isLoadingSession, setIsLoadingSession] =
|
|
11318
|
-
const [showFixFlow, setShowFixFlow] =
|
|
11319
|
-
const [fixRunId, setFixRunId] =
|
|
11320
|
-
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(
|
|
11321
12329
|
() => config2.supatestApiKey ? "authenticated" /* Authenticated */ : "unauthenticated" /* Unauthenticated */
|
|
11322
12330
|
);
|
|
11323
|
-
const [showAuthDialog, setShowAuthDialog] =
|
|
12331
|
+
const [showAuthDialog, setShowAuthDialog] = useState16(false);
|
|
11324
12332
|
const { isOverlayOpen, isCancelSuppressed, markOverlayClosed } = useOverlayEscapeGuard({
|
|
11325
|
-
overlays: [
|
|
12333
|
+
overlays: [
|
|
12334
|
+
showSessionSelector,
|
|
12335
|
+
showAuthDialog,
|
|
12336
|
+
showModelSelector,
|
|
12337
|
+
showProviderSelector,
|
|
12338
|
+
showFeedbackDialog,
|
|
12339
|
+
showFixFlow,
|
|
12340
|
+
showMcpServers,
|
|
12341
|
+
showMcpAdd,
|
|
12342
|
+
showMcpSelector
|
|
12343
|
+
]
|
|
11326
12344
|
});
|
|
11327
12345
|
useEffect13(() => {
|
|
11328
12346
|
if (!config2.supatestApiKey) {
|
|
@@ -11380,7 +12398,7 @@ var init_App = __esm({
|
|
|
11380
12398
|
return;
|
|
11381
12399
|
}
|
|
11382
12400
|
if (command === "/help" || command === "/?") {
|
|
11383
|
-
|
|
12401
|
+
onSubmitTask?.(helpPrompt);
|
|
11384
12402
|
return;
|
|
11385
12403
|
}
|
|
11386
12404
|
if (command === "/login") {
|
|
@@ -11509,6 +12527,10 @@ var init_App = __esm({
|
|
|
11509
12527
|
onSubmitTask?.(discoverPrompt);
|
|
11510
12528
|
return;
|
|
11511
12529
|
}
|
|
12530
|
+
if (command === "/mcp") {
|
|
12531
|
+
setShowMcpServers(true);
|
|
12532
|
+
return;
|
|
12533
|
+
}
|
|
11512
12534
|
const projectDir = config2.cwd || process.cwd();
|
|
11513
12535
|
const spaceIndex = trimmedTask.indexOf(" ");
|
|
11514
12536
|
const commandName = spaceIndex > 0 ? trimmedTask.slice(1, spaceIndex) : trimmedTask.slice(1);
|
|
@@ -11661,9 +12683,112 @@ var init_App = __esm({
|
|
|
11661
12683
|
setIsAgentRunning(true);
|
|
11662
12684
|
onSubmitTask?.(prompt);
|
|
11663
12685
|
};
|
|
11664
|
-
const
|
|
12686
|
+
const handleMcpServersClose = () => {
|
|
11665
12687
|
markOverlayClosed();
|
|
11666
|
-
|
|
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);
|
|
11667
12792
|
};
|
|
11668
12793
|
const isInitialMount = useRef5(true);
|
|
11669
12794
|
useEffect13(() => {
|
|
@@ -11727,9 +12852,6 @@ var init_App = __esm({
|
|
|
11727
12852
|
exit();
|
|
11728
12853
|
onExit(true);
|
|
11729
12854
|
}
|
|
11730
|
-
if (key.ctrl && key.name === "h" && !showHelp) {
|
|
11731
|
-
setShowHelp(true);
|
|
11732
|
-
}
|
|
11733
12855
|
if (key.ctrl && key.name === "l") {
|
|
11734
12856
|
clearTerminalViewportAndScrollback();
|
|
11735
12857
|
}
|
|
@@ -11747,21 +12869,14 @@ var init_App = __esm({
|
|
|
11747
12869
|
});
|
|
11748
12870
|
}
|
|
11749
12871
|
}, []);
|
|
11750
|
-
return /* @__PURE__ */
|
|
11751
|
-
|
|
12872
|
+
return /* @__PURE__ */ React30.createElement(
|
|
12873
|
+
Box27,
|
|
11752
12874
|
{
|
|
11753
12875
|
flexDirection: "column",
|
|
11754
12876
|
paddingX: 1
|
|
11755
12877
|
},
|
|
11756
|
-
/* @__PURE__ */
|
|
11757
|
-
|
|
11758
|
-
HelpMenu,
|
|
11759
|
-
{
|
|
11760
|
-
isAuthenticated: authState === "authenticated" /* Authenticated */,
|
|
11761
|
-
onClose: handleHelpClose
|
|
11762
|
-
}
|
|
11763
|
-
),
|
|
11764
|
-
showSessionSelector && apiClient && /* @__PURE__ */ React28.createElement(
|
|
12878
|
+
/* @__PURE__ */ React30.createElement(MessageList, { currentFolder, gitBranch, queuedTasks, terminalWidth }),
|
|
12879
|
+
showSessionSelector && apiClient && /* @__PURE__ */ React30.createElement(
|
|
11765
12880
|
SessionSelector,
|
|
11766
12881
|
{
|
|
11767
12882
|
apiClient,
|
|
@@ -11769,8 +12884,8 @@ var init_App = __esm({
|
|
|
11769
12884
|
onSelect: handleSessionSelect
|
|
11770
12885
|
}
|
|
11771
12886
|
),
|
|
11772
|
-
isLoadingSession && /* @__PURE__ */
|
|
11773
|
-
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(
|
|
11774
12889
|
ModelSelector,
|
|
11775
12890
|
{
|
|
11776
12891
|
currentModel: selectedModel,
|
|
@@ -11779,7 +12894,7 @@ var init_App = __esm({
|
|
|
11779
12894
|
onSelect: handleModelSelect
|
|
11780
12895
|
}
|
|
11781
12896
|
),
|
|
11782
|
-
showProviderSelector && /* @__PURE__ */
|
|
12897
|
+
showProviderSelector && /* @__PURE__ */ React30.createElement(
|
|
11783
12898
|
ProviderSelector,
|
|
11784
12899
|
{
|
|
11785
12900
|
claudeMaxAvailable: isClaudeMaxAvailable(),
|
|
@@ -11788,20 +12903,20 @@ var init_App = __esm({
|
|
|
11788
12903
|
onSelect: handleProviderSelect
|
|
11789
12904
|
}
|
|
11790
12905
|
),
|
|
11791
|
-
showAuthDialog && /* @__PURE__ */
|
|
12906
|
+
showAuthDialog && /* @__PURE__ */ React30.createElement(
|
|
11792
12907
|
AuthDialog,
|
|
11793
12908
|
{
|
|
11794
12909
|
onLogin: handleLogin
|
|
11795
12910
|
}
|
|
11796
12911
|
),
|
|
11797
|
-
showFeedbackDialog && /* @__PURE__ */
|
|
12912
|
+
showFeedbackDialog && /* @__PURE__ */ React30.createElement(
|
|
11798
12913
|
FeedbackDialog,
|
|
11799
12914
|
{
|
|
11800
12915
|
onCancel: handleFeedbackCancel,
|
|
11801
12916
|
onSubmit: handleFeedbackSubmit
|
|
11802
12917
|
}
|
|
11803
12918
|
),
|
|
11804
|
-
showFixFlow && apiClient && /* @__PURE__ */
|
|
12919
|
+
showFixFlow && apiClient && /* @__PURE__ */ React30.createElement(
|
|
11805
12920
|
FixFlow,
|
|
11806
12921
|
{
|
|
11807
12922
|
apiClient,
|
|
@@ -11811,14 +12926,33 @@ var init_App = __esm({
|
|
|
11811
12926
|
onStartFix: handleFixStart
|
|
11812
12927
|
}
|
|
11813
12928
|
),
|
|
11814
|
-
|
|
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(
|
|
11815
12950
|
InputPrompt,
|
|
11816
12951
|
{
|
|
11817
12952
|
currentFolder,
|
|
11818
12953
|
cwd: config2.cwd,
|
|
11819
12954
|
gitBranch,
|
|
11820
12955
|
isClaudeMax: !!config2.oauthToken,
|
|
11821
|
-
onHelpToggle: () => setShowHelp((prev) => !prev),
|
|
11822
12956
|
onInputChange: (val) => setHasInputContent(val.trim().length > 0),
|
|
11823
12957
|
onSubmit: handleSubmitTask,
|
|
11824
12958
|
placeholder: "Enter your task...",
|
|
@@ -11828,7 +12962,7 @@ var init_App = __esm({
|
|
|
11828
12962
|
);
|
|
11829
12963
|
};
|
|
11830
12964
|
App = (props) => {
|
|
11831
|
-
return /* @__PURE__ */
|
|
12965
|
+
return /* @__PURE__ */ React30.createElement(AppContent, { ...props });
|
|
11832
12966
|
};
|
|
11833
12967
|
}
|
|
11834
12968
|
});
|
|
@@ -11864,7 +12998,7 @@ __export(interactive_exports, {
|
|
|
11864
12998
|
runInteractive: () => runInteractive
|
|
11865
12999
|
});
|
|
11866
13000
|
import { render as render2 } from "ink";
|
|
11867
|
-
import
|
|
13001
|
+
import React31, { useEffect as useEffect15, useRef as useRef6 } from "react";
|
|
11868
13002
|
function getToolDescription2(toolName, input) {
|
|
11869
13003
|
switch (toolName) {
|
|
11870
13004
|
case "Read":
|
|
@@ -11997,7 +13131,7 @@ async function runInteractive(config2) {
|
|
|
11997
13131
|
webUrl = session.webUrl;
|
|
11998
13132
|
}
|
|
11999
13133
|
const { unmount, waitUntilExit } = render2(
|
|
12000
|
-
/* @__PURE__ */
|
|
13134
|
+
/* @__PURE__ */ React31.createElement(
|
|
12001
13135
|
InteractiveApp,
|
|
12002
13136
|
{
|
|
12003
13137
|
apiClient,
|
|
@@ -12189,17 +13323,17 @@ var init_interactive = __esm({
|
|
|
12189
13323
|
setIsAgentRunning,
|
|
12190
13324
|
setUsageStats
|
|
12191
13325
|
} = useSession();
|
|
12192
|
-
const [sessionId, setSessionId] =
|
|
12193
|
-
const [currentTask, setCurrentTask] =
|
|
12194
|
-
const [taskId, setTaskId] =
|
|
12195
|
-
const [shouldRunAgent, setShouldRunAgent] =
|
|
12196
|
-
const [taskQueue, setTaskQueue] =
|
|
12197
|
-
const [providerSessionId, setProviderSessionId] =
|
|
12198
|
-
const messageBridgeRef =
|
|
12199
|
-
const lastSubmitRef =
|
|
12200
|
-
const [pendingInjected, setPendingInjected] =
|
|
12201
|
-
const pendingInjectedRef =
|
|
12202
|
-
|
|
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(() => {
|
|
12203
13337
|
pendingInjectedRef.current = pendingInjected;
|
|
12204
13338
|
}, [pendingInjected]);
|
|
12205
13339
|
const handleSubmitTask = async (task) => {
|
|
@@ -12262,7 +13396,7 @@ var init_interactive = __esm({
|
|
|
12262
13396
|
if (shouldRunAgent && !messageBridgeRef.current) {
|
|
12263
13397
|
messageBridgeRef.current = new MessageBridge(providerSessionId || "");
|
|
12264
13398
|
}
|
|
12265
|
-
|
|
13399
|
+
React31.useEffect(() => {
|
|
12266
13400
|
if (!shouldRunAgent && taskQueue.length > 0) {
|
|
12267
13401
|
const [nextTask, ...remaining] = taskQueue;
|
|
12268
13402
|
setTaskQueue(remaining);
|
|
@@ -12276,14 +13410,14 @@ var init_interactive = __esm({
|
|
|
12276
13410
|
setShouldRunAgent(true);
|
|
12277
13411
|
}
|
|
12278
13412
|
}, [shouldRunAgent, taskQueue, addMessage, providerSessionId]);
|
|
12279
|
-
const handleClearSession =
|
|
13413
|
+
const handleClearSession = React31.useCallback(() => {
|
|
12280
13414
|
setSessionId(void 0);
|
|
12281
13415
|
setContextSessionId(void 0);
|
|
12282
13416
|
setProviderSessionId(void 0);
|
|
12283
13417
|
setTaskQueue([]);
|
|
12284
13418
|
setPendingInjected([]);
|
|
12285
13419
|
}, [setContextSessionId]);
|
|
12286
|
-
return /* @__PURE__ */
|
|
13420
|
+
return /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement(
|
|
12287
13421
|
App,
|
|
12288
13422
|
{
|
|
12289
13423
|
apiClient,
|
|
@@ -12346,7 +13480,7 @@ var init_interactive = __esm({
|
|
|
12346
13480
|
sessionId,
|
|
12347
13481
|
webUrl
|
|
12348
13482
|
}
|
|
12349
|
-
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */
|
|
13483
|
+
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */ React31.createElement(
|
|
12350
13484
|
AgentRunner,
|
|
12351
13485
|
{
|
|
12352
13486
|
apiClient,
|
|
@@ -12371,7 +13505,7 @@ var init_interactive = __esm({
|
|
|
12371
13505
|
useBracketedPaste();
|
|
12372
13506
|
const settings = loadSupatestSettings(props.config.cwd || process.cwd());
|
|
12373
13507
|
const initialProvider = settings.llmProvider || "supatest-managed";
|
|
12374
|
-
return /* @__PURE__ */
|
|
13508
|
+
return /* @__PURE__ */ React31.createElement(KeypressProvider, null, /* @__PURE__ */ React31.createElement(SessionProvider, { initialLlmProvider: initialProvider, initialModel: props.config.selectedModel }, /* @__PURE__ */ React31.createElement(InteractiveAppContent, { ...props })));
|
|
12375
13509
|
};
|
|
12376
13510
|
}
|
|
12377
13511
|
});
|
|
@@ -12395,7 +13529,7 @@ init_react();
|
|
|
12395
13529
|
init_MessageList();
|
|
12396
13530
|
init_SessionContext();
|
|
12397
13531
|
import { execSync as execSync2 } from "child_process";
|
|
12398
|
-
import { homedir as
|
|
13532
|
+
import { homedir as homedir4 } from "os";
|
|
12399
13533
|
import { Box as Box13, useApp } from "ink";
|
|
12400
13534
|
import React14, { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
12401
13535
|
var getGitBranch = () => {
|
|
@@ -12407,7 +13541,7 @@ var getGitBranch = () => {
|
|
|
12407
13541
|
};
|
|
12408
13542
|
var getCurrentFolder = () => {
|
|
12409
13543
|
const cwd = process.cwd();
|
|
12410
|
-
const home =
|
|
13544
|
+
const home = homedir4();
|
|
12411
13545
|
if (cwd.startsWith(home)) {
|
|
12412
13546
|
return `~${cwd.slice(home.length)}`;
|
|
12413
13547
|
}
|
|
@@ -12851,7 +13985,7 @@ program.name("supatest").description(
|
|
|
12851
13985
|
if (options.verbose) {
|
|
12852
13986
|
logger.setVerbose(true);
|
|
12853
13987
|
}
|
|
12854
|
-
const isDev = process.env.NODE_ENV === "development";
|
|
13988
|
+
const isDev = process.env.NODE_ENV === "development" || process.env.SUPATEST_DEV === "true";
|
|
12855
13989
|
logger.enableFileLogging(isDev);
|
|
12856
13990
|
let prompt = task;
|
|
12857
13991
|
let logs;
|