mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
feat: add --prompt and --prompt-file options to agent send command
This commit is contained in:
@@ -133,7 +133,9 @@ export function createCli(): Command {
|
||||
.command('send')
|
||||
.description('Send a message/task to an existing agent')
|
||||
.argument('<id>', 'Agent ID (or prefix)')
|
||||
.argument('<prompt>', 'The message to send')
|
||||
.argument('[prompt]', 'The message to send')
|
||||
.option('--prompt <text>', 'Provide the message inline as a flag')
|
||||
.option('--prompt-file <path>', 'Read the message from a UTF-8 text file')
|
||||
.option('--no-wait', 'Return immediately without waiting for completion')
|
||||
.option('--image <path>', 'Attach image(s) to the message', collectMultiple, [])
|
||||
).action(withOutput(runSendCommand))
|
||||
|
||||
@@ -88,7 +88,9 @@ export function createAgentCommand(): Command {
|
||||
.command('send')
|
||||
.description('Send a message/task to an existing agent')
|
||||
.argument('<id>', 'Agent ID (or prefix)')
|
||||
.argument('<prompt>', 'The message to send')
|
||||
.argument('[prompt]', 'The message to send')
|
||||
.option('--prompt <text>', 'Provide the message inline as a flag')
|
||||
.option('--prompt-file <path>', 'Read the message from a UTF-8 text file')
|
||||
.option('--no-wait', 'Return immediately without waiting for completion')
|
||||
).action(withOutput(runSendCommand))
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Command } from 'commander'
|
||||
import { connectToDaemon, getDaemonHost } from '../../utils/client.js'
|
||||
import type { CommandOptions, SingleResult, OutputSchema, CommandError } from '../../output/index.js'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { extname } from 'node:path'
|
||||
import { extname, resolve } from 'node:path'
|
||||
|
||||
/** Result type for agent send command */
|
||||
export interface AgentSendResult {
|
||||
@@ -24,6 +24,8 @@ export const agentSendSchema: OutputSchema<AgentSendResult> = {
|
||||
export interface AgentSendOptions extends CommandOptions {
|
||||
noWait?: boolean
|
||||
image?: string[]
|
||||
prompt?: string
|
||||
promptFile?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +79,57 @@ async function readImageFiles(imagePaths: string[]): Promise<Array<{ data: strin
|
||||
return images
|
||||
}
|
||||
|
||||
async function resolvePromptInput(options: {
|
||||
promptArgument: string | undefined
|
||||
promptOption: string | undefined
|
||||
promptFile: string | undefined
|
||||
}): Promise<string> {
|
||||
const promptText = options.promptArgument?.trim()
|
||||
const promptOptionText = options.promptOption?.trim()
|
||||
const promptFilePath = options.promptFile?.trim()
|
||||
const providedSourceCount = [promptText, promptOptionText, promptFilePath].filter(Boolean).length
|
||||
|
||||
if (providedSourceCount > 1) {
|
||||
const error: CommandError = {
|
||||
code: 'CONFLICTING_PROMPT_INPUT',
|
||||
message: 'Provide exactly one of prompt argument, --prompt, or --prompt-file',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
if (promptText) {
|
||||
return options.promptArgument as string
|
||||
}
|
||||
|
||||
if (promptOptionText) {
|
||||
return options.promptOption as string
|
||||
}
|
||||
|
||||
if (!promptFilePath) {
|
||||
const error: CommandError = {
|
||||
code: 'MISSING_PROMPT',
|
||||
message: 'A prompt is required',
|
||||
details: 'Usage: paseo agent send [options] <id> [prompt] | --prompt <text> | --prompt-file <path>',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
try {
|
||||
return await readFile(resolve(promptFilePath), 'utf8')
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
const error: CommandError = {
|
||||
code: 'PROMPT_FILE_READ_ERROR',
|
||||
message: `Failed to read prompt file: ${promptFilePath}`,
|
||||
details: message,
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function runSendCommand(
|
||||
agentIdArg: string,
|
||||
prompt: string,
|
||||
prompt: string | undefined,
|
||||
options: AgentSendOptions,
|
||||
_command: Command
|
||||
): Promise<SingleResult<AgentSendResult>> {
|
||||
@@ -90,19 +140,16 @@ export async function runSendCommand(
|
||||
const error: CommandError = {
|
||||
code: 'MISSING_AGENT_ID',
|
||||
message: 'Agent ID is required',
|
||||
details: 'Usage: paseo agent send [options] <id> <prompt>',
|
||||
details: 'Usage: paseo agent send [options] <id> [prompt]',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
if (!prompt || prompt.trim().length === 0) {
|
||||
const error: CommandError = {
|
||||
code: 'MISSING_PROMPT',
|
||||
message: 'A prompt is required',
|
||||
details: 'Usage: paseo agent send [options] <id> <prompt>',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
const promptInput = await resolvePromptInput({
|
||||
promptArgument: prompt,
|
||||
promptOption: options.prompt,
|
||||
promptFile: options.promptFile,
|
||||
})
|
||||
|
||||
let client
|
||||
try {
|
||||
@@ -124,7 +171,7 @@ export async function runSendCommand(
|
||||
: undefined
|
||||
|
||||
// Send the message
|
||||
await client.sendAgentMessage(agentIdArg, prompt, { images })
|
||||
await client.sendAgentMessage(agentIdArg, promptInput, { images })
|
||||
|
||||
// If --no-wait, return immediately
|
||||
if (options.noWait) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import assert from 'node:assert'
|
||||
import { $ } from 'zx'
|
||||
import { mkdtemp, rm } from 'fs/promises'
|
||||
import { mkdtemp, rm, writeFile } from 'fs/promises'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
|
||||
@@ -30,6 +30,8 @@ console.log('=== Send Command Tests ===\n')
|
||||
// Get random port that's definitely not in use (never 6767)
|
||||
const port = 10000 + Math.floor(Math.random() * 50000)
|
||||
const paseoHome = await mkdtemp(join(tmpdir(), 'paseo-test-home-'))
|
||||
const promptFilePath = join(paseoHome, 'send-prompt.txt')
|
||||
await writeFile(promptFilePath, 'prompt from file')
|
||||
|
||||
try {
|
||||
// Test 1: send --help shows options
|
||||
@@ -37,14 +39,18 @@ try {
|
||||
console.log('Test 1: send --help shows options')
|
||||
const result = await $`npx paseo send --help`.nothrow()
|
||||
assert.strictEqual(result.exitCode, 0, 'send --help should exit 0')
|
||||
assert(result.stdout.includes('--prompt'), 'help should mention --prompt option')
|
||||
assert(result.stdout.includes('--prompt-file'), 'help should mention --prompt-file option')
|
||||
assert(result.stdout.includes('--no-wait'), 'help should mention --no-wait flag')
|
||||
assert(result.stdout.includes('--host'), 'help should mention --host option')
|
||||
assert(result.stdout.includes('<id>'), 'help should mention id argument')
|
||||
assert(result.stdout.includes('<prompt>'), 'help should mention prompt argument')
|
||||
assert(result.stdout.includes('[prompt]'), 'help should mention optional prompt argument')
|
||||
console.log(' help should mention --prompt option')
|
||||
console.log(' help should mention --prompt-file option')
|
||||
console.log(' help should mention --no-wait flag')
|
||||
console.log(' help should mention --host option')
|
||||
console.log(' help should mention <id> argument')
|
||||
console.log(' help should mention <prompt> argument')
|
||||
console.log(' help should mention [prompt] argument')
|
||||
console.log('✓ send --help shows options\n')
|
||||
}
|
||||
|
||||
@@ -107,6 +113,28 @@ try {
|
||||
console.log('✓ send --no-wait flag is accepted\n')
|
||||
}
|
||||
|
||||
// Test 5b: send --prompt flag is accepted
|
||||
{
|
||||
console.log('Test 5b: send --prompt flag is accepted')
|
||||
const result =
|
||||
await $`PASEO_HOST=localhost:${port} PASEO_HOME=${paseoHome} npx paseo send --prompt "test prompt" abc123`.nothrow()
|
||||
const output = result.stdout + result.stderr
|
||||
assert(!output.includes('unknown option'), 'should accept --prompt flag')
|
||||
assert(!output.includes('error: option'), 'should not have option parsing error')
|
||||
console.log('✓ send --prompt flag is accepted\n')
|
||||
}
|
||||
|
||||
// Test 5c: send --prompt-file flag is accepted
|
||||
{
|
||||
console.log('Test 5c: send --prompt-file flag is accepted')
|
||||
const result =
|
||||
await $`PASEO_HOST=localhost:${port} PASEO_HOME=${paseoHome} npx paseo send --prompt-file ${promptFilePath} abc123`.nothrow()
|
||||
const output = result.stdout + result.stderr
|
||||
assert(!output.includes('unknown option'), 'should accept --prompt-file flag')
|
||||
assert(!output.includes('error: option'), 'should not have option parsing error')
|
||||
console.log('✓ send --prompt-file flag is accepted\n')
|
||||
}
|
||||
|
||||
// Test 6: send --host flag is accepted
|
||||
{
|
||||
console.log('Test 6: send --host flag is accepted')
|
||||
@@ -140,6 +168,20 @@ try {
|
||||
console.log('✓ Combined flags work together\n')
|
||||
}
|
||||
|
||||
// Test 8b: conflicting prompt sources are rejected
|
||||
{
|
||||
console.log('Test 8b: conflicting prompt sources are rejected')
|
||||
const result =
|
||||
await $`PASEO_HOST=localhost:${port} PASEO_HOME=${paseoHome} npx paseo send abc123 "positional prompt" --prompt "flag prompt"`.nothrow()
|
||||
assert.notStrictEqual(result.exitCode, 0, 'should fail for conflicting prompt sources')
|
||||
const output = result.stdout + result.stderr
|
||||
assert(
|
||||
output.includes('Provide exactly one of prompt argument, --prompt, or --prompt-file'),
|
||||
'should explain conflicting prompt sources'
|
||||
)
|
||||
console.log('✓ conflicting prompt sources are rejected\n')
|
||||
}
|
||||
|
||||
// Test 9: paseo --help shows send command
|
||||
{
|
||||
console.log('Test 9: paseo --help shows send command')
|
||||
|
||||
22
scripts/fix-tests/overseer-loop.sh
Executable file
22
scripts/fix-tests/overseer-loop.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
AGENT_ID="${1:-a5e75793-2e97-40dd-a38e-0150022b7e54}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROMPT_FILE="$SCRIPT_DIR/overseer.md"
|
||||
INTERVAL="${2:-1800}" # 30 minutes
|
||||
|
||||
echo "=== Overseer loop ==="
|
||||
echo " Agent: $AGENT_ID"
|
||||
echo " Prompt: $PROMPT_FILE"
|
||||
echo " Interval: ${INTERVAL}s"
|
||||
echo ""
|
||||
|
||||
iteration=0
|
||||
while true; do
|
||||
iteration=$((iteration + 1))
|
||||
echo "--- Overseer check #$iteration ($(date)) ---"
|
||||
paseo send "$AGENT_ID" --prompt-file "$PROMPT_FILE" || echo "Send failed, will retry next cycle"
|
||||
echo ""
|
||||
sleep "$INTERVAL"
|
||||
done
|
||||
131
scripts/fix-tests/overseer.md
Normal file
131
scripts/fix-tests/overseer.md
Normal file
@@ -0,0 +1,131 @@
|
||||
You are the overnight loop overseer. Two fix-tests loops are running in parallel, each in its own worktree. Your job is to check on them every 30 minutes, ensure progress, and be the final quality gate.
|
||||
|
||||
## Before your first check
|
||||
|
||||
Read these docs so you know what good looks like:
|
||||
- `docs/CODING_STANDARDS.md`
|
||||
- `docs/TESTING.md`
|
||||
|
||||
## The loops
|
||||
|
||||
| Loop | Worktree | Worker | Verifier | What it fixes |
|
||||
|---|---|---|---|---|
|
||||
| fix-app | fix-tests-app | Codex | Claude Sonnet | `packages/app` (vitest + Playwright e2e) |
|
||||
| fix-server | fix-tests-server | Codex | Claude Sonnet | `packages/server` + `packages/cli` + `packages/relay` |
|
||||
|
||||
Loop state lives in `~/.paseo/loops/`. Each loop has a `history.log` and `last_reason.md`.
|
||||
|
||||
## What to check
|
||||
|
||||
### 1. Are the loops still running?
|
||||
|
||||
```bash
|
||||
paseo ls -a
|
||||
```
|
||||
|
||||
Look for agents named `fix-app-*` and `fix-server-*`. If no agents are running for a realm, the loop may have exited (done or crashed). Check the history log.
|
||||
|
||||
### 2. Is progress being made?
|
||||
|
||||
Read the history logs:
|
||||
|
||||
```bash
|
||||
cat ~/.paseo/loops/*/history.log
|
||||
```
|
||||
|
||||
Look for:
|
||||
- Are iterations completing? If the last entry is old, something may be stuck.
|
||||
- Is `done=false` repeating with the same reason? That means the loop is stuck on the same problem.
|
||||
- Is the reason changing each iteration? That means forward progress.
|
||||
|
||||
### 3. What did the last agent do?
|
||||
|
||||
Use `paseo logs <agent-id>` to read the transcript of the most recent worker. Check:
|
||||
- Did it actually fix a test, or did it waste the iteration on something trivial?
|
||||
- Did it follow the rules (no mocks, no shoehorning, fail-fast)?
|
||||
- Is it tackling meaningful failures or avoiding the hard ones?
|
||||
|
||||
### 4. Code quality spot-check
|
||||
|
||||
Go into each worktree and review recent changes:
|
||||
|
||||
```bash
|
||||
# App realm
|
||||
cd .paseo/worktrees/fix-tests-app && git log --oneline -5 && git diff HEAD~1
|
||||
|
||||
# Server realm
|
||||
cd .paseo/worktrees/fix-tests-server && git log --oneline -5 && git diff HEAD~1
|
||||
```
|
||||
|
||||
Check for:
|
||||
- **Over-engineering** — unnecessary abstractions, premature generalization, options bags for single use
|
||||
- **Ugly hacks** — `vi.mock()`, `// @ts-ignore`, try/catch swallowing errors, conditional assertions
|
||||
- **Duplication** — same setup code copy-pasted instead of extracted into helpers
|
||||
- **Test quality** — do tests read like plain English? Are helpers being built?
|
||||
- **Good commit messages** — commits should describe what was fixed and why
|
||||
|
||||
### 5. Are tests actually getting greener?
|
||||
|
||||
Run the suites yourself in each worktree to get a ground-truth count:
|
||||
|
||||
```bash
|
||||
# In the app worktree
|
||||
cd .paseo/worktrees/fix-tests-app
|
||||
npm run test -w packages/app 2>&1 | tail -5
|
||||
npm run test:e2e -w packages/app 2>&1 | tail -5
|
||||
|
||||
# In the server worktree
|
||||
cd .paseo/worktrees/fix-tests-server
|
||||
npm run test:unit -w packages/server 2>&1 | tail -5
|
||||
npm run test:e2e -w packages/server 2>&1 | tail -5
|
||||
npm run test -w packages/cli 2>&1 | tail -5
|
||||
npm run test -w packages/relay 2>&1 | tail -5
|
||||
```
|
||||
|
||||
Track the failure count over time. If it's not going down, something is wrong.
|
||||
|
||||
## When to intervene
|
||||
|
||||
### Loop is stuck on the same test
|
||||
|
||||
If history shows 3+ iterations failing on the same thing, steer the worker prompt. Edit the prompt file in `~/.paseo/loops/<loop-id>/worker-prompt.md` to give the worker more specific guidance about the stuck test. The loop picks up prompt changes on the next iteration.
|
||||
|
||||
### Agent is avoiding hard tests
|
||||
|
||||
If the agent keeps fixing trivial things while a hard failure persists, edit the worker prompt to explicitly name the hard test and say "fix this one next."
|
||||
|
||||
### Quality is degrading
|
||||
|
||||
If you see mocks creeping in, over-engineering, or ugly hacks that the verifier missed, steer the prompt to emphasize the specific violation. Or if the problem is bad enough, go into the worktree and revert the bad commit yourself.
|
||||
|
||||
### Loop exited prematurely
|
||||
|
||||
If a loop exited but tests aren't actually all green, restart it. Read `scripts/fix-tests/run.sh` to see exactly how the loops were launched and re-run the appropriate command from there.
|
||||
|
||||
### Machine getting slow
|
||||
|
||||
If the machine feels sluggish, check for orphaned processes:
|
||||
|
||||
```bash
|
||||
ps aux | grep -E "(vitest|playwright|node.*daemon)" | grep -v grep
|
||||
```
|
||||
|
||||
Kill orphaned test processes by PID. **NEVER kill the daemon on port 6767.**
|
||||
|
||||
## What to report
|
||||
|
||||
After each check, summarize:
|
||||
1. **App realm**: iteration N, status (progressing/stuck/done), failure count trend, any quality issues
|
||||
2. **Server realm**: iteration N, status (progressing/stuck/done), failure count trend, any quality issues
|
||||
3. **Actions taken**: any prompt steers, restarts, or reverts you did
|
||||
4. **Overall**: are we on track to be green by morning?
|
||||
|
||||
## The big picture
|
||||
|
||||
The goal is not just green tests. The goal is:
|
||||
- Tests that are stupidly easy to write — Playwright specs should read like a DSL, daemon e2e tests should have rich typed helpers
|
||||
- Fast tests — real dependencies, no mocks, but using fast models and minimal test count. Less tests, higher quality, more coverage per test.
|
||||
- Clean code — follow `docs/CODING_STANDARDS.md` and `docs/TESTING.md` religiously
|
||||
- Good commits — each commit should describe a meaningful fix with context
|
||||
|
||||
You are the last line of defense. The verifier catches most issues, but you see the big picture across both realms and across time. Use that perspective.
|
||||
43
scripts/fix-tests/run.sh
Executable file
43
scripts/fix-tests/run.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
LOOP="$HOME/.claude/skills/paseo-loop/bin/loop.sh"
|
||||
|
||||
echo "=== Launching fix-tests loops ==="
|
||||
echo " App realm: worktree fix-tests-app"
|
||||
echo " Server realm: worktree fix-tests-server"
|
||||
echo ""
|
||||
|
||||
# Launch both loops in parallel
|
||||
"$LOOP" \
|
||||
--worker-prompt-file "$SCRIPT_DIR/worker-app.md" \
|
||||
--verifier-prompt-file "$SCRIPT_DIR/verifier.md" \
|
||||
--worker codex/gpt-5.4 \
|
||||
--verifier claude/sonnet \
|
||||
--name "fix-app" \
|
||||
--worktree "fix-tests-app" \
|
||||
--thinking medium \
|
||||
--archive &
|
||||
app_pid=$!
|
||||
|
||||
"$LOOP" \
|
||||
--worker-prompt-file "$SCRIPT_DIR/worker-server.md" \
|
||||
--verifier-prompt-file "$SCRIPT_DIR/verifier.md" \
|
||||
--worker codex/gpt-5.4 \
|
||||
--verifier claude/sonnet \
|
||||
--name "fix-server" \
|
||||
--worktree "fix-tests-server" \
|
||||
--thinking medium \
|
||||
--archive &
|
||||
server_pid=$!
|
||||
|
||||
echo "App loop PID: $app_pid"
|
||||
echo "Server loop PID: $server_pid"
|
||||
echo ""
|
||||
echo "Logs: ~/.paseo/loops/"
|
||||
echo "Kill: kill $app_pid $server_pid"
|
||||
echo ""
|
||||
|
||||
wait $app_pid && echo "App realm: DONE" || echo "App realm: EXITED ($?)"
|
||||
wait $server_pid && echo "Server realm: DONE" || echo "Server realm: EXITED ($?)"
|
||||
76
scripts/fix-tests/verifier.md
Normal file
76
scripts/fix-tests/verifier.md
Normal file
@@ -0,0 +1,76 @@
|
||||
You are the quality gate. Your job is to verify that the worker's changes are correct, clean, and follow project standards. You are NOT here to fix anything — only to evaluate.
|
||||
|
||||
## What to check
|
||||
|
||||
### 1. Do the tests pass?
|
||||
|
||||
Run the test suite for the realm you're verifying. If any test fails, report `done: false` immediately.
|
||||
|
||||
**App realm:**
|
||||
```bash
|
||||
npm run test -w packages/app -- --bail 1
|
||||
npm run test:e2e -w packages/app -- --max-failures 1
|
||||
```
|
||||
|
||||
**Server realm:**
|
||||
```bash
|
||||
npm run test:unit -w packages/server -- --bail 1
|
||||
npm run test:e2e -w packages/server
|
||||
npm run test:integration -w packages/server
|
||||
npm run test -w packages/cli
|
||||
npm run test -w packages/relay -- --bail 1
|
||||
```
|
||||
|
||||
### 2. Does typecheck pass?
|
||||
|
||||
```bash
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
### 3. Code quality of changed files
|
||||
|
||||
Read `docs/CODING_STANDARDS.md` and `docs/TESTING.md`, then review every file the worker changed (`git diff HEAD~1`).
|
||||
|
||||
Check for these violations — any one is grounds for `done: false`:
|
||||
|
||||
**Over-engineering:**
|
||||
- Unnecessary abstractions or helper functions for one-time operations
|
||||
- Premature generalization (config objects, feature flags, options bags for a single use case)
|
||||
- Unnecessary type gymnastics when a simple type would do
|
||||
- Added complexity that doesn't serve the test's purpose
|
||||
|
||||
**Mocks and shoehorning:**
|
||||
- Any use of `vi.mock()`, `jest.mock()`, or mocking libraries
|
||||
- Weird vitest/playwright config overrides to make tests pass
|
||||
- `try/catch` blocks swallowing errors in tests
|
||||
- Conditional assertions or `if` branches in test bodies
|
||||
- `// @ts-ignore` or `// @ts-expect-error` added to silence type errors
|
||||
- Weakened assertions (e.g., `toBeTruthy` where `toEqual` was before)
|
||||
|
||||
**Duplication:**
|
||||
- Same setup code copy-pasted across test files
|
||||
- Same assertion pattern repeated without extraction into a helper
|
||||
- Test helpers that duplicate existing helpers in the same directory
|
||||
|
||||
**Test quality:**
|
||||
- Tests that don't read like plain English
|
||||
- Test descriptions that don't match what the test actually verifies
|
||||
- Overly complex test bodies that could be simplified
|
||||
- Tests that test implementation details instead of behavior
|
||||
|
||||
**Cleanup and resource hygiene:**
|
||||
- Missing `afterAll`/`afterEach` cleanup for spawned processes
|
||||
- Processes killed by broad patterns instead of PID
|
||||
- Any code that could kill the daemon on port 6767
|
||||
- Leaked file handles, open connections, or temp files
|
||||
|
||||
### 4. Boy Scout Rule
|
||||
|
||||
Did the worker leave the files cleaner than they found them? If the worker touched a file with existing duplication or mess and didn't clean it up, that's a miss — but only flag it if the mess is in the area they were already working in. Don't flag unrelated files.
|
||||
|
||||
## How to report
|
||||
|
||||
- `done: true` — all tests pass, typecheck passes, no quality violations in changed files
|
||||
- `done: false` — explain specifically what failed or what violations you found, with file paths and line numbers. Be factual. Cite evidence. The worker will receive your reason as context for the next iteration.
|
||||
|
||||
Do not suggest fixes. Report facts.
|
||||
90
scripts/fix-tests/worker-app.md
Normal file
90
scripts/fix-tests/worker-app.md
Normal file
@@ -0,0 +1,90 @@
|
||||
Your sole purpose is to make the tests pass in `packages/app`. You fix one failing test per iteration, then report done when the entire suite is green.
|
||||
|
||||
## Before you start
|
||||
|
||||
Read these docs — they are the law:
|
||||
- `docs/CODING_STANDARDS.md`
|
||||
- `docs/TESTING.md`
|
||||
|
||||
## Your packages
|
||||
|
||||
- `packages/app` — Expo mobile + web client
|
||||
|
||||
## Test commands
|
||||
|
||||
Always use fail-fast. Do not run the whole suite. Find the first failure fast.
|
||||
|
||||
```bash
|
||||
# Unit tests (vitest)
|
||||
npm run test -w packages/app -- --bail 1
|
||||
|
||||
# E2E tests (Playwright)
|
||||
npm run test:e2e -w packages/app -- --max-failures 1
|
||||
```
|
||||
|
||||
Skip `*.real.e2e.test.ts` and `*.local.e2e.test.ts` — those are local-only manual tests.
|
||||
|
||||
## What to do each iteration
|
||||
|
||||
1. Run the unit tests with `--bail 1`. If they pass, run the Playwright e2e tests with `--max-failures 1`.
|
||||
2. Read the failure output carefully. Understand what the test is actually trying to verify.
|
||||
3. Fix it. See the rules below for how.
|
||||
4. Run typecheck: `npm run typecheck`
|
||||
5. Run the failing test again to confirm it passes.
|
||||
6. If all tests pass (both unit and e2e), report `done: true`. Otherwise report `done: false` with what failed and what you did.
|
||||
|
||||
## Rules — read every one
|
||||
|
||||
### Fix strategy
|
||||
|
||||
When a test fails:
|
||||
- **Outdated** (tests removed/renamed APIs, stale selectors) — update the test to match reality. If the test no longer tests anything meaningful, delete it.
|
||||
- **Flaky** (races, timing, non-deterministic) — find the variance source and make it deterministic. Never add retries or `waitForTimeout` as a fix.
|
||||
- **Too slow** — make it fast or delete it.
|
||||
- **Tests unimplemented behavior** — delete it. You are here to fix tests, not build features.
|
||||
|
||||
### No shoehorning
|
||||
|
||||
Do not shoehorn tests into passing. If code isn't testable, refactor the code to be testable. Signs you're shoehorning:
|
||||
- Adding `vi.mock()` to stub out a dependency
|
||||
- Adding weird vitest config overrides
|
||||
- Wrapping the test in try/catch to swallow errors
|
||||
- Adding conditional assertions or `if` branches in test bodies
|
||||
|
||||
Instead: make the dependency injectable, split the function, extract the pure logic.
|
||||
|
||||
### No mocks
|
||||
|
||||
We use real dependencies on purpose. Do not introduce `vi.mock()`, `jest.mock()`, or any mocking library. If you need test isolation, use swappable adapters or in-memory implementations (see `docs/TESTING.md`).
|
||||
|
||||
### Boy Scout Rule
|
||||
|
||||
Leave every file you touch cleaner than you found it:
|
||||
- Extract duplicated setup into shared helpers
|
||||
- Simplify complex assertions into readable helpers
|
||||
- If you see three tests doing the same setup, extract it
|
||||
- Build a vocabulary of test helpers so specs read like plain English
|
||||
|
||||
### Playwright e2e specifics
|
||||
|
||||
The Playwright tests are outdated. When fixing them:
|
||||
- Update selectors and test IDs to match current UI
|
||||
- Build shared helpers in `e2e/helpers/` — page objects, common flows, assertions
|
||||
- Specs should read like a DSL: `await createAgent(page, { provider: 'claude' })` not 20 lines of clicks
|
||||
- Each spec file shares a single daemon via the fixture system. Do not spawn extra daemons per test.
|
||||
- Clean up after yourself — if you start a process, kill it by PID when done
|
||||
|
||||
### Resource hygiene
|
||||
|
||||
- **NEVER kill the daemon running on port 6767** — that is the live development daemon. Killing it will break your own environment.
|
||||
- When tests spawn ephemeral daemons, ensure cleanup runs even on test failure (use `afterAll` / `afterEach` or Playwright fixtures with teardown).
|
||||
- Kill processes by PID, never by broad port or name patterns.
|
||||
|
||||
### What NOT to do
|
||||
|
||||
- Do not add auth checks, environment variable gates, or conditional skips
|
||||
- Do not introduce mocks
|
||||
- Do not add new vitest plugins or config changes
|
||||
- Do not implement new features to make a test pass
|
||||
- Do not add `// @ts-ignore` or `// @ts-expect-error` to silence type errors
|
||||
- Do not weaken assertions (e.g., changing `toEqual` to `toBeTruthy`)
|
||||
102
scripts/fix-tests/worker-server.md
Normal file
102
scripts/fix-tests/worker-server.md
Normal file
@@ -0,0 +1,102 @@
|
||||
Your sole purpose is to make the tests pass in `packages/server`, `packages/cli`, and `packages/relay`. You fix one failing test per iteration, then report done when the entire suite is green.
|
||||
|
||||
## Before you start
|
||||
|
||||
Read these docs — they are the law:
|
||||
- `docs/CODING_STANDARDS.md`
|
||||
- `docs/TESTING.md`
|
||||
|
||||
## Your packages
|
||||
|
||||
- `packages/server` — Daemon, agent lifecycle, WebSocket API
|
||||
- `packages/cli` — Docker-style CLI (`paseo run/ls/logs/wait`)
|
||||
- `packages/relay` — E2E encrypted relay
|
||||
|
||||
## Test commands
|
||||
|
||||
Always use fail-fast. Do not run the whole suite. Find the first failure fast.
|
||||
|
||||
```bash
|
||||
# Server unit tests
|
||||
npm run test:unit -w packages/server -- --bail 1
|
||||
|
||||
# Server e2e tests (daemon tests — the most valuable tests in the project)
|
||||
npm run test:e2e -w packages/server
|
||||
|
||||
# Server integration tests
|
||||
npm run test:integration -w packages/server
|
||||
|
||||
# CLI tests
|
||||
npm run test -w packages/cli
|
||||
|
||||
# Relay tests
|
||||
npm run test -w packages/relay -- --bail 1
|
||||
```
|
||||
|
||||
Skip `*.real.e2e.test.ts` and `*.local.e2e.test.ts` — those are local-only manual tests.
|
||||
|
||||
## What to do each iteration
|
||||
|
||||
1. Run unit tests first (`--bail 1`). If they pass, run e2e tests. If those pass, run integration, then CLI, then relay.
|
||||
2. Read the failure output carefully. Understand what the test is actually trying to verify.
|
||||
3. Fix it. See the rules below for how.
|
||||
4. Run typecheck: `npm run typecheck`
|
||||
5. Run the failing test again to confirm it passes.
|
||||
6. If all tests pass across all three packages, report `done: true`. Otherwise report `done: false` with what failed and what you did.
|
||||
|
||||
## Rules — read every one
|
||||
|
||||
### Fix strategy
|
||||
|
||||
When a test fails:
|
||||
- **Outdated** (tests removed/renamed APIs) — update the test to match reality. If the test no longer tests anything meaningful, delete it.
|
||||
- **Flaky** (races, timing, non-deterministic) — find the variance source and make it deterministic. Never add retries or sleeps as a fix.
|
||||
- **Too slow** — make it fast or delete it.
|
||||
- **Tests unimplemented behavior** — delete it. You are here to fix tests, not build features.
|
||||
|
||||
### Test value hierarchy
|
||||
|
||||
The daemon e2e tests (`packages/server/src/server/daemon-e2e/`) are the most valuable tests in this project. They test closest to the user — a real daemon, real WebSocket connections, real agent providers.
|
||||
|
||||
- If a behavior is already covered by a daemon e2e test, a unit test for the same behavior is redundant. Delete the unit test.
|
||||
- Provider-specific unit tests (e.g., `claude-agent.*.test.ts`) are for testing specific provider bugs: interruptions, autonomous wakes, edge cases in tool call parsing. Not for testing general agent lifecycle — that's what e2e tests are for.
|
||||
- If you're unsure whether a test adds value, check if the same behavior is exercised by an e2e test. If yes, delete.
|
||||
|
||||
### No shoehorning
|
||||
|
||||
Do not shoehorn tests into passing. If code isn't testable, refactor the code to be testable. Signs you're shoehorning:
|
||||
- Adding `vi.mock()` to stub out a dependency
|
||||
- Adding weird vitest config overrides
|
||||
- Wrapping the test in try/catch to swallow errors
|
||||
- Adding conditional assertions or `if` branches in test bodies
|
||||
|
||||
Instead: make the dependency injectable, split the function, extract the pure logic.
|
||||
|
||||
### No mocks
|
||||
|
||||
We use real dependencies on purpose. Do not introduce `vi.mock()`, `jest.mock()`, or any mocking library. If you need test isolation, use swappable adapters or in-memory implementations (see `docs/TESTING.md`).
|
||||
|
||||
### Boy Scout Rule
|
||||
|
||||
Leave every file you touch cleaner than you found it:
|
||||
- Extract duplicated setup into shared helpers
|
||||
- Simplify complex assertions into readable helpers
|
||||
- If you see three tests doing the same setup, extract it
|
||||
- Build a vocabulary of test helpers so specs read like plain English
|
||||
- CLI tests have shared helpers in `packages/cli/tests/helpers/` — use and extend them
|
||||
|
||||
### Resource hygiene
|
||||
|
||||
- **NEVER kill the daemon running on port 6767** — that is the live development daemon. Killing it will break your own environment.
|
||||
- Daemon e2e tests spawn their own ephemeral daemons on random ports. Ensure cleanup runs even on test failure.
|
||||
- Kill processes by PID, never by broad port or name patterns.
|
||||
- If a test leaves an orphaned process, find the cleanup bug and fix it properly.
|
||||
|
||||
### What NOT to do
|
||||
|
||||
- Do not add auth checks, environment variable gates, or conditional skips
|
||||
- Do not introduce mocks
|
||||
- Do not add new vitest plugins or config changes
|
||||
- Do not implement new features to make a test pass
|
||||
- Do not add `// @ts-ignore` or `// @ts-expect-error` to silence type errors
|
||||
- Do not weaken assertions (e.g., changing `toEqual` to `toBeTruthy`)
|
||||
Reference in New Issue
Block a user