mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
refactor: update app specification generation and XML handling
- Enhanced instructions for generating app specifications to clarify XML output requirements. - Updated permission mode in spec generation options to ensure read-only access. - Improved logging to capture XML content extraction and handle potential issues with incomplete responses. - Ensured that only valid XML is saved, avoiding conversational text from the response.
This commit is contained in:
@@ -63,10 +63,11 @@ export function getAppSpecFormatInstruction(): string {
|
||||
${APP_SPEC_XML_FORMAT}
|
||||
|
||||
CRITICAL FORMATTING REQUIREMENTS:
|
||||
- Do NOT use the Write, Edit, or Bash tools to create files - just OUTPUT the XML in your response
|
||||
- Your ENTIRE response MUST be valid XML following the exact template structure above
|
||||
- Do NOT use markdown formatting (no # headers, no **bold**, no - lists, etc.)
|
||||
- Do NOT include any explanatory text, prefix, or suffix outside the XML tags
|
||||
- Do NOT include phrases like "Based on my analysis..." or "I'll create..." before the XML
|
||||
- Do NOT include phrases like "Based on my analysis...", "I'll create...", "Let me analyze..." before the XML
|
||||
- Do NOT include any text before <project_specification> or after </project_specification>
|
||||
- Your response must start IMMEDIATELY with <project_specification> with no preceding text
|
||||
- Your response must end IMMEDIATELY with </project_specification> with no following text
|
||||
|
||||
@@ -164,6 +164,10 @@ export function createSpecGenerationOptions(
|
||||
): Options {
|
||||
return {
|
||||
...getBaseOptions(),
|
||||
// Override permissionMode - spec generation only needs read-only tools
|
||||
// Using "acceptEdits" can cause Claude to write files to unexpected locations
|
||||
// See: https://github.com/AutoMaker-Org/automaker/issues/149
|
||||
permissionMode: "default",
|
||||
model: getModelForUseCase("spec", config.model),
|
||||
maxTurns: MAX_TURNS.maximum,
|
||||
cwd: config.cwd,
|
||||
@@ -186,6 +190,8 @@ export function createFeatureGenerationOptions(
|
||||
): Options {
|
||||
return {
|
||||
...getBaseOptions(),
|
||||
// Override permissionMode - feature generation only needs read-only tools
|
||||
permissionMode: "default",
|
||||
model: getModelForUseCase("features", config.model),
|
||||
maxTurns: MAX_TURNS.quick,
|
||||
cwd: config.cwd,
|
||||
|
||||
@@ -38,11 +38,13 @@ export async function generateSpec(
|
||||
|
||||
if (analyzeProject !== false) {
|
||||
// Default to true - analyze the project
|
||||
analysisInstructions = `Based on this overview, analyze the project directory (if it exists) and create a comprehensive specification. Use the Read, Glob, and Grep tools to explore the codebase and understand:
|
||||
analysisInstructions = `Based on this overview, analyze the project directory (if it exists) using the Read, Glob, and Grep tools to understand:
|
||||
- Existing technologies and frameworks
|
||||
- Project structure and architecture
|
||||
- Current features and capabilities
|
||||
- Code patterns and conventions`;
|
||||
- Code patterns and conventions
|
||||
|
||||
After your analysis, OUTPUT the complete XML specification in your response. Do NOT attempt to write any files - just return the XML content and it will be saved automatically.`;
|
||||
} else {
|
||||
// Use default tech stack
|
||||
techStackDefaults = `Default Technology Stack:
|
||||
@@ -52,7 +54,9 @@ export async function generateSpec(
|
||||
- Styling: Tailwind CSS
|
||||
- Frontend: React
|
||||
|
||||
Use these technologies as the foundation for the specification.`;
|
||||
Use these technologies as the foundation for the specification.
|
||||
|
||||
OUTPUT the complete XML specification in your response. Do NOT attempt to write any files - just return the XML content and it will be saved automatically.`;
|
||||
}
|
||||
|
||||
const prompt = `You are helping to define a software project specification.
|
||||
@@ -163,17 +167,14 @@ ${getAppSpecFormatInstruction()}`;
|
||||
}
|
||||
} else if (msg.type === "result" && (msg as any).subtype === "success") {
|
||||
logger.info("Received success result");
|
||||
logger.info(`Result value: "${(msg as any).result}"`);
|
||||
logger.info(`Result value length: ${((msg as any).result || "").length}`);
|
||||
logger.info(
|
||||
`Current responseText length before result: ${responseText.length}`
|
||||
`Final accumulated responseText length: ${responseText.length}`
|
||||
);
|
||||
// Only use result if it has content, otherwise keep accumulated text
|
||||
if ((msg as any).result && (msg as any).result.length > 0) {
|
||||
logger.info("Using result value as responseText");
|
||||
responseText = (msg as any).result;
|
||||
} else {
|
||||
logger.info("Result is empty, keeping accumulated responseText");
|
||||
}
|
||||
// Don't overwrite responseText - the accumulated text contains the XML spec
|
||||
// The result.result field contains Claude's conversational summary (e.g., "I've created the spec...")
|
||||
// which is NOT what we want to save to app_spec.txt
|
||||
// See: https://github.com/AutoMaker-Org/automaker/issues/149
|
||||
} else if (msg.type === "result") {
|
||||
// Handle all result types
|
||||
const subtype = (msg as any).subtype;
|
||||
@@ -210,14 +211,30 @@ ${getAppSpecFormatInstruction()}`;
|
||||
logger.error("❌ WARNING: responseText is empty! Nothing to save.");
|
||||
}
|
||||
|
||||
// Extract XML content from response - Claude might include conversational text before/after
|
||||
// See: https://github.com/AutoMaker-Org/automaker/issues/149
|
||||
let xmlContent = responseText;
|
||||
const xmlStart = responseText.indexOf("<project_specification>");
|
||||
const xmlEnd = responseText.lastIndexOf("</project_specification>");
|
||||
|
||||
if (xmlStart !== -1 && xmlEnd !== -1) {
|
||||
// Extract just the XML content
|
||||
xmlContent = responseText.substring(xmlStart, xmlEnd + "</project_specification>".length);
|
||||
logger.info(`Extracted XML content: ${xmlContent.length} chars (from position ${xmlStart})`);
|
||||
} else if (xmlStart === -1) {
|
||||
logger.warn("⚠️ Response does not contain <project_specification> tag - saving raw response");
|
||||
} else {
|
||||
logger.warn("⚠️ Response has incomplete XML (missing closing tag) - saving raw response");
|
||||
}
|
||||
|
||||
// Save spec to .automaker directory
|
||||
const specDir = await ensureAutomakerDir(projectPath);
|
||||
const specPath = getAppSpecPath(projectPath);
|
||||
|
||||
logger.info("Saving spec to:", specPath);
|
||||
logger.info(`Content to save (${responseText.length} chars)`);
|
||||
logger.info(`Content to save (${xmlContent.length} chars)`);
|
||||
|
||||
await fs.writeFile(specPath, responseText);
|
||||
await fs.writeFile(specPath, xmlContent);
|
||||
|
||||
// Verify the file was written
|
||||
const savedContent = await fs.readFile(specPath, "utf-8");
|
||||
|
||||
Reference in New Issue
Block a user