mirror of
https://github.com/jparkerweb/plan2code.git
synced 2026-07-21 18:33:22 -07:00
c2579a7b6a
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.
83 lines
2.3 KiB
TypeScript
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');
|
|
});
|
|
});
|