mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
refactor(server): Use shared JSON extractor in feature and plan parsing
Update parseAndCreateFeatures and parsePlanResponse to use the shared extractJson/extractJsonWithArray utilities instead of manual regex parsing for more robust and consistent JSON extraction from AI responses. - parse-and-create-features.ts: Use extractJsonWithArray for features - generate-plan.ts: Use extractJson with requiredKey for backlog plans 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import * as secureFs from '../../lib/secure-fs.js';
|
||||
import type { EventEmitter } from '../../lib/events.js';
|
||||
import { createLogger } from '@automaker/utils';
|
||||
import { getFeaturesDir } from '@automaker/platform';
|
||||
import { extractJsonWithArray } from '../../lib/json-extractor.js';
|
||||
|
||||
const logger = createLogger('SpecRegeneration');
|
||||
|
||||
@@ -22,23 +23,30 @@ export async function parseAndCreateFeatures(
|
||||
logger.info('========== END CONTENT ==========');
|
||||
|
||||
try {
|
||||
// Extract JSON from response
|
||||
logger.info('Extracting JSON from response...');
|
||||
logger.info(`Looking for pattern: /{[\\s\\S]*"features"[\\s\\S]*}/`);
|
||||
const jsonMatch = content.match(/\{[\s\S]*"features"[\s\S]*\}/);
|
||||
if (!jsonMatch) {
|
||||
logger.error('❌ No valid JSON found in response');
|
||||
// Extract JSON from response using shared utility
|
||||
logger.info('Extracting JSON from response using extractJsonWithArray...');
|
||||
|
||||
interface FeaturesResponse {
|
||||
features: Array<{
|
||||
id: string;
|
||||
category?: string;
|
||||
title: string;
|
||||
description: string;
|
||||
priority?: number;
|
||||
complexity?: string;
|
||||
dependencies?: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
const parsed = extractJsonWithArray<FeaturesResponse>(content, 'features', { logger });
|
||||
|
||||
if (!parsed || !parsed.features) {
|
||||
logger.error('❌ No valid JSON with "features" array found in response');
|
||||
logger.error('Full content received:');
|
||||
logger.error(content);
|
||||
throw new Error('No valid JSON found in response');
|
||||
}
|
||||
|
||||
logger.info(`JSON match found (${jsonMatch[0].length} chars)`);
|
||||
logger.info('========== MATCHED JSON ==========');
|
||||
logger.info(jsonMatch[0]);
|
||||
logger.info('========== END MATCHED JSON ==========');
|
||||
|
||||
const parsed = JSON.parse(jsonMatch[0]);
|
||||
logger.info(`Parsed ${parsed.features?.length || 0} features`);
|
||||
logger.info('Parsed features:', JSON.stringify(parsed.features, null, 2));
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { Feature, BacklogPlanResult, BacklogChange, DependencyUpdate } from
|
||||
import { DEFAULT_PHASE_MODELS } from '@automaker/types';
|
||||
import { FeatureLoader } from '../../services/feature-loader.js';
|
||||
import { ProviderFactory } from '../../providers/provider-factory.js';
|
||||
import { extractJson } from '../../lib/json-extractor.js';
|
||||
import { logger, setRunningState, getErrorMessage } from './common.js';
|
||||
import type { SettingsService } from '../../services/settings-service.js';
|
||||
import { getAutoLoadClaudeMdSetting } from '../../lib/settings-helpers.js';
|
||||
@@ -43,24 +44,23 @@ function formatFeaturesForPrompt(features: Feature[]): string {
|
||||
* Parse the AI response into a BacklogPlanResult
|
||||
*/
|
||||
function parsePlanResponse(response: string): BacklogPlanResult {
|
||||
try {
|
||||
// Try to extract JSON from the response
|
||||
const jsonMatch = response.match(/```json\n?([\s\S]*?)\n?```/);
|
||||
if (jsonMatch) {
|
||||
return JSON.parse(jsonMatch[1]);
|
||||
}
|
||||
// Use shared JSON extraction utility for robust parsing
|
||||
const parsed = extractJson<BacklogPlanResult>(response, {
|
||||
logger,
|
||||
requiredKey: 'changes',
|
||||
});
|
||||
|
||||
// Try to parse the whole response as JSON
|
||||
return JSON.parse(response);
|
||||
} catch {
|
||||
// If parsing fails, return an empty result
|
||||
logger.warn('[BacklogPlan] Failed to parse AI response as JSON');
|
||||
return {
|
||||
changes: [],
|
||||
summary: 'Failed to parse AI response',
|
||||
dependencyUpdates: [],
|
||||
};
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// If parsing fails, return an empty result
|
||||
logger.warn('[BacklogPlan] Failed to parse AI response as JSON');
|
||||
return {
|
||||
changes: [],
|
||||
summary: 'Failed to parse AI response',
|
||||
dependencyUpdates: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user