Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
start:
./bin/explorbot-cli.ts start

init:
./bin/explorbot-cli.ts init

63 changes: 48 additions & 15 deletions bin/explorbot-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ addCommonOptions(program.command('plan <path> [feature]').description('Generate
}
}

await explorBot.plan(feature || undefined, { fresh: !options.append, style: options.style });
await explorBot.plan(feature || undefined, {
fresh: !options.append,
style: options.style,
});

const plan = explorBot.getCurrentPlan();
if (!plan?.tests.length) {
Expand Down Expand Up @@ -320,7 +323,13 @@ program
log(`Working in directory: ${resolvedPath}`);
}

const defaultConfig = `import { <your provider here> } from 'ai';
const defaultConfig = `import { '<your provider here>' } from '<your provider package here>';

// This example uses OpenRouter (one API key, many providers). Any Vercel AI SDK provider works; see
// https://github.com/testomatio/explorbot/blob/main/docs/providers.md
const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
});

const config = {
playwright: {
Expand All @@ -330,9 +339,9 @@ const config = {
},

ai: {
provider: <your provider here>,
model: '<your model here>',
apiKey: '<your api key here>',
visionModel: '<your vision model here>',
agenticModel: '<your agentic model here>',
},

reporter: {
Expand Down Expand Up @@ -470,7 +479,9 @@ program
.option('-p, --path <path>', 'Working directory path')
.action(async (url, description, options) => {
try {
await ConfigParser.getInstance().loadConfig({ path: options.path || process.cwd() });
await ConfigParser.getInstance().loadConfig({
path: options.path || process.cwd(),
});

if (url && description) {
const { KnowledgeTracker } = await import('../src/knowledge-tracker.js');
Expand Down Expand Up @@ -498,7 +509,9 @@ program
.option('-p, --path <path>', 'Working directory path')
.action(async (url, options) => {
try {
await ConfigParser.getInstance().loadConfig({ path: options.path || process.cwd() });
await ConfigParser.getInstance().loadConfig({
path: options.path || process.cwd(),
});
const { KnowsCommand } = await import('../src/commands/knows-command.js');
const explorBot = new ExplorBot({ path: options.path });
const command = new KnowsCommand(explorBot);
Expand Down Expand Up @@ -648,14 +661,20 @@ browserCmd
.option('-p, --path <path>', 'Working directory path')
.action(async (options) => {
const { launchServer, removeEndpointFile } = await import('../src/browser-server.js');
await ConfigParser.getInstance().loadConfig({ config: options.config, path: options.path });
await ConfigParser.getInstance().loadConfig({
config: options.config,
path: options.path,
});
const config = ConfigParser.getInstance().getConfig();

let show = config.playwright.show || false;
if (options.show !== undefined) show = true;
if (options.headless !== undefined) show = false;

const server = await launchServer({ browser: config.playwright.browser, show });
const server = await launchServer({
browser: config.playwright.browser,
show,
});

console.log('Browser server is running. Press Ctrl+C to stop.');

Expand All @@ -677,7 +696,10 @@ browserCmd
.option('-p, --path <path>', 'Working directory path')
.action(async (options) => {
const { getAliveEndpoint, removeEndpointFile } = await import('../src/browser-server.js');
await ConfigParser.getInstance().loadConfig({ config: options.config, path: options.path });
await ConfigParser.getInstance().loadConfig({
config: options.config,
path: options.path,
});

const endpoint = await getAliveEndpoint();
if (!endpoint) {
Expand All @@ -702,7 +724,10 @@ browserCmd
.option('-p, --path <path>', 'Working directory path')
.action(async (options) => {
const { getAliveEndpoint } = await import('../src/browser-server.js');
await ConfigParser.getInstance().loadConfig({ config: options.config, path: options.path });
await ConfigParser.getInstance().loadConfig({
config: options.config,
path: options.path,
});

const endpoint = await getAliveEndpoint();
if (endpoint) {
Expand Down Expand Up @@ -743,15 +768,23 @@ program

if (agent && name) {
const { AddRuleCommand } = await import('../src/commands/add-rule-command.js');
const result = AddRuleCommand.createRuleFile(agent, name, { urlPattern: options.url });
const result = AddRuleCommand.createRuleFile(agent, name, {
urlPattern: options.url,
});
process.exit(result ? 0 : 1);
}

const AddRule = (await import('../src/components/AddRule.js')).default;
render(React.createElement(AddRule, { initialAgent: agent || '', initialName: name || '' }), {
exitOnCtrlC: false,
patchConsole: false,
});
render(
React.createElement(AddRule, {
initialAgent: agent || '',
initialName: name || '',
}),
{
exitOnCtrlC: false,
patchConsole: false,
}
);
});

import { createApiCommands } from '../boat/api-tester/src/cli.ts';
Expand Down
12 changes: 8 additions & 4 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 22 additions & 3 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@ flowchart LR
**Purpose:** Handles all browser interactions — clicks, form fills, navigation.

**What it does:**

- Executes CodeceptJS commands in the browser
- Tries multiple locator strategies when selectors fail
- Automatically resolves failed interactions without stopping
- Remembers what worked (and what didn't) for next time

**Why you'll love it:**

- No more `ElementNotFound` exceptions killing your test runs
- Self-healing when your UI changes
- Learns optimal selectors for your specific app

**Commands that use Navigator:**

- `/navigate <target>`
- `I.click()`, `I.fillField()`, `I.amOnPage()`, etc.

Expand All @@ -38,19 +41,22 @@ flowchart LR
**Purpose:** Analyzes pages to understand what's actually there.

**What it does:**

- Discovers all interactive UI elements
- Expands hidden content (accordions, dropdowns, modals)
- Maps navigation paths and form structures
- Extracts structured data from tables and lists
- Filters out irrelevant elements (cookie banners, ads)

**Why you'll love it:**

- Discovers UI elements you forgot existed
- Gives you a complete picture of what's testable
- Documents forms with all their validation rules
- Configurable filtering to focus on what matters

**Commands that use Researcher:**

- `explorbot research /path` (CLI)
- `/research [path]` (TUI)
- `/research --deep` — expand hidden elements
Expand All @@ -63,6 +69,7 @@ See [Researcher Agent](./researcher.md) for detailed configuration and usage.
**Purpose:** Generates test scenarios from research findings.

**What it does:**

- Creates business-focused test scenarios
- Assigns priority levels (critical/important/high/normal/low)
- Generates expected outcomes for verification
Expand All @@ -71,12 +78,14 @@ See [Researcher Agent](./researcher.md) for detailed configuration and usage.
- Cycles through planning styles (normal, psycho, curious) for comprehensive coverage

**Why you'll love it:**

- Creates tests that matter, not just "click stuff"
- Prioritizes by risk (critical flows first)
- Different styles ensure broad coverage over multiple iterations
- Fully customizable — add your own styles and page-specific rules

**Commands that use Planner:**

- `/plan [feature]`
- `/explore`

Expand All @@ -87,18 +96,21 @@ See [Planner Agent](./planner.md) for detailed documentation on planning styles,
**Purpose:** Executes the planned scenarios.

**What it does:**

- Runs test scenarios step by step
- Adapts when things don't go as expected
- Tracks state changes during execution
- Documents actual results vs. expected
- Uses research context for smart decisions

**Why you'll love it:**

- Handles unexpected modals and popups
- Recovers from minor failures automatically
- Produces detailed execution logs

**Commands that use Tester:**

- `/test [scenario]`
- `/explore`

Expand All @@ -107,31 +119,37 @@ See [Planner Agent](./planner.md) for detailed documentation on planning styles,
**Purpose:** Supervises Tester and intervenes when tests get stuck.

**What it does:**

- Maintains separate conversation to track test progress over time
- Detects stuck patterns (loops, repeated failures, no page changes)
- Decides what context Tester needs (HTML, ARIA, UI map)
- Asks user for help when automated recovery fails

**Why you'll love it:**

- Catches when Tester is spinning wheels on the same failure
- Requests user input before giving up on a test
- Can use smarter models without token cost explosion (only sees tool summaries, not raw HTML)

**When Pilot intervenes:**

- Actions succeed but page doesn't change (wrong element)
- Same action repeated multiple times (loop)
- Same locator keeps failing (need alternative approach)
- Only research/context calls, no action tools (not progressing)

## Captain Agent *(coming soon)*
## Captain Agent

**Purpose:** Orchestrates the whole testing session.
**Purpose:** Orchestrates the whole testing session and handles user commands in TUI.

**What it does:**
- Coordinates all agents intelligently

- Responds to user commands in real-time
- Adjusts strategy based on discoveries
- Manages conversation context efficiently
- Runs in idle mode with access to diagnostic tools
- Inspects test sessions (logs, tool calls, ARIA states, pilot analysis)
- Reads/writes files, evaluates browser JS, manages tabs

## Per-Agent Model Configuration

Expand All @@ -157,6 +175,7 @@ export default {
```

**Typical optimization:**

- Navigator needs fast responses for real-time interaction
- Researcher benefits from vision capabilities
- Planner can use a slightly larger model for better test design
Expand Down
Loading
Loading