Files
plan2code/plan2code-bot/src/session-runner.test.ts
T
jparkerweb c2579a7b6a plan2code-bot: replace auto-responder with LLM-as-judge evaluation
Bot now acts as an authentic QA agent instead of rubber-stamping every
question and step.

- intelligent-responder answers AskUserQuestion via LLM using current
  observations (tools used, files created, errors) instead of keyword
  matching; auto-responder removed.
- evaluator runs after each step with step-specific criteria
  (prompts/evaluation-criteria.ts), produces 0-100 score plus
  strengths/weaknesses/critical issues. Avg <60 blocks finalization.
- observation-collector captures tool_use, file writes, errors, and
  question reasoning; session-runner falls back to scraping tool_use
  blocks when canUseTool doesn't fire and dedupes both sources.
- Bot writes BOT-EVALUATION.md and BOT-NOTES.md for metrics analysis.
- Idea generator: 12 categories instead of CLI/web-app coin flip,
  stronger seed adherence, less developer-tool bias.
- Init step now writes a minimal AGENTS.md stub instead of running
  the full init skill, avoiding hallucinated architecture before plan.
- Implement step uses Read/Write/Edit/Glob/Grep directly instead of
  a Skill sub-session that produced no visible tool observations.
- bin: add --help, accept --idea="value" form, strip surrounding
  quotes; evaluator maxTurns 3 -> 30; warn when parser misses SCORE.
2026-05-11 13:14:28 -07:00

83 lines
2.3 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
// Mock the SDK before importing session-runner
vi.mock('@anthropic-ai/claude-agent-sdk', () => ({
query: vi.fn(),
}));
import { query } from '@anthropic-ai/claude-agent-sdk';
import { runSession } from './session-runner.js';
import { ObservationCollector } from './observation-collector.js';
import type { BotConfig } from './types.js';
const config: BotConfig = {
workDir: '/tmp/work',
projectDir: '/tmp/work/my-app',
ideaDescription: 'test app',
ideaName: 'test-app',
mode: 'new-project',
};
describe('runSession', () => {
it('returns success: false when output is empty', async () => {
// Simulate a session that yields an assistant message with empty text
const mockQuery = vi.mocked(query);
mockQuery.mockReturnValue(
(async function* () {
yield {
type: 'assistant' as const,
session_id: 'sess-1',
message: { content: [{ type: 'text' as const, text: '' }] },
};
yield {
type: 'result' as const,
session_id: 'sess-1',
};
})() as any,
);
const collector = new ObservationCollector('init');
const result = await runSession({
prompt: 'do something',
config,
step: 'init',
collector,
});
expect(result.success).toBe(false);
expect(result.output.trim()).toBe('');
expect(result.observations).toBeDefined();
});
it('returns success: true when output has content', async () => {
const mockQuery = vi.mocked(query);
mockQuery.mockReturnValue(
(async function* () {
yield {
type: 'assistant' as const,
session_id: 'sess-2',
message: { content: [{ type: 'text' as const, text: 'AGENTS.md has been created successfully' }] },
};
yield {
type: 'result' as const,
session_id: 'sess-2',
};
})() as any,
);
const collector = new ObservationCollector('init');
const result = await runSession({
prompt: 'do something',
config,
step: 'init',
collector,
});
expect(result.success).toBe(true);
expect(result.output).toContain('AGENTS.md');
expect(result.sessionId).toBe('sess-2');
expect(result.observations).toBeDefined();
expect(result.observations.step).toBe('init');
});
});